zidane 8.2.2 → 8.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-oYRjlh71.js → acp-BIInKhVI.js} +6 -6
- package/dist/{acp-oYRjlh71.js.map → acp-BIInKhVI.js.map} +1 -1
- package/dist/acp-cli.js +4 -4
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +1 -1
- package/dist/{agent-CBQglXtH.js → agent-CmhCOmyO.js} +10 -5
- package/dist/{agent-CBQglXtH.js.map → agent-CmhCOmyO.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/chat/pure.d.ts +4 -4
- package/dist/chat/pure.js +7 -7
- package/dist/chat.d.ts +3 -3
- package/dist/chat.js +16 -16
- package/dist/{cli-shared-E5m_pI8A.js → cli-shared-GOehTeqk.js} +2 -2
- package/dist/{cli-shared-E5m_pI8A.js.map → cli-shared-GOehTeqk.js.map} +1 -1
- package/dist/{client-core-BdD20sOv.d.ts → client-core-Dz8hELs4.d.ts} +15 -5
- package/dist/client-core-Dz8hELs4.d.ts.map +1 -0
- package/dist/codemode-worker.js +1 -1
- package/dist/codemode.js +1 -1
- package/dist/{connection-loop-DTk82YVl.d.ts → connection-loop-dm_TN-QD.d.ts} +3 -3
- package/dist/{connection-loop-DTk82YVl.d.ts.map → connection-loop-dm_TN-QD.d.ts.map} +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/{credentials-B9DfBJP9.js → credentials-DxOQmset.js} +2 -2
- package/dist/{credentials-B9DfBJP9.js.map → credentials-DxOQmset.js.map} +1 -1
- package/dist/daemon-cli.js +2 -2
- package/dist/{dispatcher-BICXFGiJ.js → dispatcher-Dclv6HXn.js} +22 -18
- package/dist/dispatcher-Dclv6HXn.js.map +1 -0
- package/dist/{engine-QCX3GV-i.js → engine-C_U6kDfA.js} +6 -6
- package/dist/{engine-QCX3GV-i.js.map → engine-C_U6kDfA.js.map} +1 -1
- package/dist/{eval-CfQlBR81.js → eval-CIHIF_Ns.js} +3 -3
- package/dist/{eval-CfQlBR81.js.map → eval-CIHIF_Ns.js.map} +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/{executor-B8GoXaaE.js → executor-BFaR2nfc.js} +3 -3
- package/dist/{executor-B8GoXaaE.js.map → executor-BFaR2nfc.js.map} +1 -1
- package/dist/{extensions-BeH3D-bq.js → extensions-DIbpUj5R.js} +3 -3
- package/dist/{extensions-BeH3D-bq.js.map → extensions-DIbpUj5R.js.map} +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/{fold-rules-DSypi36k.d.ts → fold-rules-CMpWNPqk.d.ts} +2 -2
- package/dist/{fold-rules-DSypi36k.d.ts.map → fold-rules-CMpWNPqk.d.ts.map} +1 -1
- package/dist/{headless-R4pbpTkm.js → headless-Bs9Lb4vs.js} +4 -4
- package/dist/{headless-R4pbpTkm.js.map → headless-Bs9Lb4vs.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/history-worker.js +1 -1
- package/dist/{host-scoped-settings-CCnaBkCV.js → host-scoped-settings-DxxI_yRo.js} +2 -2
- package/dist/{host-scoped-settings-CCnaBkCV.js.map → host-scoped-settings-DxxI_yRo.js.map} +1 -1
- package/dist/{host-signals-DI9JQvR9.js → host-signals-CQ2jZR-2.js} +5 -5
- package/dist/{host-signals-DI9JQvR9.js.map → host-signals-CQ2jZR-2.js.map} +1 -1
- package/dist/{index-DyQyDLX1.d.ts → index-4mDRyTGJ.d.ts} +1722 -1691
- package/dist/index-4mDRyTGJ.d.ts.map +1 -0
- package/dist/{index-lcuvhQfF.d.ts → index-Dlmu8HnG.d.ts} +5 -5
- package/dist/{index-lcuvhQfF.d.ts.map → index-Dlmu8HnG.d.ts.map} +1 -1
- package/dist/{index-BkNeiJYI.d.ts → index-QsAN9yjZ.d.ts} +3 -3
- package/dist/{index-BkNeiJYI.d.ts.map → index-QsAN9yjZ.d.ts.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +8 -8
- package/dist/ipc/remote.d.ts +5 -5
- package/dist/ipc/remote.js +3 -3
- package/dist/ipc.d.ts +5 -5
- package/dist/ipc.js +9 -9
- package/dist/{links-CN5JFFde.js → links-D5itfH8P.js} +10 -10
- package/dist/{links-CN5JFFde.js.map → links-D5itfH8P.js.map} +1 -1
- package/dist/{mcp-allowlist-BQ5MvhHi.js → mcp-allowlist-BbnTBKNN.js} +15 -4
- package/dist/mcp-allowlist-BbnTBKNN.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/{oauth-TVPPye84.js → oauth-Ck3wQX4Q.js} +133 -43
- package/dist/oauth-Ck3wQX4Q.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/{prelude-HzY_JEGq.js → prelude-qjPUkw4f.js} +26 -2
- package/dist/prelude-qjPUkw4f.js.map +1 -0
- package/dist/{presets-DXeHsd24.js → presets-C4hkykN-.js} +2 -2
- package/dist/{presets-DXeHsd24.js.map → presets-C4hkykN-.js.map} +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/{protocol-BIv1Yuvl.js → protocol-Do_i6Ras.js} +3 -1
- package/dist/protocol-Do_i6Ras.js.map +1 -0
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/neuralwatt.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/{registry-PNi9rsGi.js → registry-DxPP_tAt.js} +3 -3
- package/dist/{registry-PNi9rsGi.js.map → registry-DxPP_tAt.js.map} +1 -1
- package/dist/{remote-hosts-b6Luwdhu.js → remote-hosts-BkOwRszu.js} +4 -4
- package/dist/{remote-hosts-b6Luwdhu.js.map → remote-hosts-BkOwRszu.js.map} +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/{secret-shapes-CEoGmfEE.js → secret-shapes-HvaYVzwc.js} +17 -4
- package/dist/secret-shapes-HvaYVzwc.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-7TUHs42_.js → session-CCsKXID6.js} +6 -2
- package/dist/session-CCsKXID6.js.map +1 -0
- package/dist/{session-factory-CMwIXE-7.js → session-factory-Ct7J9CK1.js} +6 -6
- package/dist/{session-factory-CMwIXE-7.js.map → session-factory-Ct7J9CK1.js.map} +1 -1
- package/dist/session.d.ts +2 -2
- package/dist/session.js +1 -1
- package/dist/{settings-keys-DssjmqAV.js → settings-keys-QoGJ5n3e.js} +4 -4
- package/dist/{settings-keys-DssjmqAV.js.map → settings-keys-QoGJ5n3e.js.map} +1 -1
- package/dist/{shell-path-BUFwqU_A.js → shell-path-BXXtRmEA.js} +4 -4
- package/dist/{shell-path-BUFwqU_A.js.map → shell-path-BXXtRmEA.js.map} +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/{spaces-CyjMh5Zf.js → spaces-C_x2bvEl.js} +14 -4
- package/dist/spaces-C_x2bvEl.js.map +1 -0
- package/dist/{store-L01W0gGI.js → store-BpkWIaSH.js} +10 -30
- package/dist/store-BpkWIaSH.js.map +1 -0
- package/dist/{todo-edits-H77ROt6X.js → todo-edits-DKoxu7Ss.js} +3 -3
- package/dist/{todo-edits-H77ROt6X.js.map → todo-edits-DKoxu7Ss.js.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/{tools-pZVFIJ_K.js → tools-FHEIKGkh.js} +2 -2
- package/dist/{tools-pZVFIJ_K.js.map → tools-FHEIKGkh.js.map} +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-s-yxh0jt.js → transcript-anchors-D8jWLpe5.js} +10 -10
- package/dist/{transcript-anchors-s-yxh0jt.js.map → transcript-anchors-D8jWLpe5.js.map} +1 -1
- package/dist/{transcript-cache-DXdgVuoy.js → transcript-cache-CdHKtx9y.js} +4 -4
- package/dist/{transcript-cache-DXdgVuoy.js.map → transcript-cache-CdHKtx9y.js.map} +1 -1
- package/dist/{transcript-reconcile-DeFLIQ14.js → transcript-reconcile-D_SWw6dX.js} +11 -3
- package/dist/transcript-reconcile-D_SWw6dX.js.map +1 -0
- package/dist/tui.d.ts +7 -7
- package/dist/tui.js +24 -23
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-BbGJ-KSt.d.ts → turn-operations-Dbk3xPKu.d.ts} +19 -4
- package/dist/turn-operations-Dbk3xPKu.d.ts.map +1 -0
- package/dist/{types-C0PJmBaO.d.ts → types-CMXR7A0b.d.ts} +2 -2
- package/dist/{types-C0PJmBaO.d.ts.map → types-CMXR7A0b.d.ts.map} +1 -1
- package/dist/types-CzmghX7C.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/{url-CLl0u4Z8.js → url-D4vG7lzO.js} +2 -2
- package/dist/{url-CLl0u4Z8.js.map → url-D4vG7lzO.js.map} +1 -1
- package/dist/{version-v77Y4A3C.js → version-DDRBX7K6.js} +2 -2
- package/dist/{version-v77Y4A3C.js.map → version-DDRBX7K6.js.map} +1 -1
- package/dist/{workspace-exec-stream-kqARO8iC.js → workspace-exec-stream-iHv9cooF.js} +5 -5
- package/dist/{workspace-exec-stream-kqARO8iC.js.map → workspace-exec-stream-iHv9cooF.js.map} +1 -1
- package/dist/{write-file-QYrcDdl3.js → write-file-ChtRPdEL.js} +3 -3
- package/dist/{write-file-QYrcDdl3.js.map → write-file-ChtRPdEL.js.map} +1 -1
- package/dist/{ws-BD9FN98u.js → ws-B2HB87nz.js} +6 -6
- package/dist/{ws-BD9FN98u.js.map → ws-B2HB87nz.js.map} +1 -1
- package/dist/{ws-client-D7eb2UbH.js → ws-client-DPC9ZJHr.js} +4 -4
- package/dist/{ws-client-D7eb2UbH.js.map → ws-client-DPC9ZJHr.js.map} +1 -1
- package/docs/CODEMODE.md +6 -0
- package/docs/IPC.md +2 -0
- package/docs/ONBOARDING.md +1 -1
- package/package.json +1 -1
- package/dist/client-core-BdD20sOv.d.ts.map +0 -1
- package/dist/dispatcher-BICXFGiJ.js.map +0 -1
- package/dist/index-DyQyDLX1.d.ts.map +0 -1
- package/dist/mcp-allowlist-BQ5MvhHi.js.map +0 -1
- package/dist/oauth-TVPPye84.js.map +0 -1
- package/dist/prelude-HzY_JEGq.js.map +0 -1
- package/dist/protocol-BIv1Yuvl.js.map +0 -1
- package/dist/secret-shapes-CEoGmfEE.js.map +0 -1
- package/dist/session-7TUHs42_.js.map +0 -1
- package/dist/spaces-CyjMh5Zf.js.map +0 -1
- package/dist/store-L01W0gGI.js.map +0 -1
- package/dist/transcript-reconcile-DeFLIQ14.js.map +0 -1
- package/dist/turn-operations-BbGJ-KSt.d.ts.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session-7TUHs42_.js","names":[],"sources":["../src/session/file-map.ts","../src/session/memory.ts","../src/session/remote.ts","../src/session/index.ts"],"sourcesContent":["/**\n * File-map session store.\n *\n * Wraps a narrow 3-method adapter (`get` / `save` / `delete`) that exchanges a flat\n * map of filename → string content. Useful for embedding zidane sessions inside\n * host-provided session backends that only speak in file maps (not zidane's native\n * `SessionStore` shape).\n *\n * Serialization format:\n * - `turns.jsonl` — one `SessionTurn` per line.\n * - `meta.json` — session metadata (id, agentId, status, runs, metadata, timestamps).\n *\n * JSONL for turns keeps history inspectable with tools like `jq` and resilient to\n * partial corruption — parse up to the first bad line and you still have a valid\n * prefix. Metadata lives in its own file so large turn logs don't bloat the\n * metadata path.\n *\n * Scope: each `createFileMapStore` handles a **single session** — the adapter's\n * file map holds at most one zidane session at a time. This matches how host SDKs\n * scope their session stores per conversation.\n *\n * Divergences from the built-in memory / sqlite stores:\n * - `appendTurns` / `updateStatus` / `updateRun` auto-create a minimal `SessionData`\n * record on first write, instead of silently no-oping when the session hasn't been\n * explicitly `save()`-ed. This matches the host-SDK integration path where\n * `createSession(...)` → `agent.run(...)` directly without an explicit `save()` call.\n * - `updateRun` inserts the run if not found in the cached record (rather than\n * silently dropping). Run records therefore always reach the adapter.\n */\n\nimport type { SaveMetaOptions, SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\nimport { mergeSessionMetadata } from './metadata-merge'\n\n/**\n * Host-provided file-map adapter. Three methods exchanging `Record<string, string>`\n * payloads — the whole persistence surface the wrapper needs.\n */\nexport interface FileMapAdapter {\n /** Load the current file map. Returns an empty `files` record when nothing is persisted. */\n get: () => Promise<{ files: Record<string, string> }>\n /** Replace the persisted file map. Full-rewrite semantics. */\n save: (files: Record<string, string>) => Promise<void>\n /** Delete all persisted state. */\n delete: () => Promise<void>\n}\n\n/** Options for {@link createFileMapStore}. */\nexport interface FileMapStoreOptions {\n /** Filename for the JSONL turns log. Default: `turns.jsonl`. */\n turnsFile?: string\n /** Filename for the metadata JSON. Default: `meta.json`. */\n metaFile?: string\n}\n\ninterface MetaShape {\n id: string\n agentId?: string\n /** Optional — absent on files written before projectRoot persistence. */\n projectRoot?: string\n runs: SessionRun[]\n status: SessionData['status']\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\nfunction toMeta(data: SessionData): MetaShape {\n return {\n id: data.id,\n agentId: data.agentId,\n projectRoot: data.projectRoot,\n runs: data.runs,\n status: data.status,\n metadata: data.metadata,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n }\n}\n\nfunction toData(meta: MetaShape, turns: SessionTurn[]): SessionData {\n return {\n id: meta.id,\n agentId: meta.agentId,\n ...(typeof meta.projectRoot === 'string' ? { projectRoot: meta.projectRoot } : {}),\n turns,\n runs: meta.runs,\n status: meta.status,\n metadata: meta.metadata,\n createdAt: meta.createdAt,\n updatedAt: meta.updatedAt,\n }\n}\n\nfunction parseTurnsJsonl(jsonl: string): SessionTurn[] {\n if (!jsonl)\n return []\n const turns: SessionTurn[] = []\n for (const line of jsonl.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed)\n continue\n try {\n turns.push(JSON.parse(trimmed) as SessionTurn)\n }\n catch {\n // Skip malformed lines — preserves the valid prefix on partial corruption.\n }\n }\n return turns\n}\n\nfunction serializeTurnsJsonl(turns: SessionTurn[]): string {\n if (turns.length === 0)\n return ''\n return `${turns.map(t => JSON.stringify(t)).join('\\n')}\\n`\n}\n\n/**\n * Create a single-session `SessionStore` backed by a file-map adapter.\n *\n * @param adapter - Host adapter that loads, saves and deletes the file map.\n * @param options - File names to use. See {@link FileMapStoreOptions}.\n * @example\n * ```ts\n * const session = await createSession({\n * store: createFileMapStore(hostSessionStore),\n * })\n * ```\n */\nexport function createFileMapStore(\n adapter: FileMapAdapter,\n options: FileMapStoreOptions = {},\n): SessionStore {\n const turnsFile = options.turnsFile ?? 'turns.jsonl'\n const metaFile = options.metaFile ?? 'meta.json'\n\n // Cached view of the persisted session. Populated lazily on first access so the\n // factory itself doesn't do I/O.\n let cached: SessionData | null = null\n let hydrated = false\n // In-flight hydrate memo: concurrent first calls share one adapter.get()\n // instead of racing two reads (the loser's view would clobber the winner's\n // subsequent writes — a lost-update race).\n let hydrating: Promise<void> | null = null\n // Turns salvaged from turns.jsonl when meta.json was present but corrupt.\n // We can't trust the (unparseable) meta for the session id, so we hold the\n // turns aside and re-attach them when the next write establishes an id —\n // the alternative (treating the session as missing) would let that write\n // overwrite an intact turns.jsonl with an empty one.\n let recoveredTurns: SessionTurn[] | null = null\n // Serialized `cached.turns`, grown by `appendTurns` so a write only\n // stringifies the new turns instead of the whole transcript (O(n^2) per\n // session otherwise). `null` means stale; `persist` rebuilds it.\n let turnsJsonl: string | null = null\n\n async function hydrate(): Promise<void> {\n if (hydrated)\n return\n if (!hydrating) {\n hydrating = (async () => {\n const { files } = await adapter.get()\n const metaRaw = files[metaFile]\n if (metaRaw) {\n let meta: MetaShape | null = null\n try {\n meta = JSON.parse(metaRaw) as MetaShape\n }\n catch {\n meta = null\n }\n if (meta) {\n cached = toData(meta, parseTurnsJsonl(files[turnsFile] ?? ''))\n }\n else {\n recoveredTurns = parseTurnsJsonl(files[turnsFile] ?? '')\n console.error(`[zidane/file-map] ${metaFile} is not valid JSON, keeping ${recoveredTurns.length} turn(s) from ${turnsFile} for the next write instead of treating the session as missing`)\n }\n }\n hydrated = true\n })().finally(() => {\n hydrating = null\n })\n }\n return hydrating\n }\n\n async function persist(data: SessionData): Promise<void> {\n const meta = toMeta(data)\n turnsJsonl ??= serializeTurnsJsonl(data.turns)\n await adapter.save({\n [metaFile]: JSON.stringify(meta, null, 2),\n [turnsFile]: turnsJsonl,\n })\n }\n\n // Ensure `cached` exists for `sessionId`, creating a minimal record when first written.\n // Returns false when `cached` already holds a different sessionId (request ignored).\n async function ensureCachedFor(sessionId: string): Promise<boolean> {\n await hydrate()\n if (cached) {\n return cached.id === sessionId\n }\n const now = Date.now()\n cached = {\n id: sessionId,\n // Re-attach turns salvaged from a corrupt-meta hydrate so the next\n // persist() doesn't destroy an intact turns.jsonl.\n turns: recoveredTurns ?? [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n recoveredTurns = null\n hydrated = true\n return true\n }\n\n return {\n async load(sessionId: string): Promise<SessionData | null> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return null\n return structuredClone(cached)\n },\n\n async save(data: SessionData): Promise<void> {\n cached = structuredClone(data)\n turnsJsonl = null\n hydrated = true\n await persist(cached)\n },\n\n async saveMeta(data: SessionData, options?: SaveMetaOptions): Promise<void> {\n await hydrate()\n if (!cached || cached.id !== data.id) {\n cached = structuredClone(data)\n turnsJsonl = null\n await persist(cached)\n return\n }\n // Header fields + run upsert; the stored turns stay authoritative, so\n // turns.jsonl is rewritten from the incremental cache, not re-serialized.\n const runs = cached.runs.slice()\n for (const run of data.runs) {\n const idx = runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n runs[idx] = structuredClone(run)\n else\n runs.push(structuredClone(run))\n }\n cached = {\n ...structuredClone({ ...data, turns: [], runs: [], metadata: mergeSessionMetadata(cached.metadata, data.metadata, options?.metaKeys) }),\n turns: cached.turns,\n runs,\n }\n await persist(cached)\n },\n\n async delete(sessionId: string): Promise<void> {\n await hydrate()\n if (cached && cached.id !== sessionId)\n return\n cached = null\n turnsJsonl = null\n await adapter.delete()\n },\n\n async list(filter): Promise<string[]> {\n await hydrate()\n if (!cached)\n return []\n if (filter?.agentId && cached.agentId !== filter.agentId)\n return []\n // file-map stores exactly one session, so the projectRoot filter\n // either keeps it (match / axis off) or drops it entirely.\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null && cached.projectRoot != null)\n return []\n if (typeof v === 'string' && cached.projectRoot !== v)\n return []\n }\n return [cached.id]\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n const added = structuredClone(turns)\n cached!.turns.push(...added)\n if (turnsJsonl !== null)\n turnsJsonl += serializeTurnsJsonl(added)\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return []\n const slice = cached.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(slice) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n const idx = cached!.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n cached!.runs[idx] = structuredClone(run)\n else\n cached!.runs.push(structuredClone(run))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.status = status\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n }\n}\n","/**\n * In-memory session store.\n * Useful for development and testing. Data is lost when the process exits.\n */\n\nimport type { SaveMetaOptions, SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\nimport type { SessionArtifact } from './artifacts'\nimport { matchesArtifactFilter, mergeArtifact, selectArtifactEvictions, sortArtifacts } from './artifacts'\nimport { mergeSessionMetadata } from './metadata-merge'\n\n/**\n * Creates an in-memory {@link SessionStore}. Nothing is persisted.\n *\n * @example\n * ```ts\n * import { createMemoryStore, createSession } from 'zidane/session'\n *\n * const session = await createSession({ store: createMemoryStore() })\n * ```\n */\nexport function createMemoryStore(): SessionStore {\n const sessions = new Map<string, SessionData>()\n // Artifacts live beside — not inside — `SessionData`, mirroring the\n // sqlite store's separate table so `load()`/`save()` never see them.\n const artifacts = new Map<string, SessionArtifact[]>()\n\n // Auto-create a minimal record when turns arrive for an unknown id. Mirrors\n // the file-map store: `createSession({store})` + `agent.run()` appends\n // turns without an explicit `save()` first — a get-or-no-op `appendTurns`\n // would silently lose that history. Deliberately scoped to `appendTurns`\n // only: a late `updateRun`/`updateStatus` racing a `delete()` must stay a\n // no-op rather than resurrect a deleted session.\n function ensureSession(sessionId: string): SessionData {\n let data = sessions.get(sessionId)\n if (!data) {\n const now = Date.now()\n data = {\n id: sessionId,\n turns: [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n sessions.set(sessionId, data)\n }\n return data\n }\n\n return {\n async load(sessionId: string) {\n const data = sessions.get(sessionId)\n return data ? structuredClone(data) : null\n },\n\n async save(session: SessionData) {\n sessions.set(session.id, structuredClone(session))\n },\n\n async saveMeta(session: SessionData, options?: SaveMetaOptions) {\n const existing = sessions.get(session.id)\n if (!existing) {\n sessions.set(session.id, structuredClone(session))\n return\n }\n // Header fields + run upsert; the stored turns stay authoritative.\n const runs = existing.runs.slice()\n for (const run of session.runs) {\n const idx = runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n runs[idx] = structuredClone(run)\n else\n runs.push(structuredClone(run))\n }\n sessions.set(session.id, {\n ...structuredClone({ ...session, turns: [], runs: [], metadata: mergeSessionMetadata(existing.metadata, session.metadata, options?.metaKeys) }),\n turns: existing.turns,\n runs,\n })\n },\n\n async delete(sessionId: string) {\n sessions.delete(sessionId)\n artifacts.delete(sessionId)\n },\n\n async list(filter) {\n let ids = Array.from(sessions.keys())\n if (filter?.agentId) {\n ids = ids.filter(id => sessions.get(id)?.agentId === filter.agentId)\n }\n // `projectRoot` mirrors the SQLite store's tri-state contract:\n // string → only sessions tagged with that root\n // null → only UNTAGGED sessions (legacy / pre-v3 rows)\n // undefined → axis ignored (tagged + untagged both returned)\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n ids = ids.filter(id => sessions.get(id)?.projectRoot == null)\n else if (typeof v === 'string')\n ids = ids.filter(id => sessions.get(id)?.projectRoot === v)\n }\n if (filter?.limit) {\n ids = ids.slice(0, filter.limit)\n }\n return ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const data = ensureSession(sessionId)\n data.turns.push(...structuredClone(turns))\n data.updatedAt = Date.now()\n },\n\n async mutateHistory(sessionId, mutate) {\n // Synchronous from read to write, so nothing can interleave.\n const data = sessions.get(sessionId)\n if (!data)\n return 'no-session'\n const prev = structuredClone({ turns: data.turns, runs: data.runs })\n const next = mutate(prev)\n if (next === null)\n return 'unchanged'\n data.turns = structuredClone(next.turns)\n if (next.runs !== prev.runs)\n data.runs = structuredClone(next.runs)\n data.updatedAt = Date.now()\n return 'ok'\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const data = sessions.get(sessionId)\n if (!data)\n return []\n const sliced = data.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(sliced) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const data = sessions.get(sessionId)\n if (!data)\n return\n // Upsert — see the matching comment on `txnUpdateRun` in\n // sqlite-transactions.ts for the failure mode. tl;dr: the agent's\n // `startRun()` only updates in-memory state; the store first\n // sees the run via `updateRun()`, so an update-only impl\n // silently drops every new run.\n const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n data.runs[idx] = structuredClone(run)\n else\n data.runs.push(structuredClone(run))\n data.updatedAt = Date.now()\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = sessions.get(sessionId)\n if (!data)\n return\n data.status = status\n data.updatedAt = Date.now()\n },\n\n async upsertArtifact(artifact: SessionArtifact) {\n // No auto-create (see ensureSession): unknown session → no-op.\n if (!sessions.has(artifact.sessionId))\n return null\n const rows = artifacts.get(artifact.sessionId) ?? []\n const idx = rows.findIndex(a => a.kind === artifact.kind && a.uri === artifact.uri)\n let stored: SessionArtifact\n if (idx >= 0) {\n stored = mergeArtifact(rows[idx]!, structuredClone(artifact))\n rows[idx] = stored\n }\n else {\n const evict = new Set(selectArtifactEvictions(rows))\n const kept = evict.size > 0 ? rows.filter(a => !evict.has(a.id)) : rows\n stored = structuredClone(artifact)\n kept.push(stored)\n artifacts.set(artifact.sessionId, kept)\n return structuredClone(stored)\n }\n artifacts.set(artifact.sessionId, rows)\n return structuredClone(stored)\n },\n\n async listArtifacts(filter) {\n const source = filter?.sessionId !== undefined\n ? (artifacts.get(filter.sessionId) ?? [])\n : Array.from(artifacts.values()).flat()\n const matched = sortArtifacts(source.filter(a => matchesArtifactFilter(a, filter)))\n const sliced = typeof filter?.limit === 'number' && filter.limit > 0 ? matched.slice(0, filter.limit) : matched\n return structuredClone(sliced)\n },\n\n async deleteArtifact(sessionId: string, artifactId: string) {\n const rows = artifacts.get(sessionId)\n if (!rows)\n return\n const next = rows.filter(a => a.id !== artifactId)\n if (next.length === 0)\n artifacts.delete(sessionId)\n else\n artifacts.set(sessionId, next)\n },\n }\n}\n","/**\n * Remote session store via HTTP API.\n *\n * Expects a REST API with:\n * GET {url}/sessions/{id} -> SessionData | 404\n * PUT {url}/sessions/{id} -> save SessionData\n * DELETE {url}/sessions/{id} -> delete\n * GET {url}/sessions?agentId=&limit=&projectRoot= -> { ids: string[] }\n * `projectRoot=__null__` is the wire encoding for \"untagged only\".\n * POST {url}/sessions/{id}/turns -> append turns\n * GET {url}/sessions/{id}/turns?from=&limit= -> SessionTurn[]\n * PUT {url}/sessions/{id}/runs/{runId} -> update run\n * PATCH {url}/sessions/{id} -> { status }\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\n/** Options for {@link createRemoteStore}. */\nexport interface RemoteStoreOptions {\n /** Base URL of the session API */\n url: string\n /** Optional headers (e.g. for authentication) */\n headers?: Record<string, string>\n /** Per-request timeout in ms. Default 30s — bounds a hung backend. */\n timeoutMs?: number\n}\n\nconst TRAILING_SLASH = /\\/$/\nconst DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Creates a {@link SessionStore} that talks to a REST session API over HTTP. See the module comment for the endpoints it calls.\n *\n * @param options - API base URL, headers and timeout.\n * @example\n * ```ts\n * import { createRemoteStore, createSession } from 'zidane/session'\n *\n * const store = createRemoteStore({ url: 'https://api.example.com', headers: { authorization: 'Bearer token' } })\n * const session = await createSession({ store })\n * ```\n */\nexport function createRemoteStore(options: RemoteStoreOptions): SessionStore {\n const baseUrl = options.url.replace(TRAILING_SLASH, '')\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const defaultHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n }\n\n async function request(path: string, init?: RequestInit): Promise<Response> {\n const url = `${baseUrl}${path}`\n // Bound every call so a hung backend can't wedge the caller forever.\n const res = await fetch(url, {\n ...init,\n headers: { ...defaultHeaders, ...init?.headers },\n signal: init?.signal ?? AbortSignal.timeout(timeoutMs),\n })\n return res\n }\n\n /**\n * Parse a JSON response body, rethrowing parse failures with URL/status\n * context so a malformed backend reply produces a diagnosable error\n * instead of an opaque `Unexpected token` from deep in `res.json()`.\n */\n async function parseJson<T>(res: Response, path: string): Promise<T> {\n try {\n return await res.json() as T\n }\n catch (cause) {\n throw new Error(\n `Remote session: malformed JSON from ${path} (${res.status} ${res.statusText})`,\n { cause },\n )\n }\n }\n\n return {\n async load(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`)\n if (!res.ok) {\n if (res.status === 404)\n return null\n throw new Error(`Remote session load failed: ${res.status} ${res.statusText}`)\n }\n return await parseJson<SessionData>(res, `/sessions/${sessionId}`)\n },\n\n async save(session: SessionData) {\n const res = await request(`/sessions/${encodeURIComponent(session.id)}`, {\n method: 'PUT',\n body: JSON.stringify(session),\n })\n if (!res.ok) {\n throw new Error(`Remote session save failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async delete(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'DELETE',\n })\n if (!res.ok && res.status !== 404) {\n throw new Error(`Remote session delete failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async list(filter) {\n const params = new URLSearchParams()\n if (filter?.agentId)\n params.set('agentId', filter.agentId)\n if (filter?.limit)\n params.set('limit', String(filter.limit))\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n params.set('projectRoot', '__null__')\n else if (typeof v === 'string')\n params.set('projectRoot', v)\n }\n\n const query = params.toString()\n const path = query ? `/sessions?${query}` : '/sessions'\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote session list failed: ${res.status} ${res.statusText}`)\n }\n\n const body = await parseJson<{ ids: string[] }>(res, path)\n if (!Array.isArray(body?.ids)) {\n throw new TypeError(`Remote session list: expected { ids: string[] } from ${path}, got ${typeof body?.ids}`)\n }\n return body.ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}/turns`, {\n method: 'POST',\n body: JSON.stringify(turns),\n })\n if (!res.ok) {\n throw new Error(`Remote appendTurns failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const params = new URLSearchParams()\n if (from)\n params.set('from', String(from))\n if (limit !== undefined)\n params.set('limit', String(limit))\n\n const query = params.toString()\n const path = `/sessions/${encodeURIComponent(sessionId)}/turns${query ? `?${query}` : ''}`\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote getTurns failed: ${res.status} ${res.statusText}`)\n }\n\n const turns = await parseJson<SessionTurn[]>(res, path)\n if (!Array.isArray(turns)) {\n throw new TypeError(`Remote getTurns: expected SessionTurn[] from ${path}, got ${typeof turns}`)\n }\n return turns\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const res = await request(\n `/sessions/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(run.id)}`,\n {\n method: 'PUT',\n body: JSON.stringify(run),\n },\n )\n if (!res.ok) {\n throw new Error(`Remote updateRun failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'PATCH',\n body: JSON.stringify({ status }),\n })\n if (!res.ok) {\n throw new Error(`Remote updateStatus failed: ${res.status} ${res.statusText}`)\n }\n },\n }\n}\n","/**\n * Session management for agents.\n *\n * A session tracks identity, turn history, and run metadata.\n * Plug in any storage backend by implementing the SessionStore interface,\n * or use one of the built-in stores: memory, sqlite, remote, file map.\n */\n\nimport type { RunErrorDetail } from '../errors'\nimport type { JsonValue, SessionMessage, SessionTurn, TurnUsage } from '../types'\nimport type { ArtifactFilter, SessionArtifact, SessionArtifactInput } from './artifacts'\nimport type { PairingDrop, ToWireMessagesOptions } from './messages'\nimport type { SaveMetaOptions } from './metadata-merge'\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport { Buffer } from 'node:buffer'\nimport { dropInvalidToolPairings, toWireMessages } from './messages'\n\nexport type { SessionContentBlock, SessionMessage, SessionTurn } from '../types'\nexport {\n ARTIFACT_KINDS,\n ARTIFACT_META_MAX_BYTES,\n ARTIFACTS_METADATA_KEY,\n ARTIFACTS_PER_SESSION_CAP,\n matchesArtifactFilter,\n mergeArtifact,\n normalizeArtifactInput,\n selectArtifactEvictions,\n sortArtifacts,\n} from './artifacts'\nexport type { ArtifactFilter, SessionArtifact, SessionArtifactInput } from './artifacts'\nexport { createFileMapStore } from './file-map'\n\nconst EXTENSION_SESSION_METADATA_KEY = 'extensions'\nconst EXTENSION_SESSION_ITEM_MAX_BYTES = 16_384\nconst EXTENSION_SESSION_TOTAL_MAX_BYTES = 65_536\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\nconst extensionSessionStorage = new AsyncLocalStorage<Session>()\n\n/**\n * Run `fn` with `session` bound as the active extension session for the\n * duration of the call (and every async continuation it spawns). Uses\n * `AsyncLocalStorage.run` rather than `enterWith` so concurrent agents in the\n * same process (e.g. the GUI's multi-session host) never observe each other's\n * session through `currentExtensionSession()`.\n * @param session - Session to bind. When `undefined`, `fn` runs without a binding.\n * @param fn - Function to run with the binding in place.\n */\nexport function withExtensionSession<T>(session: Session | undefined, fn: () => T): T {\n if (!session)\n return fn()\n return extensionSessionStorage.run(session, fn)\n}\n\n/**\n * Returns the session bound by {@link withExtensionSession} for the current async context, or `undefined` when none is bound.\n */\nexport function currentExtensionSession(): Session | undefined {\n return extensionSessionStorage.getStore()\n}\n\n/**\n * Reads one value an extension stored in the session metadata. Returns a copy, or `undefined` when the session, extension or key is missing or a name is unsafe.\n *\n * @param session - Session to read, if any.\n * @param extensionName - Name of the extension that owns the value.\n * @param key - Key under the extension namespace.\n */\nexport function getExtensionSessionState<T = JsonValue>(\n session: Session | undefined,\n extensionName: string,\n key: string,\n): T | undefined {\n if (!session || !isSafeExtensionStateKey(extensionName) || !isSafeExtensionStateKey(key))\n return undefined\n const root = session.metadata[EXTENSION_SESSION_METADATA_KEY]\n if (!root || typeof root !== 'object' || Array.isArray(root))\n return undefined\n const byExtension = (root as Record<string, unknown>)[extensionName]\n if (!byExtension || typeof byExtension !== 'object' || Array.isArray(byExtension))\n return undefined\n const stored = (byExtension as Record<string, unknown>)[key]\n if (stored === undefined)\n return undefined\n // Hand back a copy so a caller mutating the result can't reach into the live\n // session metadata behind `setMeta`/`save`.\n return jsonClone(stored as JsonValue) as T\n}\n\n/**\n * Stores a JSON value under an extension namespace in the session metadata and saves the session when it has a store. Does nothing without a session.\n *\n * Throws when a name is unsafe or the value exceeds the size limits (16 KiB per item, 64 KiB for all extension state).\n *\n * @param session - Session to write, if any.\n * @param extensionName - Name of the extension that owns the value.\n * @param key - Key under the extension namespace.\n * @param value - JSON value to store. It is copied.\n */\nexport async function setExtensionSessionState(\n session: Session | undefined,\n extensionName: string,\n key: string,\n value: JsonValue,\n): Promise<void> {\n if (!session)\n return\n assertSafeExtensionStateKey(extensionName, 'extensionName')\n assertSafeExtensionStateKey(key, 'key')\n assertJsonBounded(value, EXTENSION_SESSION_ITEM_MAX_BYTES, `extension session state \"${extensionName}.${key}\"`)\n\n const root = cloneExtensionStateRoot(session.metadata[EXTENSION_SESSION_METADATA_KEY])\n const byExtension = clonePlainRecord(root[extensionName])\n // Deep-copy on write so later mutations to the caller's object don't bleed\n // into persisted state.\n byExtension[key] = jsonClone(value)\n root[extensionName] = byExtension\n assertJsonBounded(root as JsonValue, EXTENSION_SESSION_TOTAL_MAX_BYTES, 'extension session state')\n session.setMeta(EXTENSION_SESSION_METADATA_KEY, root)\n if (session.store)\n await session.save()\n}\n\n/**\n * Removes one value from an extension namespace in the session metadata and saves the session when it has a store. Does nothing when the value does not exist.\n *\n * @param session - Session to modify, if any.\n * @param extensionName - Name of the extension that owns the value.\n * @param key - Key to remove.\n */\nexport async function deleteExtensionSessionState(\n session: Session | undefined,\n extensionName: string,\n key: string,\n): Promise<void> {\n if (!session || !isSafeExtensionStateKey(extensionName) || !isSafeExtensionStateKey(key))\n return\n const root = cloneExtensionStateRoot(session.metadata[EXTENSION_SESSION_METADATA_KEY])\n const byExtension = clonePlainRecord(root[extensionName])\n if (!(key in byExtension))\n return\n delete byExtension[key]\n if (Object.keys(byExtension).length > 0)\n root[extensionName] = byExtension\n else\n delete root[extensionName]\n session.setMeta(EXTENSION_SESSION_METADATA_KEY, root)\n if (session.store)\n await session.save()\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Transitive `resumedFromRunId` chain starting at `runId` (inclusive).\n *\n * A resumed run only persists the turns IT produced — the turns it was\n * seeded with still carry the original run's id. So reconstructing a\n * resumed child's full conversation means collecting turns from every run\n * in its resume lineage, not just the latest link. Cycle-guarded (a\n * malformed chain terminates instead of looping).\n * @param runs - Run records of the session.\n * @param runId - Run to start from.\n */\nexport function resumeChainRunIds(runs: readonly SessionRun[], runId: string): Set<string> {\n const chain = new Set<string>()\n let id: string | undefined = runId\n while (id && !chain.has(id)) {\n chain.add(id)\n id = runs.find(r => r.id === id)?.resumedFromRunId\n }\n return chain\n}\n\n/** Record of one agent run inside a session. */\nexport interface SessionRun {\n /** Run id. */\n id: string\n /** Start time as epoch milliseconds. */\n startedAt: number\n /** End time as epoch milliseconds. Absent while the run is in progress. */\n endedAt?: number\n /** Prompt the run started with. */\n prompt: string\n /** Run state. A persisted `running` run whose process died is shown as `aborted` on load. */\n status: 'running' | 'completed' | 'aborted' | 'error'\n /** Number of turns the run produced. */\n turns?: number\n /** Input tokens used by the run. */\n tokensIn?: number\n /** Output tokens used by the run. */\n tokensOut?: number\n /** Error message when the run failed. */\n error?: string\n /** What `error`'s one line leaves out (the provider's own text, a limit's reset). */\n errorDetail?: RunErrorDetail\n /** Per-turn usage breakdown */\n turnUsage?: TurnUsage[]\n /** Total usage across all turns */\n totalUsage?: TurnUsage\n /** Estimated cost in USD */\n cost?: number\n /**\n * The run that spawned this one, when the agent is a subagent sharing its\n * parent's session. Undefined on top-level `agent.run()`. Consumers can walk\n * `runs` by `parentRunId` to reconstruct the subagent tree.\n */\n parentRunId?: string\n /**\n * The prior run this one continues (see `AgentRunOptions.resumeRunId`).\n * Set when a stopped/failed subagent run was resumed — consumers can\n * chain runs by `resumedFromRunId` to render “child-4 (resumes child-2)”\n * style markers. Absent on normal runs.\n */\n resumedFromRunId?: string\n /**\n * Host-assigned display label (e.g. the spawn tool's `child-3`), persisted\n * so label → run resolution is a stored fact instead of a positional\n * guess. Positional `child-N` numbering can desynchronize from announced\n * labels (concurrent spawns `startRun` out of reservation order, a\n * pre-`startRun` throw advances the counter without a run, and reload\n * ordering ties break lexicographically) — resolving by stored label is\n * immune to all three. Absent on top-level runs and legacy sessions.\n */\n label?: string\n /**\n * The `spawn` tool call that started this run, in the parent's turn (see\n * `AgentRunOptions.parentCallId`): pairs a child with its call exactly,\n * where parallel calls share a task. Absent on top-level runs and older\n * sessions.\n */\n parentCallId?: string\n /**\n * Zero-based subagent depth. 0 = top-level run, 1 = direct child, …\n * Recorded here so hosts can query/filter by level without walking the tree.\n */\n depth?: number\n /**\n * How this run was initiated when not a plain user prompt. `command` marks\n * a sub-run driven from a `/` command's `ctx.runAgent`; `display` is the\n * short line the transcript shows instead of the full injected prompt\n * (which often embeds bulky material like a diff). Presentation-only —\n * the wire/model payload is unaffected. Absent on normal runs.\n */\n origin?: { kind: 'command', display?: string }\n}\n\n/** A session's turns and runs: what a history edit reads and writes (`SessionStore.mutateHistory`). */\nexport interface SessionHistory {\n /** The session's turns, in order. */\n turns: SessionTurn[]\n /** The session's runs, in order. */\n runs: SessionRun[]\n}\n\n/** Persisted form of a session: identity, turns, runs and metadata. */\nexport interface SessionData {\n /** Session id. */\n id: string\n /** Label of the agent profile that owns the session. */\n agentId?: string\n /**\n * Absolute path of the project this session belongs to — typically\n * the git root resolved from `cwd` at creation time, falling back to\n * `cwd` itself when not in a git repo. Set ONCE on creation and never\n * mutated thereafter (the session \"belongs\" to that project forever).\n *\n * Used by the TUI's sessions list to filter rows by current project,\n * so the user only sees conversations relevant to where they are\n * working — without needing one `.{prefix}/` directory per project.\n *\n * `undefined` on pre-tagging legacy sessions; the chat layer treats\n * those as \"untagged\" and hides them unless `Settings.showAllProjects`\n * is on.\n */\n projectRoot?: string\n /** Turn history, oldest first. */\n turns: SessionTurn[]\n /** Records of the runs in this session. */\n runs: SessionRun[]\n /** Session state. A persisted `running` status is stale after a restart and is shown as `idle` on load. */\n status: 'idle' | 'running' | 'completed' | 'error'\n /** Free-form metadata, such as the title, model and extension state. */\n metadata: Record<string, unknown>\n /** Creation time as epoch milliseconds. */\n createdAt: number\n /** Last update time as epoch milliseconds. */\n updatedAt: number\n}\n\n/**\n * Compact per-session summary returned by {@link SessionStore.listMeta}.\n * Raw facts only — presentation (title derivation, fallback strings)\n * stays in the chat layer (`listSessionMeta` / `deriveSessionTitle`).\n */\nexport interface SessionMetaRow {\n /** Session id. */\n id: string\n /** Header metadata (may carry a stored `title`). */\n metadata: Record<string, unknown>\n /** Absent for untagged (legacy) sessions. */\n projectRoot?: string\n /** Last update time as epoch milliseconds. */\n updatedAt: number\n /**\n * The store's change counter for this session: bumped by every write to\n * it (in the write's own transaction), so it only grows, whatever\n * `updatedAt` a writer stamps. Absent from stores that keep none.\n */\n rev?: number\n /** Number of turns. */\n turnCount: number\n /** Count of `role: 'user'` turns. */\n userMessageCount: number\n /** Number of run records. */\n runCount: number\n /**\n * First `role: 'user'` turn — the title-derivation source. Stores may\n * omit it when `metadata.title` is already set (no derivation needed).\n */\n firstUserTurn?: SessionTurn\n /**\n * Header fields consumers like the daemon's `sessions.list` need to\n * build full rows without a per-session `load()`. Optional (additive\n * contract extension) — callers fall back to `load()` when absent.\n */\n agentId?: string\n /** Creation time as epoch milliseconds. */\n createdAt?: number\n /** Session state. */\n status?: SessionData['status']\n /**\n * Number of artifacts recorded for the session. Optional (additive):\n * only stores implementing the artifact methods populate it; consumers\n * treat absence as \"unknown\", not zero.\n */\n artifactCount?: number\n}\n\n// ---------------------------------------------------------------------------\n// SessionStore interface (pluggable backend)\n// ---------------------------------------------------------------------------\n\n/**\n * Storage backend for sessions. Implement it to persist sessions somewhere else, or use a bundled store: memory, SQLite, remote or file map.\n */\nexport interface SessionStore {\n /** Optional: generate a session ID server-side (e.g. a database-issued UUID). */\n generateSessionId?: () => string | Promise<string>\n\n /**\n * Optional: generate a turn ID server-side for session-managed user turns.\n *\n * Provider-loop assistant and tool-result turns use AgentClock.randomUUID()\n * instead, so durable-execution adapters can keep those IDs replay-stable.\n */\n generateTurnId?: () => string | Promise<string>\n\n /** Load a session by ID. Returns null if not found. */\n load: (sessionId: string) => Promise<SessionData | null>\n\n /**\n * Save a session (create or update, full document). Bulk overwrite:\n * turns and runs on disk are replaced by the caller's snapshot, so a\n * stale snapshot silently discards anything another writer appended.\n * `Session.save()` only routes here when the turn list was explicitly\n * replaced (`setTurns` / `setRuns` / load-time heal); everything else\n * goes through {@link saveMeta}.\n */\n save: (session: SessionData) => Promise<void>\n\n /**\n * Optional: persist header fields only (agentId, projectRoot, status,\n * metadata, timestamps) plus an UPSERT of the runs present in the\n * snapshot — never deletes turns or runs. This is what metadata writes\n * (title, model, todos, extension state) need.\n *\n * Scope of the guarantee: TURNS appended by a concurrent process survive.\n * The header itself is still last-writer-wins — `status` and `agentId`\n * are written wholesale from the caller's snapshot, and so is `metadata`\n * unless `options.metaKeys` names the changed keys (`Session.save()`\n * always does; stores merge them with {@link mergeSessionMetadata}), and every run in the snapshot is upserted as-is (a reader\n * that loaded through `loadSession` carries the in-memory stale-running\n * demotion, so it can write `idle` / `aborted` over a live owner's marker\n * until the owner's next write). Readers that only need one key should\n * prefer a keyed patch (the host's `patchSessionMeta`) over `save()`.\n * Stores that omit it fall back to `save`, keeping the historical bulk\n * semantics.\n */\n saveMeta?: (session: SessionData, options?: SaveMetaOptions) => Promise<void>\n\n /** Delete a session. */\n delete: (sessionId: string) => Promise<void>\n\n /**\n * List session IDs, optionally filtered. `projectRoot` restricts to\n * sessions whose `SessionData.projectRoot` matches exactly — untagged\n * (legacy) sessions are NOT returned under that filter; pass `null`\n * explicitly to ask for untagged ones, or omit the field to ignore\n * the axis entirely. `agentId` filters by recorded agent; the two\n * conditions AND together when both are set.\n */\n list: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<string[]>\n\n /**\n * Optional: list session summaries without materializing full sessions.\n *\n * The generic fallback (`listSessionMeta` in `zidane/chat`) is `list()`\n * + `load()` per id — O(every turn in the store) because each `load`\n * parses the full turn history just to count rows and derive a title.\n * Stores that can answer from an index (SQL aggregates, a denormalized\n * header) should implement this; the sqlite store turns a multi-hundred-\n * ms TUI boot cost into a single query.\n *\n * Same filter contract and ordering (most recently updated first) as\n * {@link list}.\n */\n listMeta?: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<SessionMetaRow[]>\n\n /**\n * Optional: one session's header row WITHOUT materializing turns.\n * Hot-path reads (the cross-process run guard checks `status` +\n * `updatedAt` on every prompt send) must not pay a multi-MB turn parse\n * on long sessions. Callers fall back to `load()` when absent.\n */\n getMeta?: (sessionId: string) => Promise<SessionMetaRow | null>\n\n /** Append new turns to a session (incremental, avoids full re-save). */\n appendTurns: (sessionId: string, turns: SessionTurn[]) => Promise<void>\n\n /** Return a slice of turns for a session. */\n getTurns: (sessionId: string, from?: number, limit?: number) => Promise<SessionTurn[]>\n\n /**\n * Optional: rewrite a session's history as ONE atomic read-modify-write,\n * so turns appended between the read and the write can't be erased (a\n * `load()` + `save()` pair can). `mutate` gets the current turns and\n * runs and returns the next ones, or `null` to leave them untouched; a\n * throw inside it aborts without writing. Runs are written only when it\n * returns a different `runs` array. History edits (delete / edit a turn,\n * rewind) use it when present and fall back to `load()` + `save()`.\n */\n mutateHistory?: (sessionId: string, mutate: (history: SessionHistory) => SessionHistory | null) => Promise<'ok' | 'no-session' | 'unchanged'>\n\n /**\n * Optional: the turns-only form of {@link SessionStore.mutateHistory}.\n * When `mutateHistory` is absent, edits that leave the runs alone (delete\n * or edit a turn) use it; a rewind falls back to `load()` + `save()`.\n * @deprecated Implement `mutateHistory` instead.\n */\n mutateTurns?: (sessionId: string, mutate: (turns: SessionTurn[]) => SessionTurn[] | null) => Promise<'ok' | 'no-session' | 'unchanged'>\n\n /**\n * Persist a run record (called at run start with `status: 'running'`, and\n * after completeRun / abortRun / errorRun).\n *\n * **Upsert semantics required.** The agent's run lifecycle calls\n * `session.startRun()` to register a new run in IN-MEMORY state and\n * then `updateRun()`. The store doesn't see the run before the first\n * `updateRun()` — `appendTurns` persists turns but not runs. An\n * update-only implementation would silently drop every newly-created\n * run record, leaving\n * the on-disk `runs[]` missing entries that the persisted turns\n * reference (which `eventsFromTurns` then mis-classifies via\n * `ancestryOf` → broken depth, broken `child-N` labels, broken\n * `userPrompts` history).\n *\n * Implementations MUST insert when the id doesn't exist and update\n * when it does. The bundled `sqlite` / `memory` / `file-map` stores\n * all follow this contract; remote stores using HTTP PUT get upsert\n * for free via REST semantics.\n */\n updateRun: (sessionId: string, run: SessionRun) => Promise<void>\n\n /**\n * Update the top-level status of a session.\n *\n * `preserveUpdatedAt` (default false) writes the status WITHOUT\n * touching `updatedAt` — for pure-read heals (`loadSession`'s stale\n * `'running'` demotion) that must not reorder recency-sorted session\n * lists. Best-effort contract: stores that don't manage `updatedAt`\n * themselves (memory, file-map, remote) may ignore the option; the\n * sqlite store honors it.\n */\n updateStatus: (sessionId: string, status: SessionData['status'], options?: { preserveUpdatedAt?: boolean }) => Promise<void>\n\n /**\n * Optional: record an artifact (see `./artifacts.ts`). Upsert on\n * `(sessionId, kind, uri)` — an existing row keeps its `id` / `runId` /\n * `turnId` / `createdAt` and refreshes `label` / `meta` / `updatedAt`\n * (`mergeArtifact`). Resolves to the canonical stored row. Unknown\n * session ids are a no-op returning `null` (no ghost headers; same\n * posture as `updateRun`). Implementations enforce\n * `ARTIFACTS_PER_SESSION_CAP` via `selectArtifactEvictions`.\n *\n * Every artifact method is optional: stores that don't implement them\n * (file-map, remote, third-party) keep working — `Session.addArtifact`\n * degrades to a no-op and listings come back empty.\n */\n upsertArtifact?: (artifact: SessionArtifact) => Promise<SessionArtifact | null>\n\n /**\n * Optional: list artifacts, newest `updatedAt` first. `filter.sessionId`\n * scopes to one session; `filter.projectRoot` follows the tri-state\n * contract of {@link list}; omit both for every artifact in the store.\n */\n listArtifacts?: (filter?: ArtifactFilter) => Promise<SessionArtifact[]>\n\n /** Optional: remove one artifact. Unknown ids are a no-op. */\n deleteArtifact?: (sessionId: string, artifactId: string) => Promise<void>\n\n /**\n * Optional: release backend resources (e.g. close the SQLite handle).\n * Hosts should call this on teardown when present; stores backed by\n * memory or remote HTTP have nothing to release and may omit it.\n */\n close?: () => void\n}\n\n// ---------------------------------------------------------------------------\n// Session (live instance wrapping a SessionData)\n// ---------------------------------------------------------------------------\n\n/**\n * A live session: the turn history and run records of one conversation, with an optional store that persists them.\n */\nexport interface Session {\n /** Session ID */\n readonly id: string\n\n /** Agent ID (optional label) */\n readonly agentId?: string\n\n /**\n * Re-label the session's agent profile (in memory; persisted by the\n * next `save()`). Sessions remember their profile — pickers restore it\n * on activation and other windows converge on it via `sessions.touch`.\n */\n setAgentId: (agentId: string) => void\n\n /**\n * Project this session was created under — see {@link SessionData.projectRoot}.\n * Set once on creation; surfaces here for read-only inspection.\n */\n readonly projectRoot?: string\n\n /**\n * Raw persisted turn history — kept pristine for fidelity (the model's\n * reasoning chain stays intact across reloads).\n *\n * **DO NOT pass this directly to a provider request.** Mid-pair\n * persistence (interrupted runs, partial-result writes, compaction\n * boundaries) leaves orphan `tool_use` / `tool_result` blocks here that\n * every provider 400s on (`tool_result must be preceded by a tool_call\n * with the same toolCallId`). The agent's own loop repairs these\n * just-in-time at wire-send time and never writes the repair back to\n * `turns`.\n *\n * For provider requests built outside `agent.run()`, use\n * {@link Session.toMessages} — it applies the same defensive pairing\n * repair and resolves to a guaranteed-safe `SessionMessage[]`.\n */\n readonly turns: SessionTurn[]\n\n /**\n * True when this session has no turns yet.\n *\n * Use this as a first-prompt signal when setting up a run — e.g. writing initial\n * configuration only on fresh sessions. Equivalent to `turns.length === 0`.\n */\n readonly isEmpty: boolean\n\n /** Top-level session status */\n readonly status: SessionData['status']\n\n /** All runs in this session */\n readonly runs: SessionRun[]\n\n /** Arbitrary metadata */\n readonly metadata: Record<string, unknown>\n\n /** Creation time as epoch milliseconds. */\n readonly createdAt: number\n\n /** Last update time as epoch milliseconds. */\n readonly updatedAt: number\n\n /**\n * Start tracking a new run. `extras.parentRunId` + `extras.depth` are\n * populated by the spawn tool when a child agent shares its parent's\n * session; regular top-level `agent.run()` calls omit them.\n */\n startRun: (runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number, resumedFromRunId?: string, label?: string, parentCallId?: string }) => void\n\n /** Mark a run as completed */\n completeRun: (runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /**\n * Mark a run as aborted. Optional `stats` lets the agent backfill the run's token totals when\n * the abort happened *after* the loop accumulated meaningful usage —\n * common when the user presses esc mid-streaming. Without it, the run\n * record reads `0 in / 0 out` on reload regardless of how much was\n * spent before the abort. Same shape as `completeRun`'s stats so the\n * persisted `totalUsage` aggregate stays consistent across paths.\n */\n abortRun: (runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as errored. Optional `stats` — same rationale as `abortRun.stats`. */\n errorRun: (runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }, detail?: RunErrorDetail) => void\n\n /** Append turns to in-memory history AND persist via store.appendTurns (if store present) */\n appendTurns: (turns: SessionTurn[]) => Promise<void>\n\n /** Replace all turns in-memory (does not persist — use save() for that) */\n setTurns: (turns: SessionTurn[]) => void\n\n /**\n * Replace all runs in-memory (does not persist — use save() for that).\n * Mirrors {@link setTurns} for the fork / restore case: callers that\n * bootstrap a session from an externally-derived snapshot (e.g.\n * `onForkTurn` copying parent runs into a child session) need this so\n * the cloned runs land in `data.runs` before the first `save()`.\n * Production agent runs continue to mutate runs via `startRun` /\n * `completeRun` / `updateRun`; this is the bulk-replace escape hatch.\n */\n setRuns: (runs: SessionRun[]) => void\n\n /** Update the session status in memory AND via store.updateStatus (if store present) */\n updateStatus: (status: SessionData['status']) => Promise<void>\n\n /** Persist an updated run record via store.updateRun (if store present) */\n updateRun: (run: SessionRun) => Promise<void>\n\n /** Generate a session-managed turn ID using store.generateTurnId if available, else crypto.randomUUID() */\n generateTurnId: () => string | Promise<string>\n\n /** Set metadata key. `touch: false` keeps `updatedAt` (a rename or a favorite is not activity). */\n setMeta: (key: string, value: unknown, options?: { touch?: boolean }) => void\n\n /**\n * Record an artifact this session produced. Fills `id`, timestamps and\n * defaults `projectRoot` to the session's own; `origin` ties it to the\n * producing run/turn for transcript anchoring. Resolves to the stored\n * row, or `undefined` when there is no store or the store doesn't\n * implement artifacts (never throws for that case — emission is\n * best-effort by contract).\n */\n addArtifact: (input: SessionArtifactInput, origin?: { runId?: string, turnId?: string }) => Promise<SessionArtifact | undefined>\n\n /** This session's artifacts, newest first. Empty when unsupported. */\n listArtifacts: (filter?: Pick<ArtifactFilter, 'kind' | 'since' | 'limit'>) => Promise<SessionArtifact[]>\n\n /**\n * Persist the session to the store. When the store implements `saveMeta`,\n * this writes the header, the changed metadata keys and the runs, and leaves\n * turns alone, unless `setTurns` or `setRuns` replaced them. In every other\n * case it overwrites the whole document through `store.save`. Throws when\n * the session has no store.\n */\n save: () => Promise<void>\n\n /**\n * Project the session's persisted turns into wire-ready messages safe to\n * hand to any provider.\n *\n * The canonical answer to \"I need to send these turns to Anthropic /\n * OpenAI from outside `agent.run()`\" (sub-agent classifier, side-channel\n * summarizer, replay worker, …). Runs the same defensive pairing repair\n * the agent loop applies just-in-time at wire-send time\n * ({@link ensureToolResultPairing}), so orphan `tool_use` blocks and\n * dangling `tool_result`s never reach the provider as the 400\n * \"tool_result must be preceded by a tool_call with the same toolCallId\".\n *\n * Pass `provider` to also enforce the user-tail invariant\n * ({@link ensureEndsWithUserMessage}) — required for opus 4.7 / o-series\n * style models that reject assistant-prefill.\n *\n * Pure + idempotent. Resolves content refs only in the returned messages\n * and does NOT mutate `session.turns`. **Do not write the result back via\n * `setTurns` / `appendTurns`** — synthetic placeholders or resolved\n * payloads will contaminate persisted history.\n */\n toMessages: (options?: ToWireMessagesOptions) => Promise<SessionMessage[]>\n\n /** Serialize to SessionData */\n toJSON: () => SessionData\n\n /**\n * The backing store, when one was provided. Exposed so hosts can call\n * its optional `close()` on teardown (e.g. release the SQLite handle).\n */\n readonly store?: SessionStore\n}\n\n// ---------------------------------------------------------------------------\n// createSession\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createSession}. */\nexport interface CreateSessionOptions {\n /** Session ID. If omitted and store provides generateSessionId, that is used. */\n id?: string\n /** Agent ID label */\n agentId?: string\n /**\n * Project tag — see {@link SessionData.projectRoot}. Stamped once on\n * creation; ignored when `_data` is set (restoring an existing\n * session preserves whatever was already persisted there). The chat\n * layer's session factory stamps `projectRootOf(cwd)` so sessions\n * started from the same repo — any subdir, any linked worktree — share\n * one tag.\n */\n projectRoot?: string\n /** Initial metadata */\n metadata?: Record<string, unknown>\n /** Storage backend (optional, enables save/load) */\n store?: SessionStore\n /**\n * Observability hook for the load-time pairing heal: fired once per block\n * or turn `dropInvalidToolPairings` deleted while restoring `_data`.\n * Deletions are permanent once the session is saved, so hosts that alert\n * on wire-time `pairing:repair` events should wire this to the same sink.\n * Only fires on the `_data` restore path; fresh sessions never heal.\n */\n onPairingDrop?: (drop: PairingDrop) => void\n /**\n * @internal: restore from existing data (bypasses id/agentId/metadata\n * options). The session ALIASES this object for its lifetime — every\n * mutation (appendTurns, run records, updatedAt) writes through to it,\n * and the load-time pairing heal may replace its `turns` array in place\n * before the session is returned. Callers must not retain independent\n * ownership expectations.\n */\n _data?: SessionData\n}\n\n// Load-time drop-only healing: restored sessions get structurally invalid\n// tool blocks (duplicate tool_call ids, orphan tool_results) deleted before\n// any consumer sees them. Pure deletions — never synthetic content — so\n// writing the result back to the store is safe (unlike `toMessages`\n// output). Without this, a transcript corrupted once (e.g. a retry that\n// re-appended an already-persisted turn) re-triggers wire-time pairing\n// repairs on every turn forever. `loadSession` persists the healed\n// transcript; direct `createSession({ _data })` callers get the in-memory\n// heal and persist on their next `save()`. The heal replaces `_data.turns`\n// IN PLACE: the session aliases `_data` for its whole lifetime (see the\n// option doc), so an on-the-side copy would make the corrupt path the only\n// one where a caller's retained reference diverges from the live session.\n// `loadSession` detects healing by comparing `session.turns` against its\n// pre-heal reference.\nfunction healRestoredTurns(restoreData: SessionData, onPairingDrop: CreateSessionOptions['onPairingDrop']): void {\n const healed = dropInvalidToolPairings(restoreData.turns, {\n laneForRun: wireViewLaneResolver(restoreData.runs),\n ...(onPairingDrop ? { onDrop: onPairingDrop } : {}),\n })\n if (healed !== restoreData.turns)\n restoreData.turns = healed\n}\n\n/**\n * Apply per-run usage stats onto a SessionRun. Shared by `completeRun`,\n * `abortRun`, and `errorRun` so the on-disk shape stays consistent across\n * exit paths: aborted and errored runs keep the tokens actually consumed\n * instead of reading `0 in / 0 out` on reload.\n */\nfunction applyRunStats(run: SessionRun, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n run.turns = stats.turns\n run.tokensIn = stats.tokensIn\n run.tokensOut = stats.tokensOut\n if (stats.turnUsage) {\n run.turnUsage = stats.turnUsage\n const total = stats.turnUsage.reduce((acc, t) => ({\n input: acc.input + t.input,\n output: acc.output + t.output,\n cacheCreation: (acc.cacheCreation ?? 0) + (t.cacheCreation ?? 0),\n cacheRead: (acc.cacheRead ?? 0) + (t.cacheRead ?? 0),\n thinking: (acc.thinking ?? 0) + (t.thinking ?? 0),\n }), { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, thinking: 0 })\n run.totalUsage = {\n input: total.input,\n output: total.output,\n ...(total.cacheCreation ? { cacheCreation: total.cacheCreation } : {}),\n ...(total.cacheRead ? { cacheRead: total.cacheRead } : {}),\n ...(total.thinking ? { thinking: total.thinking } : {}),\n }\n }\n if (stats.cost !== undefined)\n run.cost = stats.cost\n}\n\n/**\n * Each metadata key's JSON form, as stores persist it. A value JSON drops\n * (`undefined`, a function) is left out, so the key counts as absent: its\n * removal is sent once, not on every save. `null` for a value JSON can't\n * serialize (a cycle, a BigInt), which then always counts as changed.\n */\ntype MetaFingerprints = Map<string, string | null>\n\nfunction metaFingerprints(metadata: Record<string, unknown>): MetaFingerprints {\n const fingerprints: MetaFingerprints = new Map()\n for (const [key, value] of Object.entries(metadata)) {\n let json: string | undefined | null\n try {\n json = JSON.stringify(value)\n }\n catch {\n // A cycle or a BigInt: unknowable, so the key is written every save.\n json = null\n }\n if (json !== undefined)\n fingerprints.set(key, json)\n }\n return fingerprints\n}\n\n/** The keys whose fingerprint differs between `before` and `after`, deleted keys included. */\nfunction metaKeysChangedSince(before: MetaFingerprints, after: MetaFingerprints): string[] {\n const keys: string[] = []\n for (const [key, json] of after) {\n if (json === null || json !== before.get(key))\n keys.push(key)\n }\n for (const key of before.keys()) {\n if (!after.has(key))\n keys.push(key)\n }\n return keys\n}\n\n/**\n * Create a new session.\n * Async so stores that generate IDs server-side (e.g. a database) can be supported.\n * @param options - Session identity, metadata and store. See {@link CreateSessionOptions}.\n * @example\n * ```ts\n * import { createMemoryStore, createSession } from 'zidane/session'\n *\n * const session = await createSession({ agentId: 'reviewer', store: createMemoryStore() })\n * console.log(session.id, session.turns.length)\n * ```\n */\nexport async function createSession(options: CreateSessionOptions = {}): Promise<Session> {\n const store = options.store\n const now = Date.now()\n\n let sessionId = options.id\n if (!sessionId && store?.generateSessionId) {\n sessionId = await store.generateSessionId()\n }\n if (!sessionId) {\n sessionId = generateId()\n }\n\n /*\n * Whether the in-memory turn/run lists diverge from what the store holds\n * by an explicit REPLACEMENT (`setTurns` / `setRuns` — not an append,\n * which `appendTurns` persists incrementally). Only then may `save()`\n * bulk-overwrite; otherwise it writes the header only, so a stale wrapper\n * (a TUI window in daemon mode, a reader that loaded a snapshot, an\n * extension writing `ctx.session`) can never wipe turns another process\n * appended since. The load-time heal below deliberately does NOT arm it:\n * `loadSession` persists a heal through `store.save` itself, only when its\n * concurrent-writer check passes, so a skipped or failed heal persist\n * leaves the session in metadata-only mode.\n */\n let turnsReplaced = false\n if (options._data)\n healRestoredTurns(options._data, options.onPairingDrop)\n\n const data: SessionData = options._data ?? {\n id: sessionId,\n agentId: options.agentId,\n // Stamp the project tag at creation only — restored sessions (the\n // `_data` branch above) keep whatever was already persisted, even\n // if `options.projectRoot` differs. A session's project identity\n // is sticky for its lifetime; loading the same session from a\n // different cwd doesn't re-home it.\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n turns: [],\n runs: [],\n status: 'idle',\n metadata: options.metadata ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n /**\n * Metadata keys set through `setMeta` since the last save. A header save\n * writes only these and the keys changed against `savedMeta`, so a stale\n * copy (the daemon's live agent while a client renames) can't write back\n * keys it never changed.\n */\n const changedMetaKeys = new Set<string>()\n /**\n * The metadata as of the last load or save, so a key written straight into\n * `session.metadata` (or changed inside its value) is saved too. Empty for\n * a new session: its initial metadata counts as changed.\n */\n let savedMeta: MetaFingerprints = options._data ? metaFingerprints(data.metadata) : new Map()\n\n function touch() {\n data.updatedAt = Date.now()\n }\n\n function findRun(runId: string): SessionRun | undefined {\n return data.runs.find(r => r.id === runId)\n }\n\n const session: Session = {\n ...(store ? { store } : {}),\n get id() { return data.id },\n get agentId() { return data.agentId },\n get projectRoot() { return data.projectRoot },\n get turns() { return data.turns },\n get isEmpty() { return data.turns.length === 0 },\n get status() { return data.status },\n get runs() { return data.runs },\n get metadata() { return data.metadata },\n get createdAt() { return data.createdAt },\n get updatedAt() { return data.updatedAt },\n\n startRun(runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number, resumedFromRunId?: string, label?: string, parentCallId?: string }) {\n data.runs.push({\n id: runId,\n startedAt: Date.now(),\n prompt: prompt ?? '',\n status: 'running',\n ...(extras?.parentRunId ? { parentRunId: extras.parentRunId } : {}),\n ...(typeof extras?.depth === 'number' ? { depth: extras.depth } : {}),\n ...(extras?.resumedFromRunId ? { resumedFromRunId: extras.resumedFromRunId } : {}),\n ...(extras?.label ? { label: extras.label } : {}),\n ...(extras?.parentCallId ? { parentCallId: extras.parentCallId } : {}),\n })\n touch()\n },\n\n completeRun(runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'completed'\n run.endedAt = Date.now()\n applyRunStats(run, stats)\n }\n touch()\n },\n\n abortRun(runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'aborted'\n run.endedAt = Date.now()\n // Backfill tokens when available so an aborted run's session\n // ledger reflects actual consumption rather than `0 in / 0 out`.\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n errorRun(runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }, detail?: RunErrorDetail) {\n const run = findRun(runId)\n if (run) {\n run.status = 'error'\n run.endedAt = Date.now()\n run.error = error\n if (detail)\n run.errorDetail = detail\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n async appendTurns(turns: SessionTurn[]) {\n data.turns.push(...turns)\n touch()\n if (store) {\n await store.appendTurns(data.id, turns)\n }\n },\n\n setTurns(turns: SessionTurn[]) {\n data.turns = turns\n turnsReplaced = true\n touch()\n },\n\n setRuns(runs: SessionRun[]) {\n data.runs = runs\n turnsReplaced = true\n touch()\n },\n\n async updateStatus(status: SessionData['status']) {\n data.status = status\n touch()\n if (store) {\n await store.updateStatus(data.id, status)\n }\n },\n\n async updateRun(run: SessionRun) {\n if (store) {\n await store.updateRun(data.id, run)\n }\n },\n\n generateTurnId() {\n if (store?.generateTurnId) {\n return store.generateTurnId()\n }\n return crypto.randomUUID()\n },\n\n setMeta(key: string, value: unknown, options?: { touch?: boolean }) {\n data.metadata[key] = value\n changedMetaKeys.add(key)\n if (options?.touch !== false)\n touch()\n },\n\n async addArtifact(input, origin) {\n if (!store?.upsertArtifact)\n return undefined\n const at = Date.now()\n const projectRoot = input.projectRoot ?? data.projectRoot\n const stored = await store.upsertArtifact({\n id: crypto.randomUUID(),\n sessionId: data.id,\n ...(origin?.runId ? { runId: origin.runId } : {}),\n ...(origin?.turnId ? { turnId: origin.turnId } : {}),\n kind: input.kind,\n uri: input.uri,\n label: input.label,\n ...(projectRoot ? { projectRoot } : {}),\n ...(input.meta !== undefined ? { meta: input.meta } : {}),\n createdAt: at,\n updatedAt: at,\n })\n return stored ?? undefined\n },\n\n async listArtifacts(filter) {\n if (!store?.listArtifacts)\n return []\n return store.listArtifacts({ ...filter, sessionId: data.id })\n },\n\n setAgentId(agentId: string) {\n data.agentId = agentId\n touch()\n },\n\n async save() {\n if (!store) {\n throw new Error('No SessionStore configured. Pass a store to createSession() to enable persistence.')\n }\n const saving = metaFingerprints(data.metadata)\n if (!turnsReplaced && store.saveMeta) {\n const metaKeys = [...new Set([...changedMetaKeys, ...metaKeysChangedSince(savedMeta, saving)])]\n changedMetaKeys.clear()\n try {\n await store.saveMeta(data, { metaKeys })\n }\n catch (err) {\n for (const key of metaKeys)\n changedMetaKeys.add(key)\n throw err\n }\n savedMeta = saving\n return\n }\n await store.save(data)\n turnsReplaced = false\n changedMetaKeys.clear()\n savedMeta = saving\n },\n\n toMessages(options?: ToWireMessagesOptions) {\n return toWireMessages(data.turns, options)\n },\n\n toJSON() {\n return structuredClone(data)\n },\n }\n\n return session\n}\n\n/**\n * Demote a persisted `'running'` status to `'idle'` (in place) and return the\n * data. A persisted `'running'` is necessarily stale: the run that set it lived\n * in another process (crash, kill -9, power loss) and cannot still be executing\n * in a freshly-loaded session. Call this on the result of a raw `store.load()`\n * so consumers don't render a ghost in-progress state or refuse a new run.\n * `loadSession` applies it automatically; direct `store.load()` callers (e.g.\n * the TUI) should run loaded data through this.\n *\n * Run RECORDS get the same treatment: the agent persists each record at\n * run start with `status: 'running'` (crash durability — see the\n * `SessionStore.updateRun` contract), so a freshly-loaded session may\n * carry `'running'` runs that died with their process. They demote to\n * `'aborted'` so replay renders a truthful \"aborted\" state instead of a\n * perpetually-in-flight run.\n * @param data - Loaded session data. It is modified in place.\n */\nexport function demoteStaleRunningStatus(data: SessionData): SessionData {\n if (data.status === 'running')\n data.status = 'idle'\n for (const run of data.runs) {\n if (run.status === 'running') {\n run.status = 'aborted'\n run.endedAt ??= run.startedAt\n }\n }\n return data\n}\n\n/** Options for {@link loadSession}. */\nexport interface LoadSessionOptions {\n /** Forwarded to `createSession` — see {@link CreateSessionOptions.onPairingDrop}. */\n onPairingDrop?: (drop: PairingDrop) => void\n /**\n * Write a load-time pairing heal back to the store (default true). Pass\n * `false` from READERS — a window mirroring a session another process\n * runs (daemon mode) — because the persist is a bulk turn overwrite and\n * the owner's next load heals and persists it anyway.\n */\n persistHeal?: boolean\n}\n\n/**\n * Load an existing session from a store.\n * @param store - Store to load from.\n * @param sessionId - Id of the session to load.\n * @param options - Load options. See {@link LoadSessionOptions}.\n * @returns The session, or `null` when the store has no session with that id.\n */\nexport async function loadSession(store: SessionStore, sessionId: string, options: LoadSessionOptions = {}): Promise<Session | null> {\n const loaded = await store.load(sessionId)\n if (!loaded)\n return null\n\n /*\n * Persist the header demotion for markers that are already STALE by\n * the cross-process guards' own freshness rule (updatedAt older than\n * 5 minutes — live runs keep it moving via loop persistence and the\n * daemon-mode heartbeat). Without this the on-disk row says 'running'\n * forever after a crash mid-run and status surfaces render a ghost\n * \"running\" state. The staleness bound is what keeps this safe for\n * READERS: loading a session another process is actively running must\n * never clobber the live marker. Targeted single-column write,\n * best-effort — the in-memory demotion is what correctness needs.\n * `preserveUpdatedAt`: this is a heal on a pure READ — bumping\n * `updatedAt` would reorder recency-sorted session lists just for\n * opening an old crashed session.\n */\n const staleRunning = loaded.status === 'running' && Date.now() - loaded.updatedAt > 5 * 60 * 1000\n demoteStaleRunningStatus(loaded)\n if (staleRunning)\n void store.updateStatus(sessionId, loaded.status, { preserveUpdatedAt: true }).catch(() => undefined)\n\n // createSession heals structurally invalid tool pairings on the `_data`\n // branch (drop-only — see dropInvalidToolPairings). When it changed the\n // transcript, persist the healed version so the corruption never re-heals\n // (and never re-fires wire-time repairs) on subsequent loads.\n //\n // Best-effort: the in-memory heal is what correctness needs — persistence\n // is an optimization that a future clean load retries. A save failure\n // (read-only store, transient IO) must not make a loadable session throw\n // at load time. This goes straight to `store.save` (a bulk overwrite of\n // the loaded snapshot, which the session aliases) rather than through\n // `session.save()`: the heal must never arm the session's bulk-write path,\n // or a skipped/failed persist here would turn the next metadata save into\n // the very overwrite the re-count below refused.\n const originalTurns = loaded.turns\n const originalTurnCount = originalTurns.length\n const session = await createSession({\n store,\n _data: loaded,\n ...(options.onPairingDrop ? { onPairingDrop: options.onPairingDrop } : {}),\n })\n if (session.turns !== originalTurns && options.persistHeal !== false) {\n try {\n // Shrink the concurrent-writer window: another process may have\n // appended turns between our load() and now — a bulk save() would\n // silently discard them. Re-count before overwriting; on mismatch,\n // keep the heal in-memory and let a future clean load persist it.\n const current = await store.getTurns(sessionId)\n if (current.length === originalTurnCount)\n await store.save(loaded)\n }\n catch {\n // Healed in memory; a future clean load retries the persist.\n }\n }\n return session\n}\n\n// ---------------------------------------------------------------------------\n// Re-export stores\n// ---------------------------------------------------------------------------\n\nexport type { FileMapAdapter, FileMapStoreOptions } from './file-map'\nexport { createMemoryStore } from './memory'\nexport { autoDetectAndConvert, dropInvalidToolPairings, fromAnthropic, fromOpenAI, toAnthropic, toOpenAI, toWireMessages } from './messages'\nexport type { PairingDrop, PairingDropKind } from './messages'\nexport type { ToWireMessagesOptions } from './messages'\nexport { mergeSessionMetadata } from './metadata-merge'\nexport type { SaveMetaOptions } from './metadata-merge'\nexport { createRemoteStore } from './remote'\nexport type { RemoteStoreOptions } from './remote'\n\n// NOTE: `createSqliteStore` is intentionally NOT re-exported here. It lives behind\n// the dedicated `zidane/session/sqlite` subpath so that non-Bun consumers don't\n// transitively evaluate `bun:sqlite` when they import from `zidane/session`.\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `laneForRun` resolver (see `dropInvalidToolPairings`) from a\n * session's run records, mapping each runId to the wire view its turns reach:\n *\n * - depth-0 runs (and turns with no runId — legacy sessions) all share the\n * MAIN view: the top-level conversation spans every prompt's run, so\n * duplicate tool_call ids across those runs DO collide in one request.\n * - depth>0 runs get their resume-chain ROOT as lane: a resumed subagent\n * replays its ancestors' turns in its own view, but parallel subagents\n * never see each other's.\n * - runIds with no run record (the historical dropped-subagent-run bug) are\n * isolated in their own lane — conservative: same-runId corruption still\n * heals, and an untracked child's counter-style ids can't collide with the\n * main lane's.\n */\nfunction wireViewLaneResolver(runs: readonly SessionRun[]): (runId: string | undefined) => string {\n const MAIN_LANE = '\\0main'\n const byId = new Map(runs.map(r => [r.id, r]))\n const cache = new Map<string, string>()\n return (runId) => {\n if (runId === undefined)\n return MAIN_LANE\n const cached = cache.get(runId)\n if (cached !== undefined)\n return cached\n // Walk the resume chain to its root (cycle-guarded); the root decides.\n let lane = runId\n let cur: string | undefined = runId\n const seen = new Set<string>()\n while (cur !== undefined && !seen.has(cur)) {\n seen.add(cur)\n const rec = byId.get(cur)\n if (!rec)\n break // unknown run: isolate at the deepest known link\n lane = cur\n if ((rec.depth ?? 0) === 0) {\n lane = MAIN_LANE\n break\n }\n cur = rec.resumedFromRunId\n }\n cache.set(runId, lane)\n return lane\n }\n}\n\nfunction generateId(): string {\n return `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction isSafeExtensionStateKey(key: string): boolean {\n return typeof key === 'string' && key.length > 0 && !DANGEROUS_RECORD_KEYS.has(key)\n}\n\nfunction jsonClone<T extends JsonValue>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nfunction assertSafeExtensionStateKey(key: string, label: string): void {\n if (!isSafeExtensionStateKey(key))\n throw new Error(`Invalid extension session state ${label}.`)\n}\n\nfunction cloneExtensionStateRoot(value: unknown): Record<string, Record<string, JsonValue>> {\n return clonePlainRecord(value) as Record<string, Record<string, JsonValue>>\n}\n\nfunction clonePlainRecord(value: unknown): Record<string, JsonValue> {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return {}\n const out: Record<string, JsonValue> = {}\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (!isSafeExtensionStateKey(key))\n continue\n if (isJsonValue(child))\n out[key] = child\n }\n return out\n}\n\nfunction isJsonValue(value: unknown, depth = 0): value is JsonValue {\n if (value === null)\n return true\n if (typeof value === 'string' || typeof value === 'boolean')\n return true\n if (typeof value === 'number')\n return Number.isFinite(value)\n if (typeof value !== 'object')\n return false\n if (depth >= 8)\n return false\n if (Array.isArray(value))\n return value.every(item => isJsonValue(item, depth + 1))\n return Object.entries(value).every(([key, child]) =>\n isSafeExtensionStateKey(key) && isJsonValue(child, depth + 1),\n )\n}\n\nfunction assertJsonBounded(value: JsonValue, maxBytes: number, label: string): void {\n const json = JSON.stringify(value)\n if (json === undefined)\n throw new Error(`${label} must be JSON serializable.`)\n if (Buffer.byteLength(json) > maxBytes)\n throw new Error(`${label} exceeds ${maxBytes} bytes.`)\n}\n"],"mappings":";;;;;AAmEA,SAAS,OAAO,MAA8B;CAC5C,OAAO;EACL,IAAI,KAAK;EACT,SAAS,KAAK;EACd,aAAa,KAAK;EAClB,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,WAAW,KAAK;CAClB;AACF;AAEA,SAAS,OAAO,MAAiB,OAAmC;CAClE,OAAO;EACL,IAAI,KAAK;EACT,SAAS,KAAK;EACd,GAAI,OAAO,KAAK,gBAAgB,WAAW,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;EAChF;EACA,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,WAAW,KAAK;CAClB;AACF;AAEA,SAAS,gBAAgB,OAA8B;CACrD,IAAI,CAAC,OACH,OAAO,CAAC;CACV,MAAM,QAAuB,CAAC;CAC9B,KAAK,MAAM,QAAQ,MAAM,MAAM,IAAI,GAAG;EACpC,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,SACH;EACF,IAAI;GACF,MAAM,KAAK,KAAK,MAAM,OAAO,CAAgB;EAC/C,QACM,CAEN;CACF;CACA,OAAO;AACT;AAEA,SAAS,oBAAoB,OAA8B;CACzD,IAAI,MAAM,WAAW,GACnB,OAAO;CACT,OAAO,GAAG,MAAM,KAAI,MAAK,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;AACzD;;;;;;;;;;;;;AAcA,SAAgB,mBACd,SACA,UAA+B,CAAC,GAClB;CACd,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,WAAW,QAAQ,YAAY;CAIrC,IAAI,SAA6B;CACjC,IAAI,WAAW;CAIf,IAAI,YAAkC;CAMtC,IAAI,iBAAuC;CAI3C,IAAI,aAA4B;CAEhC,eAAe,UAAyB;EACtC,IAAI,UACF;EACF,IAAI,CAAC,WACH,aAAa,YAAY;GACvB,MAAM,EAAE,UAAU,MAAM,QAAQ,IAAI;GACpC,MAAM,UAAU,MAAM;GACtB,IAAI,SAAS;IACX,IAAI,OAAyB;IAC7B,IAAI;KACF,OAAO,KAAK,MAAM,OAAO;IAC3B,QACM;KACJ,OAAO;IACT;IACA,IAAI,MACF,SAAS,OAAO,MAAM,gBAAgB,MAAM,cAAc,EAAE,CAAC;SAE1D;KACH,iBAAiB,gBAAgB,MAAM,cAAc,EAAE;KACvD,QAAQ,MAAM,qBAAqB,SAAS,8BAA8B,eAAe,OAAO,gBAAgB,UAAU,+DAA+D;IAC3L;GACF;GACA,WAAW;EACb,EAAA,CAAG,CAAC,CAAC,cAAc;GACjB,YAAY;EACd,CAAC;EAEH,OAAO;CACT;CAEA,eAAe,QAAQ,MAAkC;EACvD,MAAM,OAAO,OAAO,IAAI;EACxB,eAAe,oBAAoB,KAAK,KAAK;EAC7C,MAAM,QAAQ,KAAK;IAChB,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC;IACvC,YAAY;EACf,CAAC;CACH;CAIA,eAAe,gBAAgB,WAAqC;EAClE,MAAM,QAAQ;EACd,IAAI,QACF,OAAO,OAAO,OAAO;EAEvB,MAAM,MAAM,KAAK,IAAI;EACrB,SAAS;GACP,IAAI;GAGJ,OAAO,kBAAkB,CAAC;GAC1B,MAAM,CAAC;GACP,QAAQ;GACR,UAAU,CAAC;GACX,WAAW;GACX,WAAW;EACb;EACA,iBAAiB;EACjB,WAAW;EACX,OAAO;CACT;CAEA,OAAO;EACL,MAAM,KAAK,WAAgD;GACzD,MAAM,QAAQ;GACd,IAAI,CAAC,UAAU,OAAO,OAAO,WAC3B,OAAO;GACT,OAAO,gBAAgB,MAAM;EAC/B;EAEA,MAAM,KAAK,MAAkC;GAC3C,SAAS,gBAAgB,IAAI;GAC7B,aAAa;GACb,WAAW;GACX,MAAM,QAAQ,MAAM;EACtB;EAEA,MAAM,SAAS,MAAmB,SAA0C;GAC1E,MAAM,QAAQ;GACd,IAAI,CAAC,UAAU,OAAO,OAAO,KAAK,IAAI;IACpC,SAAS,gBAAgB,IAAI;IAC7B,aAAa;IACb,MAAM,QAAQ,MAAM;IACpB;GACF;GAGA,MAAM,OAAO,OAAO,KAAK,MAAM;GAC/B,KAAK,MAAM,OAAO,KAAK,MAAM;IAC3B,MAAM,MAAM,KAAK,WAAU,MAAK,EAAE,OAAO,IAAI,EAAE;IAC/C,IAAI,OAAO,GACT,KAAK,OAAO,gBAAgB,GAAG;SAE/B,KAAK,KAAK,gBAAgB,GAAG,CAAC;GAClC;GACA,SAAS;IACP,GAAG,gBAAgB;KAAE,GAAG;KAAM,OAAO,CAAC;KAAG,MAAM,CAAC;KAAG,UAAU,qBAAqB,OAAO,UAAU,KAAK,UAAU,SAAS,QAAQ;IAAE,CAAC;IACtI,OAAO,OAAO;IACd;GACF;GACA,MAAM,QAAQ,MAAM;EACtB;EAEA,MAAM,OAAO,WAAkC;GAC7C,MAAM,QAAQ;GACd,IAAI,UAAU,OAAO,OAAO,WAC1B;GACF,SAAS;GACT,aAAa;GACb,MAAM,QAAQ,OAAO;EACvB;EAEA,MAAM,KAAK,QAA2B;GACpC,MAAM,QAAQ;GACd,IAAI,CAAC,QACH,OAAO,CAAC;GACV,IAAI,QAAQ,WAAW,OAAO,YAAY,OAAO,SAC/C,OAAO,CAAC;GAGV,IAAI,UAAU,iBAAiB,QAAQ;IACrC,MAAM,IAAI,OAAO;IACjB,IAAI,MAAM,QAAQ,OAAO,eAAe,MACtC,OAAO,CAAC;IACV,IAAI,OAAO,MAAM,YAAY,OAAO,gBAAgB,GAClD,OAAO,CAAC;GACZ;GACA,OAAO,CAAC,OAAO,EAAE;EACnB;EAEA,MAAM,YAAY,WAAmB,OAAqC;GAExE,IAAI,CAAC,MADY,gBAAgB,SAAS,GAExC;GACF,MAAM,QAAQ,gBAAgB,KAAK;GACnC,OAAQ,MAAM,KAAK,GAAG,KAAK;GAC3B,IAAI,eAAe,MACjB,cAAc,oBAAoB,KAAK;GACzC,OAAQ,YAAY,KAAK,IAAI;GAC7B,MAAM,QAAQ,MAAO;EACvB;EAEA,MAAM,SAAS,WAAmB,OAAO,GAAG,OAAwC;GAClF,MAAM,QAAQ;GACd,IAAI,CAAC,UAAU,OAAO,OAAO,WAC3B,OAAO,CAAC;GACV,MAAM,QAAQ,OAAO,MAAM,MAAM,MAAM,UAAU,KAAA,IAAY,OAAO,QAAQ,KAAA,CAAS;GACrF,OAAO,gBAAgB,KAAK;EAC9B;EAEA,MAAM,UAAU,WAAmB,KAAgC;GAEjE,IAAI,CAAC,MADY,gBAAgB,SAAS,GAExC;GACF,MAAM,MAAM,OAAQ,KAAK,WAAU,MAAK,EAAE,OAAO,IAAI,EAAE;GACvD,IAAI,OAAO,GACT,OAAQ,KAAK,OAAO,gBAAgB,GAAG;QAEvC,OAAQ,KAAK,KAAK,gBAAgB,GAAG,CAAC;GACxC,OAAQ,YAAY,KAAK,IAAI;GAC7B,MAAM,QAAQ,MAAO;EACvB;EAEA,MAAM,aAAa,WAAmB,QAA8C;GAElF,IAAI,CAAC,MADY,gBAAgB,SAAS,GAExC;GACF,OAAQ,SAAS;GACjB,OAAQ,YAAY,KAAK,IAAI;GAC7B,MAAM,QAAQ,MAAO;EACvB;CACF;AACF;;;;;;;;;;;;;ACrTA,SAAgB,oBAAkC;CAChD,MAAM,2BAAW,IAAI,IAAyB;CAG9C,MAAM,4BAAY,IAAI,IAA+B;CAQrD,SAAS,cAAc,WAAgC;EACrD,IAAI,OAAO,SAAS,IAAI,SAAS;EACjC,IAAI,CAAC,MAAM;GACT,MAAM,MAAM,KAAK,IAAI;GACrB,OAAO;IACL,IAAI;IACJ,OAAO,CAAC;IACR,MAAM,CAAC;IACP,QAAQ;IACR,UAAU,CAAC;IACX,WAAW;IACX,WAAW;GACb;GACA,SAAS,IAAI,WAAW,IAAI;EAC9B;EACA,OAAO;CACT;CAEA,OAAO;EACL,MAAM,KAAK,WAAmB;GAC5B,MAAM,OAAO,SAAS,IAAI,SAAS;GACnC,OAAO,OAAO,gBAAgB,IAAI,IAAI;EACxC;EAEA,MAAM,KAAK,SAAsB;GAC/B,SAAS,IAAI,QAAQ,IAAI,gBAAgB,OAAO,CAAC;EACnD;EAEA,MAAM,SAAS,SAAsB,SAA2B;GAC9D,MAAM,WAAW,SAAS,IAAI,QAAQ,EAAE;GACxC,IAAI,CAAC,UAAU;IACb,SAAS,IAAI,QAAQ,IAAI,gBAAgB,OAAO,CAAC;IACjD;GACF;GAEA,MAAM,OAAO,SAAS,KAAK,MAAM;GACjC,KAAK,MAAM,OAAO,QAAQ,MAAM;IAC9B,MAAM,MAAM,KAAK,WAAU,MAAK,EAAE,OAAO,IAAI,EAAE;IAC/C,IAAI,OAAO,GACT,KAAK,OAAO,gBAAgB,GAAG;SAE/B,KAAK,KAAK,gBAAgB,GAAG,CAAC;GAClC;GACA,SAAS,IAAI,QAAQ,IAAI;IACvB,GAAG,gBAAgB;KAAE,GAAG;KAAS,OAAO,CAAC;KAAG,MAAM,CAAC;KAAG,UAAU,qBAAqB,SAAS,UAAU,QAAQ,UAAU,SAAS,QAAQ;IAAE,CAAC;IAC9I,OAAO,SAAS;IAChB;GACF,CAAC;EACH;EAEA,MAAM,OAAO,WAAmB;GAC9B,SAAS,OAAO,SAAS;GACzB,UAAU,OAAO,SAAS;EAC5B;EAEA,MAAM,KAAK,QAAQ;GACjB,IAAI,MAAM,MAAM,KAAK,SAAS,KAAK,CAAC;GACpC,IAAI,QAAQ,SACV,MAAM,IAAI,QAAO,OAAM,SAAS,IAAI,EAAE,CAAC,EAAE,YAAY,OAAO,OAAO;GAMrE,IAAI,UAAU,iBAAiB,QAAQ;IACrC,MAAM,IAAI,OAAO;IACjB,IAAI,MAAM,MACR,MAAM,IAAI,QAAO,OAAM,SAAS,IAAI,EAAE,CAAC,EAAE,eAAe,IAAI;SACzD,IAAI,OAAO,MAAM,UACpB,MAAM,IAAI,QAAO,OAAM,SAAS,IAAI,EAAE,CAAC,EAAE,gBAAgB,CAAC;GAC9D;GACA,IAAI,QAAQ,OACV,MAAM,IAAI,MAAM,GAAG,OAAO,KAAK;GAEjC,OAAO;EACT;EAEA,MAAM,YAAY,WAAmB,OAAsB;GACzD,MAAM,OAAO,cAAc,SAAS;GACpC,KAAK,MAAM,KAAK,GAAG,gBAAgB,KAAK,CAAC;GACzC,KAAK,YAAY,KAAK,IAAI;EAC5B;EAEA,MAAM,cAAc,WAAW,QAAQ;GAErC,MAAM,OAAO,SAAS,IAAI,SAAS;GACnC,IAAI,CAAC,MACH,OAAO;GACT,MAAM,OAAO,gBAAgB;IAAE,OAAO,KAAK;IAAO,MAAM,KAAK;GAAK,CAAC;GACnE,MAAM,OAAO,OAAO,IAAI;GACxB,IAAI,SAAS,MACX,OAAO;GACT,KAAK,QAAQ,gBAAgB,KAAK,KAAK;GACvC,IAAI,KAAK,SAAS,KAAK,MACrB,KAAK,OAAO,gBAAgB,KAAK,IAAI;GACvC,KAAK,YAAY,KAAK,IAAI;GAC1B,OAAO;EACT;EAEA,MAAM,SAAS,WAAmB,OAAO,GAAG,OAAgB;GAC1D,MAAM,OAAO,SAAS,IAAI,SAAS;GACnC,IAAI,CAAC,MACH,OAAO,CAAC;GACV,MAAM,SAAS,KAAK,MAAM,MAAM,MAAM,UAAU,KAAA,IAAY,OAAO,QAAQ,KAAA,CAAS;GACpF,OAAO,gBAAgB,MAAM;EAC/B;EAEA,MAAM,UAAU,WAAmB,KAAiB;GAClD,MAAM,OAAO,SAAS,IAAI,SAAS;GACnC,IAAI,CAAC,MACH;GAMF,MAAM,MAAM,KAAK,KAAK,WAAU,MAAK,EAAE,OAAO,IAAI,EAAE;GACpD,IAAI,OAAO,GACT,KAAK,KAAK,OAAO,gBAAgB,GAAG;QAEpC,KAAK,KAAK,KAAK,gBAAgB,GAAG,CAAC;GACrC,KAAK,YAAY,KAAK,IAAI;EAC5B;EAEA,MAAM,aAAa,WAAmB,QAA+B;GACnE,MAAM,OAAO,SAAS,IAAI,SAAS;GACnC,IAAI,CAAC,MACH;GACF,KAAK,SAAS;GACd,KAAK,YAAY,KAAK,IAAI;EAC5B;EAEA,MAAM,eAAe,UAA2B;GAE9C,IAAI,CAAC,SAAS,IAAI,SAAS,SAAS,GAClC,OAAO;GACT,MAAM,OAAO,UAAU,IAAI,SAAS,SAAS,KAAK,CAAC;GACnD,MAAM,MAAM,KAAK,WAAU,MAAK,EAAE,SAAS,SAAS,QAAQ,EAAE,QAAQ,SAAS,GAAG;GAClF,IAAI;GACJ,IAAI,OAAO,GAAG;IACZ,SAAS,cAAc,KAAK,MAAO,gBAAgB,QAAQ,CAAC;IAC5D,KAAK,OAAO;GACd,OACK;IACH,MAAM,QAAQ,IAAI,IAAI,wBAAwB,IAAI,CAAC;IACnD,MAAM,OAAO,MAAM,OAAO,IAAI,KAAK,QAAO,MAAK,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,IAAI;IACnE,SAAS,gBAAgB,QAAQ;IACjC,KAAK,KAAK,MAAM;IAChB,UAAU,IAAI,SAAS,WAAW,IAAI;IACtC,OAAO,gBAAgB,MAAM;GAC/B;GACA,UAAU,IAAI,SAAS,WAAW,IAAI;GACtC,OAAO,gBAAgB,MAAM;EAC/B;EAEA,MAAM,cAAc,QAAQ;GAC1B,MAAM,SAAS,QAAQ,cAAc,KAAA,IAChC,UAAU,IAAI,OAAO,SAAS,KAAK,CAAC,IACrC,MAAM,KAAK,UAAU,OAAO,CAAC,CAAC,CAAC,KAAK;GACxC,MAAM,UAAU,cAAc,OAAO,QAAO,MAAK,sBAAsB,GAAG,MAAM,CAAC,CAAC;GAClF,MAAM,SAAS,OAAO,QAAQ,UAAU,YAAY,OAAO,QAAQ,IAAI,QAAQ,MAAM,GAAG,OAAO,KAAK,IAAI;GACxG,OAAO,gBAAgB,MAAM;EAC/B;EAEA,MAAM,eAAe,WAAmB,YAAoB;GAC1D,MAAM,OAAO,UAAU,IAAI,SAAS;GACpC,IAAI,CAAC,MACH;GACF,MAAM,OAAO,KAAK,QAAO,MAAK,EAAE,OAAO,UAAU;GACjD,IAAI,KAAK,WAAW,GAClB,UAAU,OAAO,SAAS;QAE1B,UAAU,IAAI,WAAW,IAAI;EACjC;CACF;AACF;;;ACpLA,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;;;;;;;;;;;;;AAc3B,SAAgB,kBAAkB,SAA2C;CAC3E,MAAM,UAAU,QAAQ,IAAI,QAAQ,gBAAgB,EAAE;CACtD,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,iBAAyC;EAC7C,gBAAgB;EAChB,GAAG,QAAQ;CACb;CAEA,eAAe,QAAQ,MAAc,MAAuC;EAC1E,MAAM,MAAM,GAAG,UAAU;EAOzB,OAAO,MALW,MAAM,KAAK;GAC3B,GAAG;GACH,SAAS;IAAE,GAAG;IAAgB,GAAG,MAAM;GAAQ;GAC/C,QAAQ,MAAM,UAAU,YAAY,QAAQ,SAAS;EACvD,CAAC;CAEH;;;;;;CAOA,eAAe,UAAa,KAAe,MAA0B;EACnE,IAAI;GACF,OAAO,MAAM,IAAI,KAAK;EACxB,SACO,OAAO;GACZ,MAAM,IAAI,MACR,uCAAuC,KAAK,IAAI,IAAI,OAAO,GAAG,IAAI,WAAW,IAC7E,EAAE,MAAM,CACV;EACF;CACF;CAEA,OAAO;EACL,MAAM,KAAK,WAAmB;GAC5B,MAAM,MAAM,MAAM,QAAQ,aAAa,mBAAmB,SAAS,GAAG;GACtE,IAAI,CAAC,IAAI,IAAI;IACX,IAAI,IAAI,WAAW,KACjB,OAAO;IACT,MAAM,IAAI,MAAM,+BAA+B,IAAI,OAAO,GAAG,IAAI,YAAY;GAC/E;GACA,OAAO,MAAM,UAAuB,KAAK,aAAa,WAAW;EACnE;EAEA,MAAM,KAAK,SAAsB;GAC/B,MAAM,MAAM,MAAM,QAAQ,aAAa,mBAAmB,QAAQ,EAAE,KAAK;IACvE,QAAQ;IACR,MAAM,KAAK,UAAU,OAAO;GAC9B,CAAC;GACD,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,+BAA+B,IAAI,OAAO,GAAG,IAAI,YAAY;EAEjF;EAEA,MAAM,OAAO,WAAmB;GAC9B,MAAM,MAAM,MAAM,QAAQ,aAAa,mBAAmB,SAAS,KAAK,EACtE,QAAQ,SACV,CAAC;GACD,IAAI,CAAC,IAAI,MAAM,IAAI,WAAW,KAC5B,MAAM,IAAI,MAAM,iCAAiC,IAAI,OAAO,GAAG,IAAI,YAAY;EAEnF;EAEA,MAAM,KAAK,QAAQ;GACjB,MAAM,SAAS,IAAI,gBAAgB;GACnC,IAAI,QAAQ,SACV,OAAO,IAAI,WAAW,OAAO,OAAO;GACtC,IAAI,QAAQ,OACV,OAAO,IAAI,SAAS,OAAO,OAAO,KAAK,CAAC;GAC1C,IAAI,UAAU,iBAAiB,QAAQ;IACrC,MAAM,IAAI,OAAO;IACjB,IAAI,MAAM,MACR,OAAO,IAAI,eAAe,UAAU;SACjC,IAAI,OAAO,MAAM,UACpB,OAAO,IAAI,eAAe,CAAC;GAC/B;GAEA,MAAM,QAAQ,OAAO,SAAS;GAC9B,MAAM,OAAO,QAAQ,aAAa,UAAU;GAC5C,MAAM,MAAM,MAAM,QAAQ,IAAI;GAE9B,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,+BAA+B,IAAI,OAAO,GAAG,IAAI,YAAY;GAG/E,MAAM,OAAO,MAAM,UAA6B,KAAK,IAAI;GACzD,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG,GAC1B,MAAM,IAAI,UAAU,wDAAwD,KAAK,QAAQ,OAAO,MAAM,KAAK;GAE7G,OAAO,KAAK;EACd;EAEA,MAAM,YAAY,WAAmB,OAAsB;GACzD,MAAM,MAAM,MAAM,QAAQ,aAAa,mBAAmB,SAAS,EAAE,SAAS;IAC5E,QAAQ;IACR,MAAM,KAAK,UAAU,KAAK;GAC5B,CAAC;GACD,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,8BAA8B,IAAI,OAAO,GAAG,IAAI,YAAY;EAEhF;EAEA,MAAM,SAAS,WAAmB,OAAO,GAAG,OAAgB;GAC1D,MAAM,SAAS,IAAI,gBAAgB;GACnC,IAAI,MACF,OAAO,IAAI,QAAQ,OAAO,IAAI,CAAC;GACjC,IAAI,UAAU,KAAA,GACZ,OAAO,IAAI,SAAS,OAAO,KAAK,CAAC;GAEnC,MAAM,QAAQ,OAAO,SAAS;GAC9B,MAAM,OAAO,aAAa,mBAAmB,SAAS,EAAE,QAAQ,QAAQ,IAAI,UAAU;GACtF,MAAM,MAAM,MAAM,QAAQ,IAAI;GAE9B,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,2BAA2B,IAAI,OAAO,GAAG,IAAI,YAAY;GAG3E,MAAM,QAAQ,MAAM,UAAyB,KAAK,IAAI;GACtD,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB,MAAM,IAAI,UAAU,gDAAgD,KAAK,QAAQ,OAAO,OAAO;GAEjG,OAAO;EACT;EAEA,MAAM,UAAU,WAAmB,KAAiB;GAClD,MAAM,MAAM,MAAM,QAChB,aAAa,mBAAmB,SAAS,EAAE,QAAQ,mBAAmB,IAAI,EAAE,KAC5E;IACE,QAAQ;IACR,MAAM,KAAK,UAAU,GAAG;GAC1B,CACF;GACA,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,4BAA4B,IAAI,OAAO,GAAG,IAAI,YAAY;EAE9E;EAEA,MAAM,aAAa,WAAmB,QAA+B;GACnE,MAAM,MAAM,MAAM,QAAQ,aAAa,mBAAmB,SAAS,KAAK;IACtE,QAAQ;IACR,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;GACjC,CAAC;GACD,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,+BAA+B,IAAI,OAAO,GAAG,IAAI,YAAY;EAEjF;CACF;AACF;;;ACjKA,MAAM,iCAAiC;AACvC,MAAM,mCAAmC;AACzC,MAAM,oCAAoC;AAC1C,MAAM,wCAAwB,IAAI,IAAI;CAAC;CAAa;CAAa;AAAa,CAAC;AAC/E,MAAM,0BAA0B,IAAI,kBAA2B;;;;;;;;;;AAW/D,SAAgB,qBAAwB,SAA8B,IAAgB;CACpF,IAAI,CAAC,SACH,OAAO,GAAG;CACZ,OAAO,wBAAwB,IAAI,SAAS,EAAE;AAChD;;;;AAKA,SAAgB,0BAA+C;CAC7D,OAAO,wBAAwB,SAAS;AAC1C;;;;;;;;AASA,SAAgB,yBACd,SACA,eACA,KACe;CACf,IAAI,CAAC,WAAW,CAAC,wBAAwB,aAAa,KAAK,CAAC,wBAAwB,GAAG,GACrF,OAAO,KAAA;CACT,MAAM,OAAO,QAAQ,SAAS;CAC9B,IAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GACzD,OAAO,KAAA;CACT,MAAM,cAAe,KAAiC;CACtD,IAAI,CAAC,eAAe,OAAO,gBAAgB,YAAY,MAAM,QAAQ,WAAW,GAC9E,OAAO,KAAA;CACT,MAAM,SAAU,YAAwC;CACxD,IAAI,WAAW,KAAA,GACb,OAAO,KAAA;CAGT,OAAO,UAAU,MAAmB;AACtC;;;;;;;;;;;AAYA,eAAsB,yBACpB,SACA,eACA,KACA,OACe;CACf,IAAI,CAAC,SACH;CACF,4BAA4B,eAAe,eAAe;CAC1D,4BAA4B,KAAK,KAAK;CACtC,kBAAkB,OAAO,kCAAkC,4BAA4B,cAAc,GAAG,IAAI,EAAE;CAE9G,MAAM,OAAO,wBAAwB,QAAQ,SAAS,+BAA+B;CACrF,MAAM,cAAc,iBAAiB,KAAK,cAAc;CAGxD,YAAY,OAAO,UAAU,KAAK;CAClC,KAAK,iBAAiB;CACtB,kBAAkB,MAAmB,mCAAmC,yBAAyB;CACjG,QAAQ,QAAQ,gCAAgC,IAAI;CACpD,IAAI,QAAQ,OACV,MAAM,QAAQ,KAAK;AACvB;;;;;;;;AASA,eAAsB,4BACpB,SACA,eACA,KACe;CACf,IAAI,CAAC,WAAW,CAAC,wBAAwB,aAAa,KAAK,CAAC,wBAAwB,GAAG,GACrF;CACF,MAAM,OAAO,wBAAwB,QAAQ,SAAS,+BAA+B;CACrF,MAAM,cAAc,iBAAiB,KAAK,cAAc;CACxD,IAAI,EAAE,OAAO,cACX;CACF,OAAO,YAAY;CACnB,IAAI,OAAO,KAAK,WAAW,CAAC,CAAC,SAAS,GACpC,KAAK,iBAAiB;MAEtB,OAAO,KAAK;CACd,QAAQ,QAAQ,gCAAgC,IAAI;CACpD,IAAI,QAAQ,OACV,MAAM,QAAQ,KAAK;AACvB;;;;;;;;;;;;AAiBA,SAAgB,kBAAkB,MAA6B,OAA4B;CACzF,MAAM,wBAAQ,IAAI,IAAY;CAC9B,IAAI,KAAyB;CAC7B,OAAO,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG;EAC3B,MAAM,IAAI,EAAE;EACZ,KAAK,KAAK,MAAK,MAAK,EAAE,OAAO,EAAE,CAAC,EAAE;CACpC;CACA,OAAO;AACT;AAkkBA,SAAS,kBAAkB,aAA0B,eAA4D;CAC/G,MAAM,SAAS,wBAAwB,YAAY,OAAO;EACxD,YAAY,qBAAqB,YAAY,IAAI;EACjD,GAAI,gBAAgB,EAAE,QAAQ,cAAc,IAAI,CAAC;CACnD,CAAC;CACD,IAAI,WAAW,YAAY,OACzB,YAAY,QAAQ;AACxB;;;;;;;AAQA,SAAS,cAAc,KAAiB,OAAuG;CAC7I,IAAI,QAAQ,MAAM;CAClB,IAAI,WAAW,MAAM;CACrB,IAAI,YAAY,MAAM;CACtB,IAAI,MAAM,WAAW;EACnB,IAAI,YAAY,MAAM;EACtB,MAAM,QAAQ,MAAM,UAAU,QAAQ,KAAK,OAAO;GAChD,OAAO,IAAI,QAAQ,EAAE;GACrB,QAAQ,IAAI,SAAS,EAAE;GACvB,gBAAgB,IAAI,iBAAiB,MAAM,EAAE,iBAAiB;GAC9D,YAAY,IAAI,aAAa,MAAM,EAAE,aAAa;GAClD,WAAW,IAAI,YAAY,MAAM,EAAE,YAAY;EACjD,IAAI;GAAE,OAAO;GAAG,QAAQ;GAAG,eAAe;GAAG,WAAW;GAAG,UAAU;EAAE,CAAC;EACxE,IAAI,aAAa;GACf,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,GAAI,MAAM,gBAAgB,EAAE,eAAe,MAAM,cAAc,IAAI,CAAC;GACpE,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;GACxD,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;EACvD;CACF;CACA,IAAI,MAAM,SAAS,KAAA,GACjB,IAAI,OAAO,MAAM;AACrB;AAUA,SAAS,iBAAiB,UAAqD;CAC7E,MAAM,+BAAiC,IAAI,IAAI;CAC/C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAAG;EACnD,IAAI;EACJ,IAAI;GACF,OAAO,KAAK,UAAU,KAAK;EAC7B,QACM;GAEJ,OAAO;EACT;EACA,IAAI,SAAS,KAAA,GACX,aAAa,IAAI,KAAK,IAAI;CAC9B;CACA,OAAO;AACT;;AAGA,SAAS,qBAAqB,QAA0B,OAAmC;CACzF,MAAM,OAAiB,CAAC;CACxB,KAAK,MAAM,CAAC,KAAK,SAAS,OACxB,IAAI,SAAS,QAAQ,SAAS,OAAO,IAAI,GAAG,GAC1C,KAAK,KAAK,GAAG;CAEjB,KAAK,MAAM,OAAO,OAAO,KAAK,GAC5B,IAAI,CAAC,MAAM,IAAI,GAAG,GAChB,KAAK,KAAK,GAAG;CAEjB,OAAO;AACT;;;;;;;;;;;;;AAcA,eAAsB,cAAc,UAAgC,CAAC,GAAqB;CACxF,MAAM,QAAQ,QAAQ;CACtB,MAAM,MAAM,KAAK,IAAI;CAErB,IAAI,YAAY,QAAQ;CACxB,IAAI,CAAC,aAAa,OAAO,mBACvB,YAAY,MAAM,MAAM,kBAAkB;CAE5C,IAAI,CAAC,WACH,YAAY,WAAW;CAezB,IAAI,gBAAgB;CACpB,IAAI,QAAQ,OACV,kBAAkB,QAAQ,OAAO,QAAQ,aAAa;CAExD,MAAM,OAAoB,QAAQ,SAAS;EACzC,IAAI;EACJ,SAAS,QAAQ;EAMjB,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;EAClE,OAAO,CAAC;EACR,MAAM,CAAC;EACP,QAAQ;EACR,UAAU,QAAQ,YAAY,CAAC;EAC/B,WAAW;EACX,WAAW;CACb;;;;;;;CAQA,MAAM,kCAAkB,IAAI,IAAY;;;;;;CAMxC,IAAI,YAA8B,QAAQ,QAAQ,iBAAiB,KAAK,QAAQ,oBAAI,IAAI,IAAI;CAE5F,SAAS,QAAQ;EACf,KAAK,YAAY,KAAK,IAAI;CAC5B;CAEA,SAAS,QAAQ,OAAuC;EACtD,OAAO,KAAK,KAAK,MAAK,MAAK,EAAE,OAAO,KAAK;CAC3C;CAqLA,OAAO;EAlLL,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;EACzB,IAAI,KAAK;GAAE,OAAO,KAAK;EAAG;EAC1B,IAAI,UAAU;GAAE,OAAO,KAAK;EAAQ;EACpC,IAAI,cAAc;GAAE,OAAO,KAAK;EAAY;EAC5C,IAAI,QAAQ;GAAE,OAAO,KAAK;EAAM;EAChC,IAAI,UAAU;GAAE,OAAO,KAAK,MAAM,WAAW;EAAE;EAC/C,IAAI,SAAS;GAAE,OAAO,KAAK;EAAO;EAClC,IAAI,OAAO;GAAE,OAAO,KAAK;EAAK;EAC9B,IAAI,WAAW;GAAE,OAAO,KAAK;EAAS;EACtC,IAAI,YAAY;GAAE,OAAO,KAAK;EAAU;EACxC,IAAI,YAAY;GAAE,OAAO,KAAK;EAAU;EAExC,SAAS,OAAe,QAAiB,QAAqH;GAC5J,KAAK,KAAK,KAAK;IACb,IAAI;IACJ,WAAW,KAAK,IAAI;IACpB,QAAQ,UAAU;IAClB,QAAQ;IACR,GAAI,QAAQ,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;IACjE,GAAI,OAAO,QAAQ,UAAU,WAAW,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;IACnE,GAAI,QAAQ,mBAAmB,EAAE,kBAAkB,OAAO,iBAAiB,IAAI,CAAC;IAChF,GAAI,QAAQ,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;IAC/C,GAAI,QAAQ,eAAe,EAAE,cAAc,OAAO,aAAa,IAAI,CAAC;GACtE,CAAC;GACD,MAAM;EACR;EAEA,YAAY,OAAe,OAAuG;GAChI,MAAM,MAAM,QAAQ,KAAK;GACzB,IAAI,KAAK;IACP,IAAI,SAAS;IACb,IAAI,UAAU,KAAK,IAAI;IACvB,cAAc,KAAK,KAAK;GAC1B;GACA,MAAM;EACR;EAEA,SAAS,OAAe,OAAwG;GAC9H,MAAM,MAAM,QAAQ,KAAK;GACzB,IAAI,KAAK;IACP,IAAI,SAAS;IACb,IAAI,UAAU,KAAK,IAAI;IAGvB,IAAI,OACF,cAAc,KAAK,KAAK;GAC5B;GACA,MAAM;EACR;EAEA,SAAS,OAAe,OAAe,OAAwG,QAAyB;GACtK,MAAM,MAAM,QAAQ,KAAK;GACzB,IAAI,KAAK;IACP,IAAI,SAAS;IACb,IAAI,UAAU,KAAK,IAAI;IACvB,IAAI,QAAQ;IACZ,IAAI,QACF,IAAI,cAAc;IACpB,IAAI,OACF,cAAc,KAAK,KAAK;GAC5B;GACA,MAAM;EACR;EAEA,MAAM,YAAY,OAAsB;GACtC,KAAK,MAAM,KAAK,GAAG,KAAK;GACxB,MAAM;GACN,IAAI,OACF,MAAM,MAAM,YAAY,KAAK,IAAI,KAAK;EAE1C;EAEA,SAAS,OAAsB;GAC7B,KAAK,QAAQ;GACb,gBAAgB;GAChB,MAAM;EACR;EAEA,QAAQ,MAAoB;GAC1B,KAAK,OAAO;GACZ,gBAAgB;GAChB,MAAM;EACR;EAEA,MAAM,aAAa,QAA+B;GAChD,KAAK,SAAS;GACd,MAAM;GACN,IAAI,OACF,MAAM,MAAM,aAAa,KAAK,IAAI,MAAM;EAE5C;EAEA,MAAM,UAAU,KAAiB;GAC/B,IAAI,OACF,MAAM,MAAM,UAAU,KAAK,IAAI,GAAG;EAEtC;EAEA,iBAAiB;GACf,IAAI,OAAO,gBACT,OAAO,MAAM,eAAe;GAE9B,OAAO,OAAO,WAAW;EAC3B;EAEA,QAAQ,KAAa,OAAgB,SAA+B;GAClE,KAAK,SAAS,OAAO;GACrB,gBAAgB,IAAI,GAAG;GACvB,IAAI,SAAS,UAAU,OACrB,MAAM;EACV;EAEA,MAAM,YAAY,OAAO,QAAQ;GAC/B,IAAI,CAAC,OAAO,gBACV,OAAO,KAAA;GACT,MAAM,KAAK,KAAK,IAAI;GACpB,MAAM,cAAc,MAAM,eAAe,KAAK;GAc9C,OAAO,MAbc,MAAM,eAAe;IACxC,IAAI,OAAO,WAAW;IACtB,WAAW,KAAK;IAChB,GAAI,QAAQ,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;IAC/C,GAAI,QAAQ,SAAS,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;IAClD,MAAM,MAAM;IACZ,KAAK,MAAM;IACX,OAAO,MAAM;IACb,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;IACrC,GAAI,MAAM,SAAS,KAAA,IAAY,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;IACvD,WAAW;IACX,WAAW;GACb,CAAC,KACgB,KAAA;EACnB;EAEA,MAAM,cAAc,QAAQ;GAC1B,IAAI,CAAC,OAAO,eACV,OAAO,CAAC;GACV,OAAO,MAAM,cAAc;IAAE,GAAG;IAAQ,WAAW,KAAK;GAAG,CAAC;EAC9D;EAEA,WAAW,SAAiB;GAC1B,KAAK,UAAU;GACf,MAAM;EACR;EAEA,MAAM,OAAO;GACX,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,oFAAoF;GAEtG,MAAM,SAAS,iBAAiB,KAAK,QAAQ;GAC7C,IAAI,CAAC,iBAAiB,MAAM,UAAU;IACpC,MAAM,WAAW,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,qBAAqB,WAAW,MAAM,CAAC,CAAC,CAAC;IAC9F,gBAAgB,MAAM;IACtB,IAAI;KACF,MAAM,MAAM,SAAS,MAAM,EAAE,SAAS,CAAC;IACzC,SACO,KAAK;KACV,KAAK,MAAM,OAAO,UAChB,gBAAgB,IAAI,GAAG;KACzB,MAAM;IACR;IACA,YAAY;IACZ;GACF;GACA,MAAM,MAAM,KAAK,IAAI;GACrB,gBAAgB;GAChB,gBAAgB,MAAM;GACtB,YAAY;EACd;EAEA,WAAW,SAAiC;GAC1C,OAAO,eAAe,KAAK,OAAO,OAAO;EAC3C;EAEA,SAAS;GACP,OAAO,gBAAgB,IAAI;EAC7B;CAGW;AACf;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,yBAAyB,MAAgC;CACvE,IAAI,KAAK,WAAW,WAClB,KAAK,SAAS;CAChB,KAAK,MAAM,OAAO,KAAK,MACrB,IAAI,IAAI,WAAW,WAAW;EAC5B,IAAI,SAAS;EACb,IAAI,YAAY,IAAI;CACtB;CAEF,OAAO;AACT;;;;;;;;AAsBA,eAAsB,YAAY,OAAqB,WAAmB,UAA8B,CAAC,GAA4B;CACnI,MAAM,SAAS,MAAM,MAAM,KAAK,SAAS;CACzC,IAAI,CAAC,QACH,OAAO;CAgBT,MAAM,eAAe,OAAO,WAAW,aAAa,KAAK,IAAI,IAAI,OAAO,YAAY;CACpF,yBAAyB,MAAM;CAC/B,IAAI,cACF,MAAW,aAAa,WAAW,OAAO,QAAQ,EAAE,mBAAmB,KAAK,CAAC,CAAC,CAAC,YAAY,KAAA,CAAS;CAetG,MAAM,gBAAgB,OAAO;CAC7B,MAAM,oBAAoB,cAAc;CACxC,MAAM,UAAU,MAAM,cAAc;EAClC;EACA,OAAO;EACP,GAAI,QAAQ,gBAAgB,EAAE,eAAe,QAAQ,cAAc,IAAI,CAAC;CAC1E,CAAC;CACD,IAAI,QAAQ,UAAU,iBAAiB,QAAQ,gBAAgB,OAC7D,IAAI;EAMF,KAAI,MADkB,MAAM,SAAS,SAAS,EAAA,CAClC,WAAW,mBACrB,MAAM,MAAM,KAAK,MAAM;CAC3B,QACM,CAEN;CAEF,OAAO;AACT;;;;;;;;;;;;;;;;AAuCA,SAAS,qBAAqB,MAAoE;CAChG,MAAM,YAAY;CAClB,MAAM,OAAO,IAAI,IAAI,KAAK,KAAI,MAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAC7C,MAAM,wBAAQ,IAAI,IAAoB;CACtC,QAAQ,UAAU;EAChB,IAAI,UAAU,KAAA,GACZ,OAAO;EACT,MAAM,SAAS,MAAM,IAAI,KAAK;EAC9B,IAAI,WAAW,KAAA,GACb,OAAO;EAET,IAAI,OAAO;EACX,IAAI,MAA0B;EAC9B,MAAM,uBAAO,IAAI,IAAY;EAC7B,OAAO,QAAQ,KAAA,KAAa,CAAC,KAAK,IAAI,GAAG,GAAG;GAC1C,KAAK,IAAI,GAAG;GACZ,MAAM,MAAM,KAAK,IAAI,GAAG;GACxB,IAAI,CAAC,KACH;GACF,OAAO;GACP,KAAK,IAAI,SAAS,OAAO,GAAG;IAC1B,OAAO;IACP;GACF;GACA,MAAM,IAAI;EACZ;EACA,MAAM,IAAI,OAAO,IAAI;EACrB,OAAO;CACT;AACF;AAEA,SAAS,aAAqB;CAC5B,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;AAChF;AAEA,SAAS,wBAAwB,KAAsB;CACrD,OAAO,OAAO,QAAQ,YAAY,IAAI,SAAS,KAAK,CAAC,sBAAsB,IAAI,GAAG;AACpF;AAEA,SAAS,UAA+B,OAAa;CACnD,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;AAEA,SAAS,4BAA4B,KAAa,OAAqB;CACrE,IAAI,CAAC,wBAAwB,GAAG,GAC9B,MAAM,IAAI,MAAM,mCAAmC,MAAM,EAAE;AAC/D;AAEA,SAAS,wBAAwB,OAA2D;CAC1F,OAAO,iBAAiB,KAAK;AAC/B;AAEA,SAAS,iBAAiB,OAA2C;CACnE,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAC5D,OAAO,CAAC;CACV,MAAM,MAAiC,CAAC;CACxC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAgC,GAAG;EAC3E,IAAI,CAAC,wBAAwB,GAAG,GAC9B;EACF,IAAI,YAAY,KAAK,GACnB,IAAI,OAAO;CACf;CACA,OAAO;AACT;AAEA,SAAS,YAAY,OAAgB,QAAQ,GAAuB;CAClE,IAAI,UAAU,MACZ,OAAO;CACT,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAChD,OAAO;CACT,IAAI,OAAO,UAAU,UACnB,OAAO,OAAO,SAAS,KAAK;CAC9B,IAAI,OAAO,UAAU,UACnB,OAAO;CACT,IAAI,SAAS,GACX,OAAO;CACT,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,OAAM,SAAQ,YAAY,MAAM,QAAQ,CAAC,CAAC;CACzD,OAAO,OAAO,QAAQ,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,WACxC,wBAAwB,GAAG,KAAK,YAAY,OAAO,QAAQ,CAAC,CAC9D;AACF;AAEA,SAAS,kBAAkB,OAAkB,UAAkB,OAAqB;CAClF,MAAM,OAAO,KAAK,UAAU,KAAK;CACjC,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,GAAG,MAAM,4BAA4B;CACvD,IAAI,OAAO,WAAW,IAAI,IAAI,UAC5B,MAAM,IAAI,MAAM,GAAG,MAAM,WAAW,SAAS,QAAQ;AACzD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spaces-CyjMh5Zf.js","names":[],"sources":["../src/chat/model-name.ts","../src/chat/run-receipt.ts","../src/chat/orchestration.ts","../src/chat/model-setting.ts","../src/chat/spaces.ts"],"sourcesContent":["/**\n * The name a person uses for a model, for pills, pickers and lists.\n *\n * - First-party providers drop their own brand: under Anthropic, \"Claude\n * Opus 4.6\" is \"Opus 4.6\"; under OpenAI, \"OpenAI GPT-5\" is \"GPT-5\".\n * - Aggregators prefix the vendor (\"Anthropic: Claude Opus 4.6\"): the name\n * is what follows, the vendor comes back separately for a caller that\n * shows it (a meta, a tooltip).\n * - \"(latest)\" is dropped: next to a snapshot it reads as a recency claim.\n * - Without a catalog name, the id is made readable: `claude-opus-4-7-20250722`\n * → \"Opus 4.7\" under Anthropic.\n */\n\n/** Input of {@link modelDisplayName}. */\nexport interface ModelNameInput {\n /** Provider key. Inferred as `anthropic` for ids starting with `claude` when absent. */\n providerKey?: string | null\n /** Model id as the provider names it. */\n modelId: string\n /** The catalog's display name, when known. */\n modelName?: string | null\n}\n\n/** Display name of a model, with its vendor when the name carried one. */\nexport interface ModelDisplayName {\n /** Name to show. */\n name: string\n /** The model's maker, when the name carried it (aggregators: \"Anthropic\"). */\n vendor?: string\n}\n\n/** Brand words a first-party provider's names start with. */\nconst OWN_BRAND: Record<string, RegExp> = {\n anthropic: /^claude[\\s-]+/i,\n openai: /^openai[\\s:-]+/i,\n}\n\nconst ACRONYMS = new Set(['gpt', 'glm', 'oss', 'ai', 'r1', 'vl'])\n\n/** `claude-opus-4-7-20250722` → `Claude Opus 4.7`; `gpt-5.5` → `GPT 5.5`. */\nfunction readableId(id: string): string {\n const last = id.split('/').pop() ?? id\n const parts = last\n .replace(/-\\d{8}$/, '')\n .split(/[-_]/)\n .filter(Boolean)\n const out: string[] = []\n for (const part of parts) {\n const prev = out[out.length - 1]\n // Consecutive number parts are a version: 4-7 → 4.7.\n if (/^\\d+$/.test(part) && prev && /^\\d+(?:\\.\\d+)*$/.test(prev)) {\n out[out.length - 1] = `${prev}.${part}`\n continue\n }\n out.push(ACRONYMS.has(part.toLowerCase()) ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1))\n }\n return out.join(' ')\n}\n\n/**\n * Builds the picker name for a model: the catalog name when given, else the id made readable, with a vendor prefix split off and the provider's own brand word removed.\n *\n * @param input - Provider key, model id and optional catalog name.\n */\nexport function modelDisplayName({ providerKey, modelId, modelName }: ModelNameInput): ModelDisplayName {\n let name = (modelName && modelName !== modelId ? modelName : readableId(modelId)).trim()\n let vendor: string | undefined\n\n name = name.replace(/\\s*\\(latest\\)\\s*$/i, '')\n\n const prefixed = /^([^:]{2,40}):\\s+(\\S.*)$/.exec(name)\n if (prefixed) {\n vendor = prefixed[1]!.trim()\n name = prefixed[2]!.trim()\n }\n\n // A bare first-party id (`claude-opus-4-7`) names its maker even when the\n // caller has no provider at hand (a receipt, a usage row).\n const key = providerKey ?? (/^claude[-_]/i.test(modelId) ? 'anthropic' : undefined)\n const brand = key ? OWN_BRAND[key] : undefined\n if (brand && brand.test(name) && name.replace(brand, '').trim())\n name = name.replace(brand, '').trim()\n\n return vendor ? { name, vendor } : { name }\n}\n","/**\n * The per-run receipt — the dimmed one-liner under every finished run, one\n * badge per cell:\n *\n * [claude-sonnet-4] [2m14s] [↻3] [$0.04] [↑4.2k ↓812]\n *\n * One definition for three consumers — live in the TUI, live in the GUI, and\n * replay after reload — so a run never describes itself differently depending\n * on where you read it. Cells are omitted rather than zeroed when unknown: a\n * `$0.00` or `0 tok` cell is a claim, and an untrue one (most providers do not\n * report cost at all).\n *\n * Read off the persisted `SessionRun` record, which the loop already totals\n * (`turns`, `totalUsage`, `cost`, `endedAt`) — exact, and the same numbers the\n * orchestration event carries for the same run.\n *\n * Node-free; re-exported from `zidane/chat/pure` for the renderer.\n */\nimport type { SessionRun } from '../session/index'\nimport { fmtCost } from './format'\nimport { modelDisplayName } from './model-name'\n\n/** What a finished run cost and how long it took, as shown on its receipt row. */\nexport interface RunReceipt {\n /** Model that produced the LAST turn — the one the answer came from. */\n modelId?: string\n /** The provider that served that turn, so its name reads as the model picker's. */\n providerKey?: string\n /** Distinct models beyond the last one (a fallback chain that engaged). */\n otherModels?: number\n /** Wall-clock duration in milliseconds. */\n durationMs?: number\n /** Number of turns. */\n turns?: number\n /** Total cost in USD. */\n costUsd?: number\n /** Input tokens, cached tokens included. */\n tokensIn?: number\n /** Output tokens. */\n tokensOut?: number\n /**\n * Output tokens per second of GENERATION — output over (stream duration −\n * time to first token), summed across the run's turns. Excludes the wait\n * for the first byte and everything outside the provider stream (tool\n * execution, approvals), so it measures the model, not the run.\n */\n tokensPerSecond?: number\n}\n\n/** Leads the turn count: one cycle = one model round trip. */\nexport const RUN_RECEIPT_TURN_GLYPH = '↻'\n\n/**\n * `1.2k` / `48k` / `1.3M` — token counts are read at a glance, not counted.\n *\n * @param tokens - Token count.\n */\nexport function formatTokenCount(tokens: number): string {\n if (tokens >= 1_000_000)\n return `${(tokens / 1_000_000).toFixed(1)}M`\n if (tokens >= 1_000)\n return `${(tokens / 1_000).toFixed(tokens >= 10_000 ? 0 : 1)}k`\n return String(tokens)\n}\n\n/**\n * `2m18s` / `48s` / `<1s` / `1h05m`. Compact — this is a receipt, not a profiler.\n *\n * @param ms - Duration in milliseconds.\n */\nexport function formatRunDuration(ms: number): string {\n if (ms < 1000)\n return '<1s'\n const s = Math.round(ms / 1000)\n if (s < 60)\n return `${s}s`\n const m = Math.floor(s / 60)\n const rest = s % 60\n if (m < 60)\n return rest ? `${m}m${String(rest).padStart(2, '0')}s` : `${m}m`\n const h = Math.floor(m / 60)\n return `${h}h${String(m % 60).padStart(2, '0')}m`\n}\n\n/** `$0.04`, or four decimals under a cent so a cheap run is not shown as `$0.00`. Same as `fmtCost`. */\nexport const formatCostUsd = fmtCost\n\n/**\n * Build the receipt for a run record. `now` stands in for `endedAt` when the\n * record is not finalized yet (`run:end` is broadcast a beat before it is).\n *\n * @param run - The run record.\n * @param now - Epoch milliseconds used as the end time of a run still in progress.\n */\nexport function runReceiptFromRun(run: SessionRun, now = Date.now()): RunReceipt {\n /*\n * \"in\" = everything sent to the model, cache INCLUDED. Anthropic reports\n * cached prefix tokens in `cacheRead` / `cacheCreation`, not `input`, so raw\n * `input` alone under-reports by ~99% on a warm session (the usage panel\n * applies the same correction). Without this a run read `58 in / 21k out`.\n */\n const sumIn = (u: { input: number, cacheRead?: number, cacheCreation?: number }): number =>\n u.input + (u.cacheRead ?? 0) + (u.cacheCreation ?? 0)\n const usage = run.totalUsage\n ? { input: sumIn(run.totalUsage), output: run.totalUsage.output }\n : run.turnUsage?.reduce((acc, u) => ({ input: acc.input + sumIn(u), output: acc.output + u.output }), { input: 0, output: 0 })\n const models = (run.turnUsage ?? []).map(u => u.modelId).filter((m): m is string => typeof m === 'string' && m.length > 0)\n const modelId = models.at(-1)\n const providerKey = run.turnUsage?.findLast(u => u.modelId === modelId)?.provider\n const otherModels = modelId ? new Set(models).size - 1 : 0\n const durationMs = (run.endedAt ?? now) - run.startedAt\n const cost = run.cost ?? run.turnUsage?.reduce((n, u) => n + (u.cost ?? 0), 0) ?? 0\n const turns = run.turns ?? run.turnUsage?.length ?? 0\n /*\n * Generation speed needs per-turn stream timing, which only turns recorded\n * after `streamDurationMs` shipped carry — older runs simply omit the cell.\n * Per turn, generation time is the stream minus the first-byte wait; a turn\n * with no TTFT (tool-only) uses its whole stream, the closest honest proxy.\n */\n let genMs = 0\n let genOut = 0\n for (const u of run.turnUsage ?? []) {\n if (u.streamDurationMs === undefined || u.output <= 0)\n continue\n const ms = u.streamDurationMs - (u.timeToFirstTokenMs ?? 0)\n if (ms <= 0)\n continue\n genMs += ms\n genOut += u.output\n }\n const tokensPerSecond = genMs > 0 && genOut > 0 ? (genOut * 1000) / genMs : undefined\n return {\n ...(modelId ? { modelId } : {}),\n ...(modelId && providerKey ? { providerKey } : {}),\n ...(otherModels > 0 ? { otherModels } : {}),\n ...(durationMs > 0 ? { durationMs } : {}),\n ...(turns > 0 ? { turns } : {}),\n ...(cost > 0 ? { costUsd: cost } : {}),\n ...(usage && usage.input > 0 ? { tokensIn: usage.input } : {}),\n ...(usage && usage.output > 0 ? { tokensOut: usage.output } : {}),\n ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),\n }\n}\n\n/**\n * `42 tk/s`; one decimal under 10 so a slow model is not shown as `0 tk/s`.\n *\n * @param tps - Tokens per second.\n */\nexport function formatTokensPerSecond(tps: number): string {\n return `${tps < 10 ? tps.toFixed(1) : Math.round(tps).toString()} tk/s`\n}\n\n/** What a cell IS, so renderers can tint by meaning rather than by position. */\nexport type RunReceiptCellKind = 'model' | 'duration' | 'turns' | 'cost' | 'tokens' | 'speed'\n/** Sub-parts of a cell that want their own tint — the two token arrows. */\nexport type RunReceiptSegmentKind = 'tokens-in' | 'tokens-out'\n\n/** One part of a `tokens` cell, so renderers can color input and output differently. */\nexport interface RunReceiptSegment {\n /** Whether the segment is input or output tokens. */\n kind: RunReceiptSegmentKind\n /** Segment text. */\n text: string\n}\n\n/** One cell of a formatted receipt line. */\nexport interface RunReceiptCell {\n /** What the cell shows. */\n kind: RunReceiptCellKind\n /** Plain rendering — what `formatRunReceiptLine` and width fitting use. */\n text: string\n /**\n * Optional tinted sub-parts, space-joined; `text` is their plain join. A\n * renderer without per-segment color just paints `text`.\n */\n segments?: readonly RunReceiptSegment[]\n}\n\n/** How a surface names a model: the model picker's name for it (`Opus 4.5`). */\nexport type ModelNamer = (modelId: string, providerKey?: string) => string\n\n/** The name from the id alone, for a surface without the provider catalogs. */\nexport const nameFromModelId: ModelNamer = (modelId, providerKey) => modelDisplayName({ providerKey, modelId }).name\n\n/**\n * The receipt's cells, in display order. Renderers show each as a badge, tint\n * it by `kind`, and truncate to their own width; the CONTENT is fixed here. `modelName` names the model as the surface's picker does.\n *\n * @param r - The receipt to format.\n * @param modelName - Maps a model id and provider key to a display name.\n */\nexport function formatRunReceiptCells(r: RunReceipt, modelName: ModelNamer = nameFromModelId): RunReceiptCell[] {\n const cells: RunReceiptCell[] = []\n if (r.modelId) {\n const name = modelName(r.modelId, r.providerKey)\n cells.push({ kind: 'model', text: r.otherModels ? `${name} +${r.otherModels}` : name })\n }\n if (r.durationMs !== undefined)\n cells.push({ kind: 'duration', text: formatRunDuration(r.durationMs) })\n // `↻` reads as \"a cycle\" — one model round trip — so `↻7` is seven turns\n // without spending the word.\n if (r.turns !== undefined)\n cells.push({ kind: 'turns', text: `${RUN_RECEIPT_TURN_GLYPH}${r.turns}` })\n if (r.costUsd !== undefined)\n cells.push({ kind: 'cost', text: fmtCost(r.costUsd) })\n // `↑` = sent to the model, `↓` = received from it. Arrows over words: two\n // glyphs instead of six characters on a line that competes for width, and\n // they read the same in every locale.\n if (r.tokensIn !== undefined || r.tokensOut !== undefined) {\n const segments: RunReceiptSegment[] = [\n { kind: 'tokens-in', text: `↑${formatTokenCount(r.tokensIn ?? 0)}` },\n { kind: 'tokens-out', text: `↓${formatTokenCount(r.tokensOut ?? 0)}` },\n ]\n cells.push({ kind: 'tokens', text: segments.map(seg => seg.text).join(' '), segments })\n }\n if (r.tokensPerSecond !== undefined)\n cells.push({ kind: 'speed', text: formatTokensPerSecond(r.tokensPerSecond) })\n return cells\n}\n\n/** The glyph that leads the line on every surface. */\nexport const RUN_RECEIPT_GLYPH = '≡'\n\n/**\n * Fit cells to `width` columns for a renderer that draws them two blank\n * columns apart (the TUI's badge rows): returns the cells that fit whole. A\n * cut cell is truncated with `…`; anything after it is dropped. Never wraps.\n * `prefix` is the columns the renderer already spent (glyph + space).\n *\n * @param cells - Cells in display order.\n * @param width - Available width in characters.\n * @param prefix - Characters already used before the first cell.\n */\nexport function fitRunReceiptCells(cells: readonly RunReceiptCell[], width: number, prefix = 0): RunReceiptCell[] {\n let used = prefix\n const out: RunReceiptCell[] = []\n for (const cell of cells) {\n const gap = out.length ? 2 : 0\n const need = gap + cell.text.length\n if (used + need <= width) {\n out.push(cell)\n used += need\n continue\n }\n const room = width - used - gap\n if (room >= 2)\n out.push({ kind: cell.kind, text: `${cell.text.slice(0, room - 1).trimEnd()}…` })\n break\n }\n return out\n}\n\n/**\n * The plain-text receipt for slots that cannot hold badges (the event's\n * `text`, a copy): cells comma-joined, ellipsized to `width` columns. Never\n * wraps.\n *\n * @param r - The receipt to format.\n * @param width - Maximum length. Longer lines end in an ellipsis.\n * @param modelName - Maps a model id and provider key to a display name.\n */\nexport function formatRunReceiptLine(r: RunReceipt, width = Number.POSITIVE_INFINITY, modelName: ModelNamer = nameFromModelId): string {\n const line = formatRunReceiptCells(r, modelName).map(c => c.text).join(', ')\n if (line.length <= width)\n return line\n if (width <= 1)\n return width === 1 ? '…' : ''\n return `${line.slice(0, width - 1).trimEnd()}…`\n}\n\n/**\n * Anything to show? A run with no record data renders no line at all.\n *\n * @param r - The receipt to test.\n */\nexport function hasRunReceipt(r: RunReceipt): boolean {\n return formatRunReceiptCells(r).length > 0\n}\n","import { escapeXml } from '../xml'\n/**\n * Orchestration — the pure model for a \"main\" session that drives others.\n *\n * A main session is an ordinary session with a role, and a worker is an\n * ordinary session with a parent pointer. Both are `metadata` keys, so every\n * existing surface (sidebar, history, attach, search, replay) works on them\n * with no special cases; only the surfaces that WANT to treat them specially\n * (pinning, the event row, the jump shortcut) read these keys.\n *\n * This is deliberately not `spawn`. A spawned child is ephemeral, inline, and\n * dies with the parent's run; a worker is a persistent, resumable session with\n * its own cwd, model chain, todos and history, visible in every window, and it\n * outlives the main's run. The parent pointer is what makes them a tree.\n *\n * Node-free: re-exported from `zidane/chat/pure` for the Electron renderer,\n * which needs `isMainSession` / `orchestratedBy` for pinning and the event row.\n */\nimport { fmtCost } from './format'\nimport { formatRunDuration, formatTokenCount } from './run-receipt'\n\n/** Title the main session is created with. Users can rename it; the ROLE is what matters. */\nexport const MAIN_SESSION_TITLE = 'Main'\n\n/** `session.metadata.role` — present and `'main'` on the one orchestrating session. */\nexport const SESSION_ROLE_KEY = 'role'\n/** `session.metadata.orchestratedBy` — the main session's id, on a worker. */\nexport const ORCHESTRATED_BY_KEY = 'orchestratedBy'\n/**\n * What happened in a worker. `needs-attention` is a worker blocked on an\n * approval or interaction that no window has answered — the main can steer,\n * but a human still has to decide.\n */\nexport type SessionEventStatus = 'done' | 'error' | 'cancelled' | 'needs-attention'\n\n/** A report from a worker session to the orchestrating main session. */\nexport interface SessionEvent {\n /** The worker the event is about. Click target in both clients. */\n sessionId: string\n /** Worker title at event time — titles change, the event should not. */\n title: string\n /** What happened in the worker. */\n status: SessionEventStatus\n /** One-line human summary; also what the model reads. */\n summary: string\n /** Wall-clock at the event. */\n at: number\n /** Run cost in USD when the provider reported one. Omitted, never `0`. */\n costUsd?: number\n /** Worker cwd — lets the main reason about which project reported. */\n cwd?: string\n /** Wall-clock of the run that produced this event, when it has ended. */\n durationMs?: number\n /** Input + output tokens across the run's assistant turns. Omitted, never `0`. */\n tokens?: number\n /** Turns the run produced. Omitted, never `0`. */\n turns?: number\n /** Created by the main (vs. a session the human started elsewhere). */\n worker?: boolean\n /** For `error`: the failure text, separate from `summary` so the UI can style it. */\n error?: string\n /**\n * For `needs-attention`: WHAT is being asked — the approval's tool, the\n * plan's title, the question's prompt. The main can often answer from this\n * alone instead of reading the worker's transcript.\n */\n detail?: string\n /**\n * Monotonic per-main sequence. Lets a consumer dedup or detect a gap when\n * the same event can arrive twice (live signal, then replay) or a window\n * reattaches.\n */\n seq?: number\n /**\n * The prompt that started the run — \"what was asked\". Paired with\n * `summary` (\"what it said\") this is what turns telemetry into a decision:\n * a 692-token essay is fine if that was the goal, and noise if it wasn't.\n */\n goal?: string\n /**\n * Abnormal terminal state beyond `status`: the run ended because the model\n * hit a ceiling (`length`), spun on empty pauses (`pause`), or was refused\n * (`content-filter`). Omitted for a normal `stop`.\n */\n finishReason?: 'length' | 'pause' | 'content-filter'\n /** What the run touched — the receipts a decision needs. */\n artifacts?: RunArtifacts\n}\n\n/** What a run touched, as built by {@link artifactsFromTurns}. */\nexport interface RunArtifacts {\n /** Files edited or written, deduplicated, in first-touch order; capped at 12. */\n changed: readonly string[]\n /** Shell commands run. */\n commands: number\n /** Tool calls in total. */\n toolCalls: number\n}\n\n/**\n * Type guard over untrusted metadata.\n *\n * @param metadata - Session metadata to inspect.\n */\nexport function isMainSession(metadata: Readonly<Record<string, unknown>> | undefined): boolean {\n return metadata?.[SESSION_ROLE_KEY] === 'main'\n}\n\n/**\n * The main session id a worker reports to, or `null` for an ordinary session.\n *\n * @param metadata - Session metadata to inspect.\n */\nexport function orchestratedBy(metadata: Readonly<Record<string, unknown>> | undefined): string | null {\n const value = metadata?.[ORCHESTRATED_BY_KEY]\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * `session.metadata.sideOf` — the parent's id, on a `/side` session (role\n * `'side'`): hidden from lists and search, deleted with its parent.\n */\nexport const SIDE_OF_KEY = 'sideOf'\n\n/**\n * The parent a side session answers about, or `null` for any other session.\n *\n * @param metadata - Session metadata to inspect.\n */\nexport function sideSessionParent(metadata: Readonly<Record<string, unknown>> | undefined): string | null {\n const value = metadata?.[SIDE_OF_KEY]\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * A `/side` session: it lives in its parent's panel, so session lists\n * (the sidebar, `sessions.list`, the Main's `sessions list`) leave it out.\n *\n * @param metadata - Session metadata to inspect.\n */\nexport function isSideSession(metadata: Readonly<Record<string, unknown>> | undefined): boolean {\n return sideSessionParent(metadata) !== null\n}\n\n/**\n * A session the user doesn't open themselves: a `/side` session or an\n * orchestrator's worker. Its `run:end` carries `background: true`.\n *\n * @param metadata - Session metadata to inspect.\n */\nexport function isBackgroundSession(metadata: Readonly<Record<string, unknown>> | undefined): boolean {\n return isSideSession(metadata) || orchestratedBy(metadata) !== null\n}\n\n/** Tools whose `path` input means \"this file changed\". */\nconst CHANGE_TOOLS = new Set(['edit', 'multi_edit', 'write_file'])\nconst ARTIFACT_PATH_CAP = 12\n\n/**\n * What a run touched, read off its assistant turns' tool calls. Only the\n * tools that MUTATE count as \"changed\" — reads are not artifacts. Capped so a\n * refactor across a hundred files does not turn the event into a manifest;\n * the count still tells the truth.\n */\nexport function artifactsFromTurns(turns: readonly { role: string, content: readonly { type: string, name?: string, input?: Record<string, unknown> }[] }[]): RunArtifacts | undefined {\n const changed: string[] = []\n const seen = new Set<string>()\n let commands = 0\n let toolCalls = 0\n for (const turn of turns) {\n if (turn.role !== 'assistant')\n continue\n for (const block of turn.content) {\n if (block.type !== 'tool_call' || !block.name)\n continue\n toolCalls++\n if (block.name === 'shell')\n commands++\n if (CHANGE_TOOLS.has(block.name)) {\n const path = block.input?.path\n if (typeof path === 'string' && path && !seen.has(path)) {\n seen.add(path)\n changed.push(path)\n }\n }\n }\n }\n if (toolCalls === 0)\n return undefined\n return { changed: changed.slice(0, ARTIFACT_PATH_CAP), commands, toolCalls }\n}\n\nconst STATUSES: readonly SessionEventStatus[] = ['done', 'error', 'cancelled', 'needs-attention']\n\n/**\n * Wire format between the harness and the model — and, because it is persisted\n * inside the user turn's text block, between a live session and its replay.\n *\n * Follows `<task-notification>` exactly: child-tag XML, one block per event,\n * escaped with `escapeXml`. The model already knows this shape as \"the harness\n * telling me something\", which is the point — a main that mistook a worker\n * report for a human instruction would act on it as one. `eventsFromTurns`\n * detects the block on replay and re-synthesizes the structured row instead of\n * painting the raw XML as a user prompt.\n *\n * @param event - The event to serialize.\n */\nexport function renderSessionEventXml(event: SessionEvent): string {\n const tag = (name: string, value: string | number | undefined): string[] =>\n value === undefined ? [] : [` <${name}>${escapeXml(String(value))}</${name}>`]\n return [\n '<session-event>',\n ...tag('session-id', event.sessionId),\n ...tag('title', event.title),\n ...tag('status', event.status),\n ...tag('at', event.at),\n ...tag('cwd', event.cwd ?? ''),\n ...tag('cost-usd', event.costUsd),\n ...tag('duration-ms', event.durationMs),\n ...tag('tokens', event.tokens),\n ...tag('turns', event.turns),\n ...tag('worker', event.worker === undefined ? undefined : String(event.worker)),\n ...tag('seq', event.seq),\n ...tag('finish-reason', event.finishReason),\n ...tag('error', event.error),\n ...tag('detail', event.detail),\n ...tag('goal', event.goal),\n ...(event.artifacts\n ? [\n ` <artifacts tool-calls=\"${event.artifacts.toolCalls}\" commands=\"${event.artifacts.commands}\">`,\n ...event.artifacts.changed.map(p => ` <changed>${escapeXml(p)}</changed>`),\n ' </artifacts>',\n ]\n : []),\n ...tag('summary', event.summary),\n '</session-event>',\n ].join('\\n')\n}\n\nconst SESSION_EVENT_RE = /^\\s*<session-event>([\\s\\S]*?)<\\/session-event>\\s*$/\nconst FIELD = {\n sessionId: /<session-id>([\\s\\S]*?)<\\/session-id>/,\n title: /<title>([\\s\\S]*?)<\\/title>/,\n status: /<status>([\\s\\S]*?)<\\/status>/,\n at: /<at>([\\s\\S]*?)<\\/at>/,\n cwd: /<cwd>([\\s\\S]*?)<\\/cwd>/,\n costUsd: /<cost-usd>([\\s\\S]*?)<\\/cost-usd>/,\n durationMs: /<duration-ms>([\\s\\S]*?)<\\/duration-ms>/,\n tokens: /<tokens>([\\s\\S]*?)<\\/tokens>/,\n turns: /<turns>([\\s\\S]*?)<\\/turns>/,\n worker: /<worker>([\\s\\S]*?)<\\/worker>/,\n error: /<error>([\\s\\S]*?)<\\/error>/,\n detail: /<detail>([\\s\\S]*?)<\\/detail>/,\n seq: /<seq>([\\s\\S]*?)<\\/seq>/,\n goal: /<goal>([\\s\\S]*?)<\\/goal>/,\n finishReason: /<finish-reason>([\\s\\S]*?)<\\/finish-reason>/,\n artifacts: /<artifacts tool-calls=\"(\\d+)\" commands=\"(\\d+)\">([\\s\\S]*?)<\\/artifacts>/,\n summary: /<summary>([\\s\\S]*?)<\\/summary>/,\n} as const\nconst CHANGED_RE = /<changed>([\\s\\S]*?)<\\/changed>/g\nconst FINISH_REASONS = new Set(['length', 'pause', 'content-filter'])\n\n/**\n * Inverse of {@link renderSessionEventXml}. Untrusted input — it comes off disk\n * and outlives this module's versions — so it validates each field and returns\n * `null` for anything malformed, in which case the turn replays as the plain\n * user text it structurally is.\n *\n * @param text - User-turn text that may hold a `<session-event>` block.\n */\nexport function parseSessionEventXml(text: string): SessionEvent | null {\n const m = text.match(SESSION_EVENT_RE)\n if (!m)\n return null\n const body = m[1]\n const pick = (re: RegExp): string | undefined => {\n const inner = body.match(re)\n return inner ? unescapeXml(inner[1].trim()) : undefined\n }\n const sessionId = pick(FIELD.sessionId)\n const title = pick(FIELD.title)\n const status = pick(FIELD.status)\n const at = Number(pick(FIELD.at))\n const summary = pick(FIELD.summary)\n if (!sessionId || title === undefined || summary === undefined)\n return null\n if (!status || !(STATUSES as readonly string[]).includes(status))\n return null\n if (!Number.isFinite(at))\n return null\n const cost = Number(pick(FIELD.costUsd))\n const cwd = pick(FIELD.cwd)\n const positive = (raw: string | undefined): number | undefined => {\n const n = Number(raw)\n return Number.isFinite(n) && n > 0 ? n : undefined\n }\n const durationMs = positive(pick(FIELD.durationMs))\n const tokens = positive(pick(FIELD.tokens))\n const turns = positive(pick(FIELD.turns))\n const worker = pick(FIELD.worker)\n const error = pick(FIELD.error)\n const detail = pick(FIELD.detail)\n const seq = positive(pick(FIELD.seq))\n const goal = pick(FIELD.goal)\n const finishReason = pick(FIELD.finishReason)\n const art = body.match(FIELD.artifacts)\n const artifacts: RunArtifacts | undefined = art\n ? {\n toolCalls: Number(art[1]),\n commands: Number(art[2]),\n changed: [...art[3].matchAll(CHANGED_RE)].map(m => unescapeXml(m[1].trim())),\n }\n : undefined\n return {\n sessionId,\n title,\n status: status as SessionEventStatus,\n summary,\n at,\n ...(Number.isFinite(cost) && cost > 0 ? { costUsd: cost } : {}),\n ...(cwd !== undefined ? { cwd } : {}),\n ...(durationMs !== undefined ? { durationMs } : {}),\n ...(tokens !== undefined ? { tokens } : {}),\n ...(turns !== undefined ? { turns } : {}),\n ...(worker === 'true' ? { worker: true } : worker === 'false' ? { worker: false } : {}),\n ...(error ? { error } : {}),\n ...(detail ? { detail } : {}),\n ...(seq !== undefined ? { seq } : {}),\n ...(goal ? { goal } : {}),\n ...(finishReason && FINISH_REASONS.has(finishReason) ? { finishReason: finishReason as SessionEvent['finishReason'] } : {}),\n ...(artifacts ? { artifacts } : {}),\n }\n}\n\nfunction unescapeXml(s: string): string {\n return s\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, '\\'')\n .replace(/&/g, '&')\n}\n\n/**\n * The short label a transcript row shows: `api-refactor finished`. Status\n * words are chosen to read as a verb phrase after the title.\n *\n * @param event - The event to label.\n */\nexport function sessionEventLabel(event: SessionEvent): string {\n const word: Record<SessionEventStatus, string> = {\n 'done': 'finished',\n 'error': 'failed',\n 'cancelled': 'was cancelled',\n 'needs-attention': 'needs attention',\n }\n return `${event.title} ${word[event.status]}`\n}\n\n// ---------------------------------------------------------------------------\n// Presentation shared by both clients — one definition of the card's content\n// so a TUI card and a GUI card describe the same event with the same words.\n// ---------------------------------------------------------------------------\n\n/** Semantic tone a renderer maps onto its theme. */\nexport type SessionEventTone = 'accent' | 'error' | 'dim' | 'warn'\n\n/** Glyph, tone and verb used to render one {@link SessionEventStatus}. */\nexport interface SessionEventStatusStyle {\n /** Glyph for the icon column. */\n glyph: string\n /** Theme tone for the row. */\n tone: SessionEventTone\n /** Verb phrase after the title: \"finished\", \"needs your input\", … */\n verb: string\n}\n\n/**\n * Status → how it reads. `needs-attention` is deliberately the loudest: it is\n * the one status where the human, not the main, has to act.\n */\nexport const SESSION_EVENT_STATUS_STYLE: Readonly<Record<SessionEventStatus, SessionEventStatusStyle>> = {\n 'done': { glyph: '✓', tone: 'accent', verb: 'finished' },\n 'error': { glyph: '✗', tone: 'error', verb: 'failed' },\n 'cancelled': { glyph: '⊘', tone: 'dim', verb: 'was cancelled' },\n 'needs-attention': { glyph: '!', tone: 'warn', verb: 'needs your input' },\n}\n\n/**\n * The directory name a session lives in — the same basename label the sidebar\n * groups by. `undefined` for a directory-less session (the main) or no cwd.\n *\n * @param cwd - The session's working directory.\n */\nexport function sessionDirLabel(cwd: string | undefined): string | undefined {\n if (!cwd)\n return undefined\n const parts = cwd.split(/[\\\\/]+/).filter(Boolean)\n return parts[parts.length - 1]\n}\n\n/**\n * Short, stable id fragment for the header — enough to disambiguate, not to read.\n *\n * @param id - Full session id.\n */\nexport function shortSessionId(id: string): string {\n return id.length <= 8 ? id : id.slice(0, 8)\n}\n\n/**\n * The header's metadata cells, in display order, omitting anything unknown.\n * Renderers join them with their own separator; the CONTENT is fixed here so\n * both surfaces show the same receipt for the same run.\n *\n * The session id is deliberately NOT a cell: it is the least legible datum and\n * exists only to disambiguate. Renderers append `shortSessionId` LAST and\n * dimmer than the rest.\n *\n * @param event - The event to summarize.\n */\nexport function sessionEventMeta(event: SessionEvent): string[] {\n const cells: string[] = []\n // Directory first: it is identity, not a receipt number — two sessions can\n // share a title across projects, and the sidebar disambiguates the same way.\n const dir = sessionDirLabel(event.cwd)\n if (dir)\n cells.push(dir)\n if (event.durationMs !== undefined)\n cells.push(formatRunDuration(event.durationMs))\n if (event.tokens !== undefined)\n cells.push(`${formatTokenCount(event.tokens)} tok`)\n if (event.turns !== undefined)\n cells.push(`${event.turns} turn${event.turns === 1 ? '' : 's'}`)\n if (event.costUsd !== undefined)\n cells.push(fmtCost(event.costUsd))\n return cells\n}\n\n/**\n * The dimmed second line: the last thing the session said, collapsed to one\n * paragraph and capped. Renderers may clamp further (line-clamp in the GUI,\n * width in the TUI); this is the upper bound and the shared ellipsis rule.\n *\n * @param event - The event to excerpt.\n * @param max - Maximum length in characters, counting the ellipsis.\n */\nexport function sessionEventExcerpt(event: SessionEvent, max = 240): string {\n /*\n * What the second line is FOR depends on the status. A blocked session's\n * most useful line is the question being asked; a failed one's is the error.\n * Only a finished session's is \"what it last said\".\n */\n const source = event.status === 'needs-attention' && event.detail\n ? event.detail\n : event.status === 'error' && event.error\n ? event.error\n : event.summary\n const flat = excerptPlainText(source)\n if (flat.length <= max)\n return flat\n return `${flat.slice(0, max - 1).trimEnd()}…`\n}\n\n/**\n * A worker's report is markdown; an excerpt is one line of inline text. Block\n * syntax (headings, bullets, quotes, tables, fences) and emphasis markers are\n * dropped so `**Changes** - **`x`**` reads as `Changes: x`, not as raw source.\n */\nfunction excerptPlainText(markdown: string): string {\n const parts: string[] = []\n for (const raw of markdown.split('\\n')) {\n let line = raw.trim()\n if (!line || /^(?:```|~~~)/.test(line) || /^[-\\s:|][-\\s:]*\\|[-\\s:|]*$/.test(line) || /^(?:-{3,}|\\*{3,}|_{3,})$/.test(line))\n continue\n const heading = /^#{1,6}\\s/.test(line) || /^(?:\\*\\*|__)[^*_]+(?:\\*\\*|__):?$/.test(line)\n line = line\n .replace(/^#{1,6}\\s+/, '')\n .replace(/^>\\s?/, '')\n .replace(/^(?:[-*+]|\\d+[.)])\\s+(?:\\[[ x]\\]\\s+)?/i, '')\n // A table row reads as a sentence: `| gui | pass |` → `gui, pass.`\n if (line.startsWith('|'))\n line = `${line.replace(/^\\||\\|$/g, '').split('|').map(cell => cell.trim()).filter(Boolean).join(', ').replace(/[.:!?]$/, '')}.`\n line = line\n .replace(/(\\*\\*|__)(.+?)\\1/g, '$2')\n .replace(/(^|[^\\w*])\\*(?!\\s)([^*]+)\\*\\B/g, '$1$2')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/`/g, '')\n .trim()\n if (!line)\n continue\n if (heading && !/[.:!?]$/.test(line))\n line += ':'\n parts.push(line)\n }\n return parts.join(' ').replace(/\\s+/g, ' ').trim()\n}\n\n/**\n * Ellipsize a title to `max` characters; titles are user text and can be a paragraph.\n *\n * @param title - Title text. Whitespace runs collapse to one space.\n * @param max - Maximum length in characters, counting the ellipsis.\n */\nexport function truncateTitle(title: string, max = 48): string {\n const flat = title.replace(/\\s+/g, ' ').trim()\n return flat.length <= max ? flat : `${flat.slice(0, max - 1).trimEnd()}…`\n}\n\n/**\n * System-prompt fragment appended to every WORKER's agent. A worker's last\n * message becomes the main's `summary`, so it must be a report, not a sign-off\n * — the main should be able to act on it without reading the transcript.\n */\nexport const WORKER_REPORT_FRAGMENT = `You are a worker session created by an orchestrating main session. Your final message of a run is delivered to it verbatim as your report, so end every run with a short structured report:\n\n## Report\n- **Goal**: what you were asked, in one line\n- **Outcome**: done / partial / blocked — one line, and why if not done\n- **Changed**: files or artifacts touched (paths), or \"none\"\n- **Checks**: what you ran and the result (tests, typecheck, lint), or \"none\"\n- **Open questions**: anything the main or a human must decide, or \"none\"\n- **Next**: the concrete next step you recommend, or \"none\"\n\nKeep it factual and under ~15 lines. This block is what the main decides on; prose before it is fine, prose instead of it is not.`\n\n/**\n * System-prompt fragment appended to the MAIN's agent: how to delegate without\n * wasting calls. The `sessions` tool description says what each verb does;\n * this says which one to reach for, and when not to call at all.\n */\nexport const MAIN_PLAYBOOK_FRAGMENT = `You are the main session: an orchestrator. Work happens in worker sessions; you brief them, integrate their reports, and keep the human informed. Do not do the workers' work here.\n\nWork efficiently with the \\`sessions\\` tool:\n- Never poll. After create or send you WILL receive a <session-event> when the worker finishes, fails, or blocks. Do not read or list a worker that has not reported yet.\n- Reuse before creating. To continue work in a project, send to its existing idle session (listed under Projects below; its context is already warm). Create a worker only for a new, independent unit of work, or when the project has no suitable session.\n- You already know the ids. Take them from Projects below, from a <session-event>, or from an earlier list. Do not list just to find an id you have.\n- send vs steer: send when the worker is idle or the new prompt should queue after its current run; steer only to change course mid-run.\n- Fire-and-forget when you can. For work you will not act on when it lands (docs, cleanup, low-stakes checks), pass wake: false on create or send; the report still reaches your history, you just are not woken for it.\n- Brief completely. A worker prompt states the goal, the constraints, what to check (tests, typecheck, lint), and that it must end with its report. Follow-up questions cost a full round trip.\n- read is cheap (last message); transcript is not. Use transcript only when a report is unclear.\n- Report to the human briefly: what went where, what came back, what needs a decision. Do not re-narrate worker reports.`\n\n/** One known project, for the main's briefing. */\nexport interface ProjectBrief {\n /** Absolute directory. */\n root: string\n /** Non-main sessions rooted here. */\n sessions: number\n /** Most recent change across those sessions, epoch ms. */\n lastActiveAt: number\n /** Sessions currently running or blocked. */\n active: number\n /** Most recently active session here — the natural target for `send`. */\n latest: { sessionId: string, title: string, state: 'idle' | 'running' | 'blocked' }\n}\n\n/**\n * The main's \"Projects\" briefing: which directories have ongoing work and\n * the session to address in each, so the main does not rediscover them with\n * a list call every run. Snapshot at agent start — live state is one `list`\n * away and the text says so.\n */\nexport function renderProjectsBriefing(projects: readonly ProjectBrief[], now: number): string {\n const lines = ['## Projects', 'Known as of this session\\'s start; live state comes from `sessions list`. Directory-less sessions are omitted.']\n if (projects.length === 0) {\n lines.push('- none yet')\n return lines.join('\\n')\n }\n for (const p of projects) {\n const cells = [\n `${p.sessions} session${p.sessions === 1 ? '' : 's'}`,\n `last active ${agoLabel(now - p.lastActiveAt)}`,\n ]\n if (p.active > 0)\n cells.push(`${p.active} active`)\n lines.push(`- ${sessionDirLabel(p.root) ?? p.root} (${p.root}) · ${cells.join(' · ')}`)\n lines.push(` latest: ${p.latest.sessionId} \"${truncateTitle(p.latest.title, 60)}\" (${p.latest.state})`)\n }\n return lines.join('\\n')\n}\n\n/** Coarse relative time for the briefing: `just now`, `12m ago`, `3h ago`, `5d ago`. */\nexport function agoLabel(elapsedMs: number): string {\n const m = Math.floor(Math.max(0, elapsedMs) / 60_000)\n if (m < 1)\n return 'just now'\n if (m < 60)\n return `${m}m ago`\n const h = Math.floor(m / 60)\n if (h < 48)\n return `${h}h ago`\n return `${Math.floor(h / 24)}d ago`\n}\n\nexport { formatRunDuration, formatTokenCount } from './run-receipt'\n","/**\n * Model choices stored as one settings string (`compactionModel`, a space's\n * default in the GUI rows): `\"providerKey/modelId\"`, or\n * `\"providerKey@account/modelId\"` for a pinned account (a credential's stable\n * id). Split on the FIRST slash — model ids may contain slashes, provider\n * keys and credential ids never do. Node-free: shared by the hosts and the\n * GUI renderer (`zidane/chat/pure`).\n */\n\nexport interface ModelSettingPick {\n /** Provider key. */\n providerKey: string\n /** Model id. */\n modelId: string\n /** The stored account of the provider (credential id); absent = its active account. */\n account?: string\n}\n\n/**\n * Parse a model setting; `undefined` for unset or malformed.\n *\n * @param raw - Setting text such as `provider/model` or `provider@account/model`. Returns `undefined` when empty or malformed.\n * @example\n * ```ts\n * import { parseModelSetting } from 'zidane/chat/pure'\n *\n * parseModelSetting('anthropic@work/claude-sonnet-4-5')\n * // { providerKey: 'anthropic', modelId: 'claude-sonnet-4-5', account: 'work' }\n * ```\n */\nexport function parseModelSetting(raw: string | undefined): ModelSettingPick | undefined {\n if (!raw)\n return undefined\n const slash = raw.indexOf('/')\n if (slash <= 0 || slash === raw.length - 1)\n return undefined\n const head = raw.slice(0, slash)\n const modelId = raw.slice(slash + 1)\n const at = head.indexOf('@')\n if (at === -1)\n return { providerKey: head, modelId }\n const providerKey = head.slice(0, at)\n const account = head.slice(at + 1)\n if (!providerKey)\n return undefined\n return account ? { providerKey, modelId, account } : { providerKey, modelId }\n}\n\n/**\n * The settings string for a pick (inverse of {@link parseModelSetting}).\n *\n * @param pick - Provider, model and optional account.\n */\nexport function formatModelSetting(pick: ModelSettingPick): string {\n return `${pick.providerKey}${pick.account ? `@${pick.account}` : ''}/${pick.modelId}`\n}\n","/**\n * Spaces — named groups of sessions (Personal, Work…), each with its own\n * default model. Opt-in (`Settings.spaces`, off by default).\n *\n * A session belongs to ONE space, stamped as `metadata.spaceId`. Nothing is\n * stamped for the Default space, so every session created before the feature\n * (or while it is off, or by a host that predates it) is a Default session\n * with no migration. An id naming a space that no longer exists also reads as\n * Default, which is what deleting a space means for its sessions.\n *\n * While the feature is off every reader treats all sessions as one list: the\n * stored ids are kept but ignored ({@link sessionSpaceFilter} returns null).\n *\n * Node-free and React-free: shared by the TUI, the daemon, the GUI main\n * process and the GUI renderer (`zidane/chat/pure`).\n */\n\nimport type { ThinkingLevel } from '../thinking-levels'\nimport type { PaletteItem } from './palette'\nimport { isThinkingLevel } from '../thinking-levels'\nimport { parseModelSetting } from './model-setting'\n\n/** A space's icon: one emoji, or a small image data-URL (the GUI's directory icon shape). */\nexport type SpaceIcon = { kind: 'emoji', value: string } | { kind: 'image', value: string }\n\n/** A named group of sessions with optional defaults for new ones. */\nexport interface Space {\n /** Stable id. {@link DEFAULT_SPACE_ID} for the Default space. */\n id: string\n /** Display name. */\n name: string\n /** Icon shown in the switcher. */\n icon?: SpaceIcon\n /** Default provider for new sessions in this space; set together with {@link modelId}. */\n providerKey?: string\n /** Default model id for new sessions. Used together with `providerKey`. */\n modelId?: string\n /** The stored account of {@link providerKey} the default runs on (credential id); absent = its active account. */\n account?: string\n /** Fallback chain seeded with the default model (`provider`/`model` pairs). */\n fallbacks?: { providerKey: string, modelId: string }[]\n /** Effort new sessions in this space start with, whatever their model; absent = Settings' (`defaultEffort`). */\n effort?: ThinkingLevel\n /** Model options (`fast`…) new sessions in this space start with, where their model offers them; absent = Settings' (`defaultModelOptions`). */\n modelOptions?: Record<string, boolean>\n /** Agent new sessions in this space start on (`AgentProfile.id`); absent = the global default (`Settings.defaultAgent`). */\n agentId?: string\n}\n\n/** Id of the Default space. */\nexport const DEFAULT_SPACE_ID = 'default'\n/** Name of the Default space. */\nexport const DEFAULT_SPACE_NAME = 'Default'\n/** Session metadata key holding the space id. */\nexport const SPACE_METADATA_KEY = 'spaceId'\n\nconst DEFAULT_SPACE: Space = { id: DEFAULT_SPACE_ID, name: DEFAULT_SPACE_NAME }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction readIcon(value: unknown): SpaceIcon | undefined {\n if (!isRecord(value))\n return undefined\n const { kind, value: v } = value\n return (kind === 'emoji' || kind === 'image') && typeof v === 'string' && v.length > 0 ? { kind, value: v } : undefined\n}\n\n/** A `{ option: on }` record, or `undefined` when it holds none. */\nfunction readModelOptions(value: unknown): Record<string, boolean> | undefined {\n if (!isRecord(value))\n return undefined\n const entries = Object.entries(value).filter((e): e is [string, boolean] => typeof e[1] === 'boolean')\n return entries.length > 0 ? Object.fromEntries(entries) : undefined\n}\n\nfunction readSpace(value: unknown): Space | null {\n if (!isRecord(value) || typeof value.id !== 'string' || value.id.trim() === '')\n return null\n const id = value.id.trim()\n const name = typeof value.name === 'string' && value.name.trim() ? value.name.trim() : (id === DEFAULT_SPACE_ID ? DEFAULT_SPACE_NAME : id)\n const icon = readIcon(value.icon)\n const hasModel = typeof value.providerKey === 'string' && value.providerKey !== '' && typeof value.modelId === 'string' && value.modelId !== ''\n const account = hasModel && typeof value.account === 'string' && value.account !== '' ? value.account : undefined\n const fallbacks = hasModel && Array.isArray(value.fallbacks)\n ? value.fallbacks.flatMap(f => isRecord(f) && typeof f.providerKey === 'string' && typeof f.modelId === 'string' && f.providerKey && f.modelId\n ? [{ providerKey: f.providerKey, modelId: f.modelId }]\n : [])\n : []\n const effort = isThinkingLevel(value.effort) ? value.effort : undefined\n const modelOptions = readModelOptions(value.modelOptions)\n const agentId = typeof value.agentId === 'string' && value.agentId.trim() ? value.agentId.trim() : undefined\n return {\n id,\n name,\n ...(icon ? { icon } : {}),\n ...(hasModel ? { providerKey: value.providerKey as string, modelId: value.modelId as string } : {}),\n ...(account ? { account } : {}),\n ...(fallbacks.length > 0 ? { fallbacks } : {}),\n ...(effort ? { effort } : {}),\n ...(modelOptions ? { modelOptions } : {}),\n ...(agentId ? { agentId } : {}),\n }\n}\n\n/**\n * The stored list made safe to use: malformed entries and duplicate ids\n * dropped, and the Default space always present (first when it was missing).\n * Order is kept: it is the switcher order.\n *\n * @param list - Stored value, usually parsed from settings.\n */\nexport function normalizeSpaces(list: unknown): Space[] {\n const out: Space[] = []\n const seen = new Set<string>()\n for (const entry of Array.isArray(list) ? list : []) {\n const space = readSpace(entry)\n if (!space || seen.has(space.id))\n continue\n seen.add(space.id)\n out.push(space)\n }\n if (!seen.has(DEFAULT_SPACE_ID))\n out.unshift({ ...DEFAULT_SPACE })\n return out\n}\n\n/**\n * The raw space id stamped on a session's metadata, if any.\n *\n * @param metadata - Session metadata.\n */\nexport function readSessionSpaceId(metadata: Readonly<Record<string, unknown>> | undefined): string | undefined {\n const raw = metadata?.[SPACE_METADATA_KEY]\n return typeof raw === 'string' && raw !== '' && raw !== DEFAULT_SPACE_ID ? raw : undefined\n}\n\n/**\n * The space a session is shown in: its stamped id when that space exists,\n * else Default (unstamped, Default itself, or a deleted space).\n *\n * @param spaceId - Id stamped on the session, if any.\n * @param spaces - The space list.\n */\nexport function resolveSessionSpace(spaceId: string | undefined, spaces: readonly Space[]): string {\n return spaceId && spaces.some(s => s.id === spaceId) ? spaceId : DEFAULT_SPACE_ID\n}\n\n/** The parts of the settings the helpers read. */\nexport interface SpacesSettings {\n /** Whether the Spaces feature is on. */\n spaces?: boolean\n /** Stored list of spaces, validated by {@link normalizeSpaces}. */\n spaceList?: unknown\n /** Global default agent profile id. */\n defaultAgent?: string\n /** Global default model, as `provider/model`. */\n defaultModel?: string\n /** Global default reasoning effort. */\n defaultEffort?: string\n /** Global default model options. */\n defaultModelOptions?: unknown\n}\n\n/** The space new sessions in `spaceId` join, while the feature is on. */\nfunction sessionSpace(settings: SpacesSettings | undefined, spaceId: string | undefined): Space | undefined {\n if (!spacesEnabled(settings))\n return undefined\n const spaces = normalizeSpaces(settings?.spaceList)\n return spaces.find(s => s.id === resolveSessionSpace(spaceId, spaces))\n}\n\n/**\n * Feature on? Off by default.\n *\n * @param settings - The settings the helpers read.\n */\nexport function spacesEnabled(settings: SpacesSettings | undefined): boolean {\n return settings?.spaces === true\n}\n\n/**\n * The space to show: the remembered one when it still exists, else the first\n * space in the list (Default unless the user moved it).\n *\n * @param spaces - The space list.\n * @param remembered - Id of the space shown last.\n */\nexport function activeSpaceOf(spaces: readonly Space[], remembered: string | null | undefined): Space {\n return spaces.find(s => s.id === remembered) ?? spaces[0] ?? { ...DEFAULT_SPACE }\n}\n\n/**\n * The predicate a sessions list filters by, or `null` (no filtering) while\n * the feature is off. `alwaysShow` keeps rows that belong to every space\n * (the orchestration main).\n *\n * @param settings - The settings the helpers read.\n * @param activeSpaceId - Id of the space being shown.\n */\nexport function sessionSpaceFilter<T extends { spaceId?: string, isMain?: boolean }>(\n settings: SpacesSettings | undefined,\n activeSpaceId: string,\n): ((row: T) => boolean) | null {\n if (!spacesEnabled(settings))\n return null\n const spaces = normalizeSpaces(settings?.spaceList)\n const active = resolveSessionSpace(activeSpaceId, spaces)\n return row => row.isMain === true || resolveSessionSpace(row.spaceId, spaces) === active\n}\n\n/**\n * The metadata entries stamping a session into `spaceId`: none for Default\n * (an absent id IS Default), so creating in Default writes nothing new.\n *\n * @param spaceId - Space to stamp into.\n */\nexport function spaceMetadata(spaceId: string | undefined): Record<string, string> {\n return spaceId && spaceId !== DEFAULT_SPACE_ID ? { [SPACE_METADATA_KEY]: spaceId } : {}\n}\n\n/** The model a new session in a space starts on. */\nexport interface SpaceModelDefault {\n /** Provider key. */\n providerKey: string\n /** Model id. */\n modelId: string\n /** Stored account of the provider to run on. Absent means its active account. */\n account?: string\n /** Fallback targets after the primary. */\n fallbacks?: { providerKey: string, modelId: string }[]\n /** Effort a new session on this model starts with; absent = the last used for the model. */\n effort?: ThinkingLevel\n /** Model options (`fast`…) it starts with; absent = the last used for the model. */\n modelOptions?: Record<string, boolean>\n}\n\n/**\n * The default model a new session in `spaceId` starts on, or `undefined`\n * (feature off, unknown space, or no default set) — the caller then falls\n * back to its usual last-used model.\n *\n * @param settings - The settings the helpers read.\n * @param spaceId - Space the session is created in.\n */\nexport function spaceModelDefault(settings: SpacesSettings | undefined, spaceId: string | undefined): SpaceModelDefault | undefined {\n const space = sessionSpace(settings, spaceId)\n const model = ownModel(space)\n if (!space || !model)\n return undefined\n return {\n ...model,\n ...(space.effort ? { effort: space.effort } : {}),\n ...(space.modelOptions ? { modelOptions: { ...space.modelOptions } } : {}),\n }\n}\n\n/** A space's own default model, with its account and fallback chain; `undefined` when it sets none. */\nfunction ownModel(space: Space | undefined): Omit<SpaceModelDefault, 'effort' | 'modelOptions'> | undefined {\n if (!space?.providerKey || !space.modelId)\n return undefined\n return {\n providerKey: space.providerKey,\n modelId: space.modelId,\n ...(space.account ? { account: space.account } : {}),\n ...(space.fallbacks && space.fallbacks.length > 0 ? { fallbacks: space.fallbacks.map(f => ({ ...f })) } : {}),\n }\n}\n\n/** The effort and options a new session starts with, as configured. */\nexport interface SessionKnobs {\n /** Reasoning effort; absent = the last used with the session's model. */\n effort?: ThinkingLevel\n /** Model options (`fast`…), on or off, applied where the model offers them; absent = the last used with the model. */\n modelOptions?: Record<string, boolean>\n}\n\n/**\n * The effort and options a new session in `spaceId` starts with, whatever its\n * model: each the space's own (spaces on), else the global one\n * (`Settings.defaultEffort` / `defaultModelOptions`), else absent (the last\n * used with the model). A pick in the composer wins over these.\n *\n * @param settings - The settings the helpers read.\n * @param spaceId - Space the session is created in.\n */\nexport function defaultSessionKnobs(settings: SpacesSettings | undefined, spaceId: string | undefined): SessionKnobs {\n const space = sessionSpace(settings, spaceId)\n const effort = space?.effort ?? (isThinkingLevel(settings?.defaultEffort) ? settings.defaultEffort : undefined)\n const modelOptions = space?.modelOptions ?? readModelOptions(settings?.defaultModelOptions)\n return { ...(effort ? { effort } : {}), ...(modelOptions ? { modelOptions: { ...modelOptions } } : {}) }\n}\n\n/**\n * The model a new session in `spaceId` starts on: the space's default\n * (spaces on), else the global one (`Settings.defaultModel`), else\n * `undefined` (the caller's last used), with the effort and options new\n * sessions start with ({@link defaultSessionKnobs}). A pick in the composer\n * wins over this ({@link newSessionModel}).\n *\n * @param settings - The settings the helpers read.\n * @param spaceId - Space the session is created in.\n */\nexport function defaultSessionModel(settings: SpacesSettings | undefined, spaceId: string | undefined): SpaceModelDefault | undefined {\n const model = ownModel(sessionSpace(settings, spaceId)) ?? parseModelSetting(settings?.defaultModel)\n return model ? { ...model, ...defaultSessionKnobs(settings, spaceId) } : undefined\n}\n\n/**\n * The agent a new session in `spaceId` starts on: the space's (spaces on),\n * else the global one (`Settings.defaultAgent`), else `undefined` (Build). An\n * id the registry doesn't hold is the caller's to resolve (`resolveAgentId`).\n *\n * @param settings - The settings the helpers read.\n * @param spaceId - Space the session is created in.\n */\nexport function defaultSessionAgent(settings: SpacesSettings | undefined, spaceId: string | undefined): string | undefined {\n return sessionSpace(settings, spaceId)?.agentId ?? (settings?.defaultAgent?.trim() || undefined)\n}\n\n/**\n * The model a new session starts on: an explicit pick wins, else the space's\n * default. `fallbacks` is set only when the result IS the space default (a\n * different pick must not inherit the space's chain); `undefined` fallbacks\n * leave the caller's usual seeding (`lastModelChain`) in place.\n *\n * @param pick - The model picked in the composer, if any.\n * @param spaceDefault - The space's default model, if any.\n */\nexport function newSessionModel(\n pick: { providerKey?: string, modelId?: string, account?: string },\n spaceDefault: SpaceModelDefault | undefined,\n): SpaceModelDefault | undefined {\n const explicit = pick.providerKey && pick.modelId\n ? { providerKey: pick.providerKey, modelId: pick.modelId, ...(pick.account ? { account: pick.account } : {}) }\n : undefined\n if (!spaceDefault)\n return explicit\n if (explicit && (explicit.providerKey !== spaceDefault.providerKey || explicit.modelId !== spaceDefault.modelId || (explicit.account ?? undefined) !== (spaceDefault.account ?? undefined)))\n return explicit\n return spaceDefault\n}\n\n/**\n * The space id to stamp on a session created while `activeSpaceId` is shown,\n * or `undefined` (feature off, or Default) — pass it to {@link spaceMetadata}.\n *\n * @param settings - The settings the helpers read.\n * @param activeSpaceId - Id of the space being shown.\n */\nexport function spaceIdForNewSession(settings: SpacesSettings | undefined, activeSpaceId: string | null | undefined): string | undefined {\n if (!spacesEnabled(settings) || !activeSpaceId)\n return undefined\n const id = resolveSessionSpace(activeSpaceId, normalizeSpaces(settings?.spaceList))\n return id === DEFAULT_SPACE_ID ? undefined : id\n}\n\n/**\n * A fresh, unused space id (`space-<n>`).\n *\n * @param spaces - The space list.\n */\nexport function newSpaceId(spaces: readonly Space[]): string {\n const taken = new Set(spaces.map(s => s.id))\n let n = spaces.length\n while (taken.has(`space-${n}`))\n n++\n return `space-${n}`\n}\n\n/**\n * One space looked up by id or name (both case-insensitive), the id match first. `undefined` for a blank query.\n *\n * @param spaces - The space list.\n * @param query - Space name or id.\n */\nexport function findSpace(spaces: readonly Space[], query: string): Space | undefined {\n const q = query.trim().toLowerCase()\n if (!q)\n return undefined\n return spaces.find(s => s.id.toLowerCase() === q) ?? spaces.find(s => s.name.toLowerCase() === q)\n}\n\n/** Prefix of a {@link spaceSwitchItems} entry's id. */\nconst SWITCH_ITEM_PREFIX = 'space:'\n\n/**\n * The command palette's \"Switch to space\" entries: one per space but the\n * shown one. The GUI and the TUI list these same entries, and each runs them\n * through its own switch ({@link spaceOfSwitchItem}).\n *\n * @param spaces - The space list.\n * @param activeSpaceId - Id of the space being shown.\n * @param source - Id of the palette provider listing them.\n */\nexport function spaceSwitchItems(spaces: readonly Space[], activeSpaceId: string, source: string): PaletteItem[] {\n return spaces\n .filter(space => space.id !== activeSpaceId)\n .map(space => ({\n id: `${SWITCH_ITEM_PREFIX}${space.id}`,\n title: `Switch to space: ${space.name}`,\n group: 'Navigate',\n icon: { gui: 'i-ph-squares-four' },\n keywords: ['space', 'workspace', 'profile'],\n source,\n }))\n}\n\n/**\n * The space a {@link spaceSwitchItems} entry switches to; `undefined` for any other id.\n *\n * @param itemId - Palette item id.\n */\nexport function spaceOfSwitchItem(itemId: string): string | undefined {\n return itemId.startsWith(SWITCH_ITEM_PREFIX) ? itemId.slice(SWITCH_ITEM_PREFIX.length) : undefined\n}\n"],"mappings":"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"}
|