{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/harness/session/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EACX,MAAM,EAGN,KAAK,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,OAAO,EACP,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,YAAY,EACZ,OAAO,EACP,iBAAiB,EAEjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAMN,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAGhB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,2BAA2B;IAC3C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,sFAAsF;AACtF,qBAAa,qBAAsB,SAAQ,KAAK;IAC/C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AAED,0CAA0C;AAC1C,qBAAa,yBAA0B,SAAQ,KAAK;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAKzC;CACD;AAED,yCAAyC;AACzC,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY,MAAM,EAAE,MAAM,EAIzB;CACD;AAED,0EAA0E;AAC1E,qBAAa,mCAAoC,SAAQ,KAAK;IAC7D,cAGC;CACD;AAED,uDAAuD;AACvD,qBAAa,yBAA0B,SAAQ,KAAK;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YAAY,QAAQ,EAAE,MAAM,EAI3B;CACD;AAkID,+EAA+E;AAC/E,qBAAa,oBAAoB,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,CAAE,YAAW,OAAO,CAAC,SAAS,CAAC;IACnH,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;IACnE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,KAAK,CAAyC;IACtD,OAAO,CAAC,YAAY,CAA4B;IAEhD,YAAY,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,2BAAgC,EAM3F;IAEK,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAkB/D;IAEK,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAOlF;IAEK,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAG7E;IAEK,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAEvE;IAEK,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAG1F;IAEK,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAGjF;IAEK,QAAQ,CAAC,CAAC,EACf,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EACrB,OAAO,EAAE,eAAe,GAAG,SAAS,EACpC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAG3B;IAEK,UAAU,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAG7E;IAEK,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CAGtD;IAEK,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE3D;IAEK,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE9E;IAEK,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAsBnF;IAEK,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAK3F;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAIxE;IAEK,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAarF;IAED,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhF;IAED,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjE;IAED,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzF;IAED,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpE;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE;IAED,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;IAED,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrC;IAEK,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIzE;IAEK,cAAc,CACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,YAAY,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,EAC5G,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,CA4BjB;IAED,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,UAAU;CAGlB","sourcesContent":["import { uuidv7 } from \"@earendil-works/pi-ai/utils/uuid\";\nimport type { AgentMessage } from \"../../types.ts\";\nimport type { Context } from \"../context.ts\";\nimport { insertEntry } from \"./commit.ts\";\nimport { MutationLine } from \"./mutation-line.ts\";\nimport type {\n\tBranch,\n\tBranchScan,\n\tCommitResult,\n\tEntry,\n\tEntryQuery,\n\tIdGenerator,\n\tJsonValue,\n\tSession,\n\tSessionMetadata,\n\tSessionMutation,\n\tSessionMutationCallback,\n\tSessionStats,\n\tStorage,\n\tStorageBranchScan,\n\tWrite,\n} from \"./types.ts\";\nimport {\n\tappendList as appendListWrite,\n\tbranchTip,\n\tdeleteList as deleteListWrite,\n\tdeleteValue as deleteValueWrite,\n\tentryLabel,\n\ttype ListElement,\n\ttype ListReadOptions,\n\ttype StoredValue,\n\tsessionName,\n\tsetValue as setValueWrite,\n\ttype Value,\n\ttype ValueList,\n} from \"./values.ts\";\n\nexport interface StorageBackedSessionOptions {\n\tmutationLine?: MutationLine;\n\tidGenerator?: IdGenerator;\n\tonClose?: () => void;\n}\n\n/** Durable session state is internally inconsistent and cannot be safely advanced. */\nexport class SessionInvariantError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"SessionInvariantError\";\n\t}\n}\n\n/** A requested Branch name is invalid. */\nexport class SessionInvalidBranchError extends Error {\n\treadonly branch: string;\n\treadonly reason: string;\n\n\tconstructor(branch: string, reason: string) {\n\t\tsuper(`Invalid branch ${JSON.stringify(branch)}: ${reason}`);\n\t\tthis.name = \"SessionInvalidBranchError\";\n\t\tthis.branch = branch;\n\t\tthis.reason = reason;\n\t}\n}\n\n/** A requested branch already exists. */\nexport class SessionBranchExistsError extends Error {\n\treadonly branch: string;\n\n\tconstructor(branch: string) {\n\t\tsuper(`Branch already exists: ${branch}`);\n\t\tthis.name = \"SessionBranchExistsError\";\n\t\tthis.branch = branch;\n\t}\n}\n\n/** A pending assistant message cannot be persisted as a session entry. */\nexport class SessionPendingAssistantMessageError extends Error {\n\tconstructor() {\n\t\tsuper(\"Cannot persist a pending assistant message\");\n\t\tthis.name = \"SessionPendingAssistantMessageError\";\n\t}\n}\n\n/** A requested session entry target does not exist. */\nexport class SessionUnknownTargetError extends Error {\n\treadonly targetId: string;\n\n\tconstructor(targetId: string) {\n\t\tsuper(`Unknown target: ${targetId}`);\n\t\tthis.name = \"SessionUnknownTargetError\";\n\t\tthis.targetId = targetId;\n\t}\n}\n\nclass StorageBackedSessionMutation implements SessionMutation {\n\tprivate readonly storage: Storage;\n\tprivate readonly release: () => void;\n\tprivate active = true;\n\tprivate commitResult: Promise<CommitResult> | undefined;\n\tprivate endPromise: Promise<void> | undefined;\n\n\tconstructor(storage: Storage, release: () => void) {\n\t\tthis.storage = storage;\n\t\tthis.release = release;\n\t}\n\n\tcommit(writes: Write[], context: Context): Promise<CommitResult> {\n\t\tthis.assertActive();\n\t\tif (this.commitResult !== undefined) return Promise.reject(new Error(\"SessionMutator commit already attempted\"));\n\t\ttry {\n\t\t\tfor (const write of writes) {\n\t\t\t\tif (\n\t\t\t\t\twrite.kind === \"entry\" &&\n\t\t\t\t\twrite.entry.type === \"message\" &&\n\t\t\t\t\twrite.entry.message.role === \"assistant\" &&\n\t\t\t\t\twrite.entry.message.stopReason === \"pending\"\n\t\t\t\t) {\n\t\t\t\t\tthrow new SessionPendingAssistantMessageError();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.commitResult = this.storage.commit(writes, context);\n\t\t} catch (error) {\n\t\t\tthis.commitResult = Promise.reject(error);\n\t\t}\n\t\treturn this.commitResult;\n\t}\n\n\tend(_context: Context): Promise<void> {\n\t\tif (this.endPromise !== undefined) return this.endPromise;\n\t\tthis.active = false;\n\t\tthis.endPromise = this.settle().finally(this.release);\n\t\treturn this.endPromise;\n\t}\n\n\tgetEntries(ids: string[], context: Context): Promise<Map<string, Entry>> {\n\t\tthis.assertActive();\n\t\treturn this.storage.getEntries(ids, context);\n\t}\n\n\tgetStats(context: Context): Promise<SessionStats> {\n\t\tthis.assertActive();\n\t\treturn this.storage.getStats(context);\n\t}\n\n\tgetValue<T>(address: Value<T>, context: Context): Promise<StoredValue<T> | undefined> {\n\t\tthis.assertActive();\n\t\treturn this.storage.getValue(address, context);\n\t}\n\n\tscanValues<T>(prefix: Value<T>, context: Context): Promise<StoredValue<T>[]> {\n\t\tthis.assertActive();\n\t\treturn this.storage.scanValues(prefix, context);\n\t}\n\n\treadList<T>(\n\t\taddress: ValueList<T>,\n\t\toptions: ListReadOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<ListElement<T>[]> {\n\t\tthis.assertActive();\n\t\treturn this.storage.readList(address, options, context);\n\t}\n\n\tscanBranch(query: StorageBranchScan, context: Context): Promise<Entry[]> {\n\t\tthis.assertActive();\n\t\treturn this.storage.scanBranch(query, context);\n\t}\n\n\tprivate settle(): Promise<void> {\n\t\treturn (\n\t\t\tthis.commitResult?.then(\n\t\t\t\t() => undefined,\n\t\t\t\t() => undefined,\n\t\t\t) ?? Promise.resolve()\n\t\t);\n\t}\n\n\tprivate assertActive(): void {\n\t\tif (!this.active) throw new Error(\"SessionMutator cannot be used outside its mutation callback\");\n\t}\n}\n\nclass StorageBackedBranch implements Branch {\n\treadonly name: string;\n\tprivate readonly session: StorageBackedSession;\n\n\tconstructor(name: string, session: StorageBackedSession) {\n\t\tthis.name = name;\n\t\tthis.session = session;\n\t}\n\n\tgetTipId(context: Context): Promise<string | null> {\n\t\treturn this.session.getBranchTip(this.name, context);\n\t}\n\n\tasync findEntries(query: BranchScan | undefined, context: Context): Promise<Entry[]> {\n\t\tquery ??= {};\n\t\tconst start = query.start ?? (await this.getTipId(context));\n\t\tif (start === null) return [];\n\t\treturn this.session.scanBranch({ ...query, start, order: query.order ?? \"newestFirst\" }, context);\n\t}\n\n\tasync findEntry(query: BranchScan | undefined, context: Context): Promise<Entry | undefined> {\n\t\tquery ??= {};\n\t\treturn (\n\t\t\tawait this.findEntries({ ...query, limit: query.limit === undefined ? 1 : Math.min(query.limit, 1) }, context)\n\t\t)[0];\n\t}\n\n\tappendMessage(message: AgentMessage, context: Context): Promise<string> {\n\t\treturn this.session.appendToBranch(this.name, { type: \"message\", message }, context);\n\t}\n\n\tappendCustomEntry(customType: string, data: JsonValue | undefined, context: Context): Promise<string> {\n\t\treturn this.session.appendToBranch(\n\t\t\tthis.name,\n\t\t\t{ type: \"custom\", customType, ...(data === undefined ? {} : { data }) },\n\t\t\tcontext,\n\t\t);\n\t}\n}\n\n/** Package-internal typed boundary shared by concrete session repositories. */\nexport class StorageBackedSession<TMetadata extends SessionMetadata = SessionMetadata> implements Session<TMetadata> {\n\treadonly metadata: TMetadata;\n\treadonly idGenerator: IdGenerator;\n\tprivate readonly storage: Storage;\n\tprivate readonly mutationLine: MutationLine;\n\tprivate readonly onClose: (() => void) | undefined;\n\tprivate readonly branches = new Map<string, StorageBackedBranch>();\n\tprivate readonly closedError = new Error(\"Session is closed\");\n\tprivate state: \"open\" | \"closing\" | \"closed\" = \"open\";\n\tprivate closePromise: Promise<void> | undefined;\n\n\tconstructor(metadata: TMetadata, storage: Storage, options: StorageBackedSessionOptions = {}) {\n\t\tthis.metadata = metadata;\n\t\tthis.idGenerator = options.idGenerator ?? { next: uuidv7 };\n\t\tthis.storage = storage;\n\t\tthis.mutationLine = options.mutationLine ?? new MutationLine();\n\t\tthis.onClose = options.onClose;\n\t}\n\n\tasync beginMutation(_context: Context): Promise<SessionMutation> {\n\t\tthis.assertOpen();\n\t\tlet grant!: (mutation: SessionMutation) => void;\n\t\tlet rejectGrant!: (error: unknown) => void;\n\t\tconst granted = new Promise<SessionMutation>((resolve, reject) => {\n\t\t\tgrant = resolve;\n\t\t\trejectGrant = reject;\n\t\t});\n\t\tlet release!: () => void;\n\t\tconst finished = new Promise<void>((resolve) => {\n\t\t\trelease = resolve;\n\t\t});\n\t\tconst line = this.mutationLine.run(async () => {\n\t\t\tgrant(new StorageBackedSessionMutation(this.storage, release));\n\t\t\tawait finished;\n\t\t});\n\t\tvoid line.catch(rejectGrant);\n\t\treturn granted;\n\t}\n\n\tasync mutate<T>(mutation: SessionMutationCallback<T>, context: Context): Promise<T> {\n\t\tconst mutator = await this.beginMutation(context);\n\t\ttry {\n\t\t\treturn await mutation(mutator, context);\n\t\t} finally {\n\t\t\tawait mutator.end(context);\n\t\t}\n\t}\n\n\tasync getEntries(ids: string[], context: Context): Promise<Map<string, Entry>> {\n\t\tthis.assertOpen();\n\t\treturn this.storage.getEntries(ids, context);\n\t}\n\n\tasync getEntry(id: string, context: Context): Promise<Entry | undefined> {\n\t\treturn (await this.getEntries([id], context)).get(id);\n\t}\n\n\tasync getValue<T>(address: Value<T>, context: Context): Promise<StoredValue<T> | undefined> {\n\t\tthis.assertOpen();\n\t\treturn this.storage.getValue(address, context);\n\t}\n\n\tasync scanValues<T>(prefix: Value<T>, context: Context): Promise<StoredValue<T>[]> {\n\t\tthis.assertOpen();\n\t\treturn this.storage.scanValues(prefix, context);\n\t}\n\n\tasync readList<T>(\n\t\taddress: ValueList<T>,\n\t\toptions: ListReadOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<ListElement<T>[]> {\n\t\tthis.assertOpen();\n\t\treturn this.storage.readList(address, options, context);\n\t}\n\n\tasync scanBranch(query: StorageBranchScan, context: Context): Promise<Entry[]> {\n\t\tthis.assertOpen();\n\t\treturn this.storage.scanBranch(query, context);\n\t}\n\n\tasync getStats(context: Context): Promise<SessionStats> {\n\t\tthis.assertOpen();\n\t\treturn this.storage.getStats(context);\n\t}\n\n\tasync getName(context: Context): Promise<string | undefined> {\n\t\treturn (await this.getValue(sessionName, context))?.value;\n\t}\n\n\tasync getLabel(targetId: string, context: Context): Promise<string | undefined> {\n\t\treturn (await this.getValue(entryLabel(targetId), context))?.value;\n\t}\n\n\tasync findEntries(query: EntryQuery | undefined, context: Context): Promise<Entry[]> {\n\t\tquery ??= {};\n\t\tthis.assertOpen();\n\t\tconst order = query.order ?? \"desc\";\n\t\tif (query.cursor !== undefined) {\n\t\t\tif (order === \"asc\" && query.cursor.seq === Number.MAX_SAFE_INTEGER) return [];\n\t\t\tif (order === \"desc\" && query.cursor.seq <= 1) return [];\n\t\t}\n\t\treturn this.storage.scanEntries(\n\t\t\t{\n\t\t\t\ttype: query.type,\n\t\t\t\tcustomType: query.customType,\n\t\t\t\torder,\n\t\t\t\tlimit: query.limit,\n\t\t\t\t...(query.cursor === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: order === \"asc\"\n\t\t\t\t\t\t? { fromSeq: query.cursor.seq + 1 }\n\t\t\t\t\t\t: { toSeq: query.cursor.seq - 1 }),\n\t\t\t},\n\t\t\tcontext,\n\t\t);\n\t}\n\n\tasync findEntry(query: EntryQuery | undefined, context: Context): Promise<Entry | undefined> {\n\t\tquery ??= {};\n\t\treturn (\n\t\t\tawait this.findEntries({ ...query, limit: query.limit === undefined ? 1 : Math.min(query.limit, 1) }, context)\n\t\t)[0];\n\t}\n\n\tasync branch(name: string, context: Context): Promise<Branch | undefined> {\n\t\tthis.assertValidBranchName(name);\n\t\tif ((await this.getValue(branchTip(name), context)) === undefined) return undefined;\n\t\treturn this.getOrCreateBranchObject(name);\n\t}\n\n\tasync createBranch(name: string, at: string | null, context: Context): Promise<Branch> {\n\t\tthis.assertOpen();\n\t\tthis.assertValidBranchName(name);\n\t\tawait this.mutate(async (mutator) => {\n\t\t\tif ((await mutator.getValue(branchTip(name), context)) !== undefined) {\n\t\t\t\tthrow new SessionBranchExistsError(name);\n\t\t\t}\n\t\t\tif (at !== null && !(await mutator.getEntries([at], context)).has(at)) {\n\t\t\t\tthrow new SessionUnknownTargetError(at);\n\t\t\t}\n\t\t\tawait mutator.commit([setValueWrite(branchTip(name), at)], context);\n\t\t}, context);\n\t\treturn this.getOrCreateBranchObject(name);\n\t}\n\n\tsetValue<T>(address: Value<T>, next: NoInfer<T>, context: Context): Promise<void> {\n\t\treturn this.mutate(async (mutator) => {\n\t\t\tawait mutator.commit([setValueWrite(address, next)], context);\n\t\t}, context);\n\t}\n\n\tdeleteValue<T>(address: Value<T>, context: Context): Promise<void> {\n\t\treturn this.mutate(async (mutator) => {\n\t\t\tawait mutator.commit([deleteValueWrite(address)], context);\n\t\t}, context);\n\t}\n\n\tappendList<T>(address: ValueList<T>, element: NoInfer<T>, context: Context): Promise<void> {\n\t\treturn this.mutate(async (mutator) => {\n\t\t\tawait mutator.commit([appendListWrite(address, element)], context);\n\t\t}, context);\n\t}\n\n\tdeleteList<T>(address: ValueList<T>, context: Context): Promise<void> {\n\t\treturn this.mutate(async (mutator) => {\n\t\t\tawait mutator.commit([deleteListWrite(address)], context);\n\t\t}, context);\n\t}\n\n\tsetName(name: string | undefined, context: Context): Promise<void> {\n\t\treturn name === undefined ? this.deleteValue(sessionName, context) : this.setValue(sessionName, name, context);\n\t}\n\n\tsetLabel(targetId: string, label: string | undefined, context: Context): Promise<void> {\n\t\tconst address = entryLabel(targetId);\n\t\treturn label === undefined ? this.deleteValue(address, context) : this.setValue(address, label, context);\n\t}\n\n\tclose(context: Context): Promise<void> {\n\t\tif (this.closePromise !== undefined) return this.closePromise;\n\t\tthis.state = \"closing\";\n\t\tthis.closePromise = this.mutationLine\n\t\t\t.seal(this.closedError)\n\t\t\t.then(() => this.storage.close(context))\n\t\t\t.finally(() => {\n\t\t\t\tthis.state = \"closed\";\n\t\t\t\tthis.onClose?.();\n\t\t\t});\n\t\treturn this.closePromise;\n\t}\n\n\tasync getBranchTip(name: string, context: Context): Promise<string | null> {\n\t\tconst stored = await this.getValue(branchTip(name), context);\n\t\tif (stored === undefined) throw new SessionInvariantError(`Unknown branch: ${name}`);\n\t\treturn stored.value;\n\t}\n\n\tasync appendToBranch(\n\t\tname: string,\n\t\tentry: { type: \"message\"; message: AgentMessage } | { type: \"custom\"; customType: string; data?: JsonValue },\n\t\tcontext: Context,\n\t): Promise<string> {\n\t\tthis.assertOpen();\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\" && entry.message.stopReason === \"pending\") {\n\t\t\tthrow new SessionPendingAssistantMessageError();\n\t\t}\n\t\tconst id = this.idGenerator.next();\n\t\tawait this.mutate(async (mutator) => {\n\t\t\tconst tip = await mutator.getValue(branchTip(name), context);\n\t\t\tif (tip === undefined) throw new SessionInvariantError(`Unknown branch: ${name}`);\n\t\t\tawait mutator.commit(\n\t\t\t\t[\n\t\t\t\t\tinsertEntry(\n\t\t\t\t\t\tentry.type === \"message\"\n\t\t\t\t\t\t\t? { id, parentId: tip.value, type: \"message\", message: entry.message }\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\tparentId: tip.value,\n\t\t\t\t\t\t\t\t\ttype: \"custom\",\n\t\t\t\t\t\t\t\t\tcustomType: entry.customType,\n\t\t\t\t\t\t\t\t\t...(entry.data === undefined ? {} : { data: entry.data }),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t\tsetValueWrite(branchTip(name), id),\n\t\t\t\t],\n\t\t\t\tcontext,\n\t\t\t);\n\t\t}, context);\n\t\treturn id;\n\t}\n\n\tprivate getOrCreateBranchObject(name: string): Branch {\n\t\tlet branch = this.branches.get(name);\n\t\tif (branch === undefined) {\n\t\t\tbranch = new StorageBackedBranch(name, this);\n\t\t\tthis.branches.set(name, branch);\n\t\t}\n\t\treturn branch;\n\t}\n\n\tprivate assertValidBranchName(name: string): void {\n\t\tif (name.length === 0) throw new SessionInvalidBranchError(name, \"branch name must not be empty\");\n\t\tif (name.includes(\"\\u0000\")) {\n\t\t\tthrow new SessionInvalidBranchError(name, \"branch name must not contain \\\\u0000\");\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.state !== \"open\") throw this.closedError;\n\t}\n}\n"]}