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.
Files changed (168) hide show
  1. package/dist/{acp-oYRjlh71.js → acp-BIInKhVI.js} +6 -6
  2. package/dist/{acp-oYRjlh71.js.map → acp-BIInKhVI.js.map} +1 -1
  3. package/dist/acp-cli.js +4 -4
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-CBQglXtH.js → agent-CmhCOmyO.js} +10 -5
  7. package/dist/{agent-CBQglXtH.js.map → agent-CmhCOmyO.js.map} +1 -1
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/chat/pure.d.ts +4 -4
  11. package/dist/chat/pure.js +7 -7
  12. package/dist/chat.d.ts +3 -3
  13. package/dist/chat.js +16 -16
  14. package/dist/{cli-shared-E5m_pI8A.js → cli-shared-GOehTeqk.js} +2 -2
  15. package/dist/{cli-shared-E5m_pI8A.js.map → cli-shared-GOehTeqk.js.map} +1 -1
  16. package/dist/{client-core-BdD20sOv.d.ts → client-core-Dz8hELs4.d.ts} +15 -5
  17. package/dist/client-core-Dz8hELs4.d.ts.map +1 -0
  18. package/dist/codemode-worker.js +1 -1
  19. package/dist/codemode.js +1 -1
  20. package/dist/{connection-loop-DTk82YVl.d.ts → connection-loop-dm_TN-QD.d.ts} +3 -3
  21. package/dist/{connection-loop-DTk82YVl.d.ts.map → connection-loop-dm_TN-QD.d.ts.map} +1 -1
  22. package/dist/contexts/daytona.d.ts +1 -1
  23. package/dist/contexts/e2b.d.ts +1 -1
  24. package/dist/{credentials-B9DfBJP9.js → credentials-DxOQmset.js} +2 -2
  25. package/dist/{credentials-B9DfBJP9.js.map → credentials-DxOQmset.js.map} +1 -1
  26. package/dist/daemon-cli.js +2 -2
  27. package/dist/{dispatcher-BICXFGiJ.js → dispatcher-Dclv6HXn.js} +22 -18
  28. package/dist/dispatcher-Dclv6HXn.js.map +1 -0
  29. package/dist/{engine-QCX3GV-i.js → engine-C_U6kDfA.js} +6 -6
  30. package/dist/{engine-QCX3GV-i.js.map → engine-C_U6kDfA.js.map} +1 -1
  31. package/dist/{eval-CfQlBR81.js → eval-CIHIF_Ns.js} +3 -3
  32. package/dist/{eval-CfQlBR81.js.map → eval-CIHIF_Ns.js.map} +1 -1
  33. package/dist/eval.d.ts +1 -1
  34. package/dist/eval.js +1 -1
  35. package/dist/{executor-B8GoXaaE.js → executor-BFaR2nfc.js} +3 -3
  36. package/dist/{executor-B8GoXaaE.js.map → executor-BFaR2nfc.js.map} +1 -1
  37. package/dist/{extensions-BeH3D-bq.js → extensions-DIbpUj5R.js} +3 -3
  38. package/dist/{extensions-BeH3D-bq.js.map → extensions-DIbpUj5R.js.map} +1 -1
  39. package/dist/extensions.d.ts +1 -1
  40. package/dist/extensions.js +1 -1
  41. package/dist/{fold-rules-DSypi36k.d.ts → fold-rules-CMpWNPqk.d.ts} +2 -2
  42. package/dist/{fold-rules-DSypi36k.d.ts.map → fold-rules-CMpWNPqk.d.ts.map} +1 -1
  43. package/dist/{headless-R4pbpTkm.js → headless-Bs9Lb4vs.js} +4 -4
  44. package/dist/{headless-R4pbpTkm.js.map → headless-Bs9Lb4vs.js.map} +1 -1
  45. package/dist/headless.d.ts +1 -1
  46. package/dist/headless.js +1 -1
  47. package/dist/history-worker.js +1 -1
  48. package/dist/{host-scoped-settings-CCnaBkCV.js → host-scoped-settings-DxxI_yRo.js} +2 -2
  49. package/dist/{host-scoped-settings-CCnaBkCV.js.map → host-scoped-settings-DxxI_yRo.js.map} +1 -1
  50. package/dist/{host-signals-DI9JQvR9.js → host-signals-CQ2jZR-2.js} +5 -5
  51. package/dist/{host-signals-DI9JQvR9.js.map → host-signals-CQ2jZR-2.js.map} +1 -1
  52. package/dist/{index-DyQyDLX1.d.ts → index-4mDRyTGJ.d.ts} +1722 -1691
  53. package/dist/index-4mDRyTGJ.d.ts.map +1 -0
  54. package/dist/{index-lcuvhQfF.d.ts → index-Dlmu8HnG.d.ts} +5 -5
  55. package/dist/{index-lcuvhQfF.d.ts.map → index-Dlmu8HnG.d.ts.map} +1 -1
  56. package/dist/{index-BkNeiJYI.d.ts → index-QsAN9yjZ.d.ts} +3 -3
  57. package/dist/{index-BkNeiJYI.d.ts.map → index-QsAN9yjZ.d.ts.map} +1 -1
  58. package/dist/index.d.ts +3 -3
  59. package/dist/index.js +8 -8
  60. package/dist/ipc/remote.d.ts +5 -5
  61. package/dist/ipc/remote.js +3 -3
  62. package/dist/ipc.d.ts +5 -5
  63. package/dist/ipc.js +9 -9
  64. package/dist/{links-CN5JFFde.js → links-D5itfH8P.js} +10 -10
  65. package/dist/{links-CN5JFFde.js.map → links-D5itfH8P.js.map} +1 -1
  66. package/dist/{mcp-allowlist-BQ5MvhHi.js → mcp-allowlist-BbnTBKNN.js} +15 -4
  67. package/dist/mcp-allowlist-BbnTBKNN.js.map +1 -0
  68. package/dist/mcp.d.ts +1 -1
  69. package/dist/{oauth-TVPPye84.js → oauth-Ck3wQX4Q.js} +133 -43
  70. package/dist/oauth-Ck3wQX4Q.js.map +1 -0
  71. package/dist/output/stream-json.d.ts +1 -1
  72. package/dist/output/terminal.d.ts +1 -1
  73. package/dist/plugins.d.ts +1 -1
  74. package/dist/{prelude-HzY_JEGq.js → prelude-qjPUkw4f.js} +26 -2
  75. package/dist/prelude-qjPUkw4f.js.map +1 -0
  76. package/dist/{presets-DXeHsd24.js → presets-C4hkykN-.js} +2 -2
  77. package/dist/{presets-DXeHsd24.js.map → presets-C4hkykN-.js.map} +1 -1
  78. package/dist/presets.d.ts +1 -1
  79. package/dist/presets.js +1 -1
  80. package/dist/{protocol-BIv1Yuvl.js → protocol-Do_i6Ras.js} +3 -1
  81. package/dist/protocol-Do_i6Ras.js.map +1 -0
  82. package/dist/providers/anthropic.d.ts +1 -1
  83. package/dist/providers/arcee.d.ts +1 -1
  84. package/dist/providers/baseten.d.ts +1 -1
  85. package/dist/providers/cerebras.d.ts +1 -1
  86. package/dist/providers/google.d.ts +1 -1
  87. package/dist/providers/local.d.ts +1 -1
  88. package/dist/providers/neuralwatt.d.ts +1 -1
  89. package/dist/providers/openai-compat.d.ts +1 -1
  90. package/dist/providers/openai.d.ts +1 -1
  91. package/dist/providers/openrouter.d.ts +1 -1
  92. package/dist/providers/xai.d.ts +1 -1
  93. package/dist/providers.d.ts +1 -1
  94. package/dist/{registry-PNi9rsGi.js → registry-DxPP_tAt.js} +3 -3
  95. package/dist/{registry-PNi9rsGi.js.map → registry-DxPP_tAt.js.map} +1 -1
  96. package/dist/{remote-hosts-b6Luwdhu.js → remote-hosts-BkOwRszu.js} +4 -4
  97. package/dist/{remote-hosts-b6Luwdhu.js.map → remote-hosts-BkOwRszu.js.map} +1 -1
  98. package/dist/restate.d.ts +1 -1
  99. package/dist/{secret-shapes-CEoGmfEE.js → secret-shapes-HvaYVzwc.js} +17 -4
  100. package/dist/secret-shapes-HvaYVzwc.js.map +1 -0
  101. package/dist/session/sqlite.d.ts +1 -1
  102. package/dist/{session-7TUHs42_.js → session-CCsKXID6.js} +6 -2
  103. package/dist/session-CCsKXID6.js.map +1 -0
  104. package/dist/{session-factory-CMwIXE-7.js → session-factory-Ct7J9CK1.js} +6 -6
  105. package/dist/{session-factory-CMwIXE-7.js.map → session-factory-Ct7J9CK1.js.map} +1 -1
  106. package/dist/session.d.ts +2 -2
  107. package/dist/session.js +1 -1
  108. package/dist/{settings-keys-DssjmqAV.js → settings-keys-QoGJ5n3e.js} +4 -4
  109. package/dist/{settings-keys-DssjmqAV.js.map → settings-keys-QoGJ5n3e.js.map} +1 -1
  110. package/dist/{shell-path-BUFwqU_A.js → shell-path-BXXtRmEA.js} +4 -4
  111. package/dist/{shell-path-BUFwqU_A.js.map → shell-path-BXXtRmEA.js.map} +1 -1
  112. package/dist/skills.d.ts +1 -1
  113. package/dist/{spaces-CyjMh5Zf.js → spaces-C_x2bvEl.js} +14 -4
  114. package/dist/spaces-C_x2bvEl.js.map +1 -0
  115. package/dist/{store-L01W0gGI.js → store-BpkWIaSH.js} +10 -30
  116. package/dist/store-BpkWIaSH.js.map +1 -0
  117. package/dist/{todo-edits-H77ROt6X.js → todo-edits-DKoxu7Ss.js} +3 -3
  118. package/dist/{todo-edits-H77ROt6X.js.map → todo-edits-DKoxu7Ss.js.map} +1 -1
  119. package/dist/tools/fetch-url.d.ts +1 -1
  120. package/dist/tools/web-search.d.ts +1 -1
  121. package/dist/{tools-pZVFIJ_K.js → tools-FHEIKGkh.js} +2 -2
  122. package/dist/{tools-pZVFIJ_K.js.map → tools-FHEIKGkh.js.map} +1 -1
  123. package/dist/tools.d.ts +1 -1
  124. package/dist/tools.js +2 -2
  125. package/dist/{transcript-anchors-s-yxh0jt.js → transcript-anchors-D8jWLpe5.js} +10 -10
  126. package/dist/{transcript-anchors-s-yxh0jt.js.map → transcript-anchors-D8jWLpe5.js.map} +1 -1
  127. package/dist/{transcript-cache-DXdgVuoy.js → transcript-cache-CdHKtx9y.js} +4 -4
  128. package/dist/{transcript-cache-DXdgVuoy.js.map → transcript-cache-CdHKtx9y.js.map} +1 -1
  129. package/dist/{transcript-reconcile-DeFLIQ14.js → transcript-reconcile-D_SWw6dX.js} +11 -3
  130. package/dist/transcript-reconcile-D_SWw6dX.js.map +1 -0
  131. package/dist/tui.d.ts +7 -7
  132. package/dist/tui.js +24 -23
  133. package/dist/tui.js.map +1 -1
  134. package/dist/{turn-operations-BbGJ-KSt.d.ts → turn-operations-Dbk3xPKu.d.ts} +19 -4
  135. package/dist/turn-operations-Dbk3xPKu.d.ts.map +1 -0
  136. package/dist/{types-C0PJmBaO.d.ts → types-CMXR7A0b.d.ts} +2 -2
  137. package/dist/{types-C0PJmBaO.d.ts.map → types-CMXR7A0b.d.ts.map} +1 -1
  138. package/dist/types-CzmghX7C.js.map +1 -1
  139. package/dist/types.d.ts +2 -2
  140. package/dist/{url-CLl0u4Z8.js → url-D4vG7lzO.js} +2 -2
  141. package/dist/{url-CLl0u4Z8.js.map → url-D4vG7lzO.js.map} +1 -1
  142. package/dist/{version-v77Y4A3C.js → version-DDRBX7K6.js} +2 -2
  143. package/dist/{version-v77Y4A3C.js.map → version-DDRBX7K6.js.map} +1 -1
  144. package/dist/{workspace-exec-stream-kqARO8iC.js → workspace-exec-stream-iHv9cooF.js} +5 -5
  145. package/dist/{workspace-exec-stream-kqARO8iC.js.map → workspace-exec-stream-iHv9cooF.js.map} +1 -1
  146. package/dist/{write-file-QYrcDdl3.js → write-file-ChtRPdEL.js} +3 -3
  147. package/dist/{write-file-QYrcDdl3.js.map → write-file-ChtRPdEL.js.map} +1 -1
  148. package/dist/{ws-BD9FN98u.js → ws-B2HB87nz.js} +6 -6
  149. package/dist/{ws-BD9FN98u.js.map → ws-B2HB87nz.js.map} +1 -1
  150. package/dist/{ws-client-D7eb2UbH.js → ws-client-DPC9ZJHr.js} +4 -4
  151. package/dist/{ws-client-D7eb2UbH.js.map → ws-client-DPC9ZJHr.js.map} +1 -1
  152. package/docs/CODEMODE.md +6 -0
  153. package/docs/IPC.md +2 -0
  154. package/docs/ONBOARDING.md +1 -1
  155. package/package.json +1 -1
  156. package/dist/client-core-BdD20sOv.d.ts.map +0 -1
  157. package/dist/dispatcher-BICXFGiJ.js.map +0 -1
  158. package/dist/index-DyQyDLX1.d.ts.map +0 -1
  159. package/dist/mcp-allowlist-BQ5MvhHi.js.map +0 -1
  160. package/dist/oauth-TVPPye84.js.map +0 -1
  161. package/dist/prelude-HzY_JEGq.js.map +0 -1
  162. package/dist/protocol-BIv1Yuvl.js.map +0 -1
  163. package/dist/secret-shapes-CEoGmfEE.js.map +0 -1
  164. package/dist/session-7TUHs42_.js.map +0 -1
  165. package/dist/spaces-CyjMh5Zf.js.map +0 -1
  166. package/dist/store-L01W0gGI.js.map +0 -1
  167. package/dist/transcript-reconcile-DeFLIQ14.js.map +0 -1
  168. package/dist/turn-operations-BbGJ-KSt.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spaces-C_x2bvEl.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 /** The agent profile the run ran as. */\n agent?: string\n /** The previous run ran as another agent: the user switched before this run. */\n agentSwitched?: boolean\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/** Leads the agent when the run switched to it from the previous run's. */\nexport const RUN_RECEIPT_SWITCH_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 * @param previous - The session's top-level run before this one, to tell an agent switch.\n */\nexport function runReceiptFromRun(run: SessionRun, now = Date.now(), previous?: SessionRun): 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 const agentSwitched = run.agent !== undefined && previous?.agent !== undefined && previous.agent.id !== run.agent.id\n return {\n ...(run.agent ? { agent: run.agent.label } : {}),\n ...(agentSwitched ? { agentSwitched } : {}),\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 = 'agent' | '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.agent)\n cells.push({ kind: 'agent', text: r.agentSwitched ? `${RUN_RECEIPT_SWITCH_GLYPH}${r.agent}` : r.agent })\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 // The agent names a run, it doesn't make one worth a receipt.\n return formatRunReceiptCells(r).some(cell => cell.kind !== 'agent')\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(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, '\\'')\n .replace(/&amp;/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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"}
@@ -7,7 +7,7 @@ import { i as formatTokenUsage, n as effectiveInputTokens, r as flattenTurns } f
7
7
  import { a as fmtCost, l as formatDuration, p as previewLine } from "./format-YgaIcuQ5.js";
8
8
  import { t as mergeReplyText } from "./reply-text-Qkc6DtEt.js";
9
9
  import { f as readSessionEnvironment, r as LOCAL_ENVIRONMENT } from "./environment-B4asakh7.js";
10
- import { F as parseSessionEventXml, M as isMainSession, N as isSideSession, Z as formatRunReceiptLine, et as hasRunReceipt, f as readSessionSpaceId, nt as runReceiptFromRun } from "./spaces-CyjMh5Zf.js";
10
+ import { F as parseSessionEventXml, M as isMainSession, N as isSideSession, Q as formatRunReceiptLine, f as readSessionSpaceId, rt as runReceiptFromRun, tt as hasRunReceipt } from "./spaces-C_x2bvEl.js";
11
11
  import { F as commandNoteText, I as commandStepLabel, b as extractEditPayload, c as inferPromptRefs, d as nestedRecordEvents, k as parseEditOutcomesFromResult, n as titleFromText, s as readModelApproval, t as oneLineTitle, z as parseCommandHeader } from "./session-title-text-DHCNlzjb.js";
12
12
  import { Buffer } from "node:buffer";
13
13
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
@@ -317,8 +317,12 @@ function withRunReceipts(events, turns, runs, turnStarts) {
317
317
  const lastTurnIdByRun = /* @__PURE__ */ new Map();
318
318
  for (const turn of turns) if (turn.runId) lastTurnIdByRun.set(turn.runId, turn.id);
319
319
  const receiptAfterTurn = /* @__PURE__ */ new Map();
320
+ let previousTopLevel;
320
321
  for (const run of runs) {
321
- if (run.parentRunId !== void 0 || run.endedAt === void 0) continue;
322
+ if (run.parentRunId !== void 0) continue;
323
+ const previous = previousTopLevel;
324
+ if (run.agent) previousTopLevel = run;
325
+ if (run.endedAt === void 0) continue;
322
326
  const lastTurnId = lastTurnIdByRun.get(run.id);
323
327
  if (!lastTurnId) continue;
324
328
  const ending = [];
@@ -328,7 +332,7 @@ function withRunReceipts(events, turns, runs, turnStarts) {
328
332
  ...run.errorDetail ? { detail: run.errorDetail } : {},
329
333
  turnId: lastTurnId
330
334
  });
331
- const receipt = runReceiptFromRun(run);
335
+ const receipt = runReceiptFromRun(run, Date.now(), previous);
332
336
  if (hasRunReceipt(receipt)) ending.push({
333
337
  kind: "run-summary",
334
338
  text: formatRunReceiptLine(receipt),
@@ -757,34 +761,10 @@ function promptAttachments(turn, block) {
757
761
  }
758
762
  return attachments;
759
763
  }
760
- /**
761
- * The transcript row of a system note: an info row with its label, else its
762
- * text. Shared by the replay and the live stream so both render it alike.
763
- *
764
- * @param block - A text block carrying a system note.
765
- * @param tag - The turn it belongs to, plus subagent ancestry.
766
- * @param tag.turnId - The note's turn.
767
- * @param tag.childId - The subagent lane, for a subagent's turn.
768
- * @param tag.depth - The subagent depth, for a subagent's turn.
769
- */
770
- function systemNoteEvent(block, tag) {
771
- return {
772
- kind: "info",
773
- text: block.systemNote.label ?? block.text,
774
- notice: {
775
- severity: "info",
776
- label: "note"
777
- },
778
- ...tag
779
- };
780
- }
781
764
  /** Replay one non-blank text block of a user turn. */
782
765
  function replayUserText(state, turn, block, depth, tag, marker) {
783
766
  const { events } = state;
784
- if (isSystemNoteBlock(block)) {
785
- events.push(systemNoteEvent(block, tag));
786
- return;
787
- }
767
+ if (isSystemNoteBlock(block)) return;
788
768
  const taskEvent = parseTaskNotificationBlock(block.text, tag);
789
769
  if (taskEvent) {
790
770
  events.push(taskEvent);
@@ -1462,6 +1442,6 @@ function usageByModel(runs) {
1462
1442
  return [...byModel.values()].sort((a, b) => b.cost - a.cost || totalTokens(b) - totalTokens(a));
1463
1443
  }
1464
1444
  //#endregion
1465
- export { buildSpawnQueuedEvent as A, systemNoteEvent as C, updateToolEventOutcomes as D, toolResultText as E, usageByModel as O, sumRunCosts as S, toolCallPreview as T, runProgress as _, eventsFromTurns as a, sessionEnvironmentSummary as b, headerFromData as c, loadState as d, marginTopFor as f, runInProgress as g, replayTurns as h, deriveSessionTitle as i, buildSpawnStartEvent as j, buildSpawnEndEvent as k, lastContextSizeFromTurns as l, readSessionHeader as m, compactSummaryEvent as n, getSessionHeader as o, noticeTurnContent as p, createStateStore as r, getSessionMeta as s, DEFAULT_SESSION_TITLE as t, listSessionMeta as u, saveState as v, titleFromTurns as w, stripSpawnTokensLine as x, serverToolResultSummary as y };
1445
+ export { buildSpawnStartEvent as A, titleFromTurns as C, usageByModel as D, updateToolEventOutcomes as E, buildSpawnEndEvent as O, sumRunCosts as S, toolResultText as T, runProgress as _, eventsFromTurns as a, sessionEnvironmentSummary as b, headerFromData as c, loadState as d, marginTopFor as f, runInProgress as g, replayTurns as h, deriveSessionTitle as i, buildSpawnQueuedEvent as k, lastContextSizeFromTurns as l, readSessionHeader as m, compactSummaryEvent as n, getSessionHeader as o, noticeTurnContent as p, createStateStore as r, getSessionMeta as s, DEFAULT_SESSION_TITLE as t, listSessionMeta as u, saveState as v, toolCallPreview as w, stripSpawnTokensLine as x, serverToolResultSummary as y };
1466
1446
 
1467
- //# sourceMappingURL=store-L01W0gGI.js.map
1447
+ //# sourceMappingURL=store-BpkWIaSH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-BpkWIaSH.js","names":[],"sources":["../src/chat/spawn-markers.ts","../src/chat/replay-runs.ts","../src/chat/store.ts"],"sourcesContent":["/**\n * Pure builders for live subagent lifecycle markers — the `spawn-start` /\n * `spawn-end` StreamEvents the engine broadcasts from the `spawn:before` /\n * `spawn:complete` hooks — and from their nested `child:spawn:*`\n * re-emissions, whose `parentChildId` lands on `spawn.parentChildId`.\n *\n * Mirrors canonical replay's `pushSpawnStart` / `pushSpawnEnd`\n * (`src/chat/store.ts`) so a live GUI transcript and a reloaded one render\n * byte-identical markers. Kept apart from the engine so the wire-text\n * shapes are unit-testable without an Electron harness.\n */\nimport type { AgentStats, SpawnQueuedContext } from '../types'\nimport type { SpawnEndInfo, StreamEvent } from './types'\nimport { effectiveInputTokens, flattenTurns, formatTokenUsage } from '../stats'\n\nimport { fmtCost, formatDuration, previewLine } from './format'\n\n/**\n * Wall-clock start + task preview per in-flight child, keyed by spawn id.\n * Captured at `spawn:before` and read back at `spawn:complete` — drives\n * the elapsed time on the spawn-end marker and copies the stable task\n * preview onto the terminal marker.\n */\nexport interface SpawnStartRecord {\n /** Epoch milliseconds when the child started. */\n startedAt: number\n /** The task prompt given to the child. */\n task: string\n /** Model the child runs on. */\n model?: string\n}\n\n/** Upper bound on the task carried on a start marker: enough to read, not a payload. */\nexport const SPAWN_PROMPT_MAX = 20_000\n\n/** A worker's task as carried on its start marker. */\nexport function spawnPrompt(task: string): string {\n return task.length > SPAWN_PROMPT_MAX ? `${task.slice(0, SPAWN_PROMPT_MAX)}\\n\\n… (${task.length - SPAWN_PROMPT_MAX} more characters)` : task\n}\n\n/**\n * `spawn-start` marker for a child that just launched. `previewLine`\n * collapses inline newlines/tabs so the marker stays on one visual row.\n * Resumed children carry the label of the run they continue in\n * `spawn.resumes`, as the reload path does.\n *\n * @param ctx - The new child: id, task and optional call id, depth, model and resume or parent child ids.\n * @param startedAt - Epoch milliseconds when the child started.\n */\nexport function buildSpawnStartEvent(\n ctx: { id: string, task: string, callId?: string, depth?: number, model?: string, resumesChildId?: string, parentChildId?: string },\n startedAt: number,\n): StreamEvent {\n const taskPreview = previewLine(ctx.task, 80)\n\n return {\n kind: 'spawn-start',\n text: taskPreview,\n childId: ctx.id,\n depth: ctx.depth ?? 1,\n spawn: {\n startedAt,\n prompt: spawnPrompt(ctx.task),\n ...(ctx.resumesChildId ? { resumes: ctx.resumesChildId } : {}),\n ...(ctx.parentChildId ? { parentChildId: ctx.parentChildId } : {}),\n ...(ctx.callId ? { callId: ctx.callId } : {}),\n },\n ...(ctx.model ? { model: ctx.model } : {}),\n }\n}\n\n/**\n * `spawn-queued` marker: a top-level spawn call waiting for a slot. On the\n * parent's own lane (only the top-level agent waits), live only.\n */\nexport function buildSpawnQueuedEvent(ctx: SpawnQueuedContext): StreamEvent {\n return {\n kind: 'spawn-queued',\n text: `waiting for one of ${ctx.waitingFor.max} workers to finish`,\n ...(ctx.callId ? { callId: ctx.callId } : {}),\n waitingFor: ctx.waitingFor,\n }\n}\n\n/**\n * `spawn-end` marker. The facts ride on `spawn.end` (status, elapsed\n * wall-clock time, USD cost summed across the child's whole turn tree, token\n * totals); renderers read them through `describeSpawnEnd`. Most providers\n * don't populate `cost`, so it is usually absent: no \"$0.00\" lies. `timeout`\n * maps to `done`, matching the TUI.\n *\n * `text` keeps its stored shape for clients that predate `spawn.end`; see\n * {@link spawnEndText}.\n *\n * @param ctx - The finished child: id, depth, status, stats and parent child id.\n * @param started - Record saved when the child started. Without it the event has no elapsed time or task.\n * @param now - Epoch milliseconds for the end of the run.\n */\nexport function buildSpawnEndEvent(\n ctx: { id: string, depth?: number, status?: 'completed' | 'aborted' | 'timeout' | 'error', stats: AgentStats, parentChildId?: string },\n started: SpawnStartRecord | undefined,\n now = Date.now(),\n): StreamEvent {\n const startedAt = started?.startedAt\n const cost = flattenTurns(ctx.stats).reduce((sum, turn) => sum + (turn.cost ?? 0), 0)\n const end: SpawnEndInfo = {\n status: ctx.status === 'aborted' || ctx.status === 'error' ? ctx.status : 'done',\n ...(startedAt !== undefined ? { durationMs: now - startedAt } : {}),\n ...(cost > 0 ? { costUsd: cost } : {}),\n tokensIn: effectiveInputTokens(ctx.stats),\n tokensOut: ctx.stats.totalOut,\n }\n\n return {\n kind: 'spawn-end',\n text: spawnEndText(end, formatTokenUsage(ctx.stats)),\n childId: ctx.id,\n depth: ctx.depth ?? 1,\n spawn: {\n startedAt: startedAt ?? now,\n ...(started?.task ? { task: started.task } : {}),\n turns: ctx.stats.turns,\n ...(ctx.parentChildId ? { parentChildId: ctx.parentChildId } : {}),\n end,\n },\n ...(started?.model ? { model: started.model } : {}),\n }\n}\n\n/**\n * The stored `text` of a `spawn-end` marker: `done · 42s · $0.01 · in 3100 / 400 out`,\n * or `done in 3100 / 400 out` with neither time nor cost. A data format kept\n * byte-stable for clients that predate `spawn.end` and parse it; never shown\n * as is (renderers use `describeSpawnEnd`). Shared by the live and replay\n * builders so both write the same bytes.\n *\n * @param end - How the worker ended.\n * @param usage - The `formatTokenUsage` line for its subtree.\n */\nexport function spawnEndText(end: SpawnEndInfo, usage: string): string {\n const segments = [\n ...(end.durationMs !== undefined ? [formatDuration(end.durationMs)] : []),\n ...(end.costUsd !== undefined ? [fmtCost(end.costUsd)] : []),\n ]\n return segments.length > 0 ? [end.status, ...segments, usage].join(' \\u00B7 ') : `${end.status} ${usage}`\n}\n","/**\n * Run-level reconstruction for transcript replay (`eventsFromTurns` in\n * `./store`): the run index (with records synthesized for legacy gaps),\n * `child-N` labels, ancestry and subtree totals, the spawn markers, and the\n * per-run receipts.\n */\nimport type { SessionRun } from '../session'\nimport type { SessionTurn } from '../types'\nimport type { ModelApproval, SpawnEndInfo, StreamEvent } from './types'\nimport { formatTokenUsage } from '../stats'\nimport { toolResultToText } from '../types'\nimport { previewLine } from './format'\nimport { readModelApproval } from './model-approval'\nimport { formatRunReceiptLine, hasRunReceipt, runReceiptFromRun } from './run-receipt'\nimport { spawnEndText, spawnPrompt } from './spawn-markers'\n\n/**\n * The run records a replay works from, by id: `runs[]` first-write-wins, plus\n * synthesized records for runs the turns reference but `runs[]` lacks.\n */\nexport function indexReplayRuns(turns: SessionTurn[], runs: readonly SessionRun[]): Map<string, SessionRun> {\n // First-write-wins on duplicate ids. Historical sessions persisted\n // before each mint re-synced the run counter with the session\n // (`syncRunCounter` in `src/agent-runtime.ts`) could have a child agent and\n // its parent both registering runs with the SAME `run_<n>` id — the subagent ran first (lands first in\n // `runs[]`), the parent's collision came later (lands last).\n // Last-write-wins would resolve the id to the collision (depth=0,\n // no parentRunId), the structural ancestry walk would return empty,\n // and the renderer would paint a subagent's transcript at depth 0:\n // indent gets crazy, spawn markers placed wrong, the whole block\n // looks \"off\". Preferring the FIRST entry keeps the depth + parent\n // info from the subagent record, which is what every turn under\n // that runId actually wants. Clean sessions have one entry per id,\n // so first/last makes no difference.\n const runById = new Map<string, SessionRun>()\n for (const run of runs) {\n if (!runById.has(run.id))\n runById.set(run.id, run)\n }\n\n // Defensive synthesis for legacy sessions whose `runs[]` is missing\n // entries the turns reference (e.g. the pre-fix `updateRun`-not-\n // upsert bug in the sqlite/memory stores dropped subagent records\n // silently). Without it, an orphan turn's `ancestryOf()` returns an\n // empty chain → depth = 0 → the subagent's user-text-block leaks\n // as a `user-prompt` event and pollutes the TUI's history.\n //\n // Inference is structural: walk turns chronologically, maintain a\n // stack of currently-open runIds, and decide whether a NEW runId\n // is nested or top-level based on whether the stack-top run has any\n // unresolved tool_calls. A nested runId pushes (depth grows); a\n // top-level runId resets the stack first. Tracked per-runId in\n // `pendingToolCallsPerRun` — incremented on `tool_call` blocks,\n // decremented on `tool_result` blocks.\n //\n // Without the nesting gate, the naive \"push every new runId, never\n // pop\" walk treated back-to-back top-level prompts as a deepening\n // chain of subagents, and synthesized depth grew unbounded —\n // `indentFor(36)` = 72 cells, which is the far-right rendering the\n // user reported for a session with many top-level chats plus one\n // missing run record. Real records (present in `runById`) are NEVER\n // overwritten — synthesis only fills the gap.\n const runStack: string[] = []\n const pendingToolCallsPerRun = new Map<string, Set<string>>()\n for (const turn of turns) {\n const rid = turn.runId\n if (!rid)\n continue\n const idxInStack = runStack.indexOf(rid)\n if (idxInStack >= 0) {\n // Already open — pop any deeper nesting that's now closed.\n runStack.length = idxInStack + 1\n }\n else {\n // New runId — nested only when the parent has a pending\n // SPAWN-shaped tool_call (the call that's waiting on a subagent).\n // Restricting to spawn-like names matters: a run killed mid-tool\n // (dev-loop restart, crash) leaves its ordinary tool_calls\n // unresolved FOREVER, and gating on \"any pending call\" would\n // synthesize every subsequent top-level run with a missing record\n // as that dead run's child — the user's own prompt\n // rendered as a `child-N` worker running `<prompt>` with `0 / 0 out`.\n const top = runStack[runStack.length - 1]\n const parentHasPending = top !== undefined && (pendingToolCallsPerRun.get(top)?.size ?? 0) > 0\n if (!parentHasPending) {\n // Fresh top-level — drop the stack so a long chat history\n // doesn't accumulate phantom depth.\n runStack.length = 0\n }\n runStack.push(rid)\n\n if (!runById.has(rid))\n runById.set(rid, synthesizedRun(turn, rid, runStack))\n }\n\n // Update the pending-tool-calls set for this turn's runId. Tracked\n // here (not in the synthesis branch alone) so we have an accurate\n // running view for every turn — the stack-top check above reads\n // this for the next iteration.\n const pending = pendingToolCallsPerRun.get(rid) ?? new Set<string>()\n for (const block of turn.content) {\n // Only spawn-shaped calls can open a child run — tracking every\n // tool_call would let a run killed mid-`shell`/`read_file` adopt\n // unrelated later runs as phantom children (see gate above).\n if (block.type === 'tool_call' && /spawn/i.test(block.name))\n pending.add(block.id)\n else if (block.type === 'tool_result')\n pending.delete(block.callId)\n }\n pendingToolCallsPerRun.set(rid, pending)\n }\n return runById\n}\n\n/** The record synthesized for a run whose own record is missing, at its place in the open-run stack. */\nfunction synthesizedRun(turn: SessionTurn, rid: string, runStack: readonly string[]): SessionRun {\n // Missing record — synthesize. Depth = stack depth - 1 (zero-based).\n // Prompt: the first user text-block on this turn (when present)\n // so the `child-N` spawn-start marker renders with a meaningful\n // preview rather than an empty string. Empty when the first\n // turn isn't a user turn or has no text block — `pushSpawnStart`\n // then falls through to the bare label.\n const depth = runStack.length - 1\n const parentRunId = depth > 0 ? runStack[depth - 1] : undefined\n let inferredPrompt = ''\n if (turn.role === 'user') {\n for (const block of turn.content) {\n if (block.type === 'text' && !block.systemNote && block.text.trim()) {\n inferredPrompt = block.text\n break\n }\n }\n }\n return {\n id: rid,\n startedAt: turn.createdAt,\n prompt: inferredPrompt,\n status: 'completed',\n depth,\n ...(parentRunId ? { parentRunId } : {}),\n }\n}\n\n/** `runId` → its friendly `child-N` label (the run id itself when it has none). */\nexport function childLabeler(runById: ReadonlyMap<string, SessionRun>): (runId: string) => string {\n // Build a chronological mapping from the persisted `runId` (e.g. `run_2`) to\n // a friendly `child-N` label that mirrors what the spawn tool emits live\n // (see the session-scoped counter in `src/tools/spawn-helpers.ts`). Without this the reloaded\n // transcript labels its subagent block `[run_2]` while the persisted tool\n // result text inside that very block says `[sub-agent child-1]`.\n //\n // Reads from `runById` (NOT the raw `runs` param) so synthesized\n // fallback runs from `indexReplayRuns`' legacy-session synthesis also get\n // a proper `child-N` label.\n const childLabelByRunId = new Map<string, string>()\n const childRuns = [...runById.values()]\n .filter(r => (r.depth ?? 0) > 0)\n .sort((a, b) => a.startedAt - b.startedAt)\n // Prefer the label persisted at `startRun` (`SessionRun.label`) — the\n // positional fallback covers legacy sessions and synthesized runs, but\n // can desync from announced labels under concurrent spawns or counter\n // drift, which would mislabel resume targets.\n childRuns.forEach((r, i) => childLabelByRunId.set(r.id, r.label ?? `child-${i + 1}`))\n return (runId: string) => childLabelByRunId.get(runId) ?? runId\n}\n\n/** `callId` → the tool name its assistant `tool_call` named. */\nexport function toolNamesByCallId(turns: SessionTurn[]): Map<string, string> {\n // Build a `callId → toolName` map from the assistant `tool_call` blocks so\n // we can tag each user-role `tool_result` event with the tool it answers.\n // The renderer uses this for tool-aware filters (e.g. hiding the spawn\n // tool's result when `hideSubagentOutput` is on).\n const toolByCallId = new Map<string, string>()\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')\n toolByCallId.set(block.id, block.name)\n }\n }\n return toolByCallId\n}\n\n/** What a call's user-role `tool_result` replays onto its `tool` event. */\nexport interface ReplayedResult {\n /** The result's text: per-edit outcomes are parsed from it (`parseEditOutcomesFromResult`). */\n text: string\n /** The Model decision verdict stored on the result's metadata. */\n approval?: ModelApproval\n}\n\n/** `callId` → its user-role `tool_result` ({@link ReplayedResult}). */\nexport function resultsByCallId(turns: SessionTurn[]): Map<string, ReplayedResult> {\n // Live capture mutates `_outcomes` on input before tool execution; on\n // disk, only the tool_result body carries the post-decision shape, so the\n // replay parses it.\n const resultByCallId = new Map<string, ReplayedResult>()\n for (const turn of turns) {\n if (turn.role !== 'user')\n continue\n for (const block of turn.content) {\n if (block.type !== 'tool_result')\n continue\n const text = typeof block.output === 'string' ? block.output : toolResultToText(block.output)\n const approval = readModelApproval(block.metadata?.approval)\n resultByCallId.set(block.callId, approval ? { text, approval } : { text })\n }\n }\n return resultByCallId\n}\n\n/**\n * The `child-N` label of the subagent that spawned `run` — the live\n * `child:spawn:*` hook's `parentChildId`. Undefined for a direct child of a\n * top-level run (and for orphans whose parent record is gone).\n */\nexport function spawnerLabeler(\n runById: ReadonlyMap<string, SessionRun>,\n labelFor: (runId: string) => string,\n): (run: SessionRun) => string | undefined {\n return (run: SessionRun) => {\n const parent = run.parentRunId ? runById.get(run.parentRunId) : undefined\n return parent && (parent.depth ?? 0) > 0 ? labelFor(parent.id) : undefined\n }\n}\n\n/** A turn's run ancestry over `runById`. */\nexport function runAncestry(runById: ReadonlyMap<string, SessionRun>): (turnRunId: string | undefined) => SessionRun[] {\n // Ancestry chain for a turn's run — `[root child, …, the turn's own run]`,\n // excluding any depth-0 (top-level) ancestor since top-level runs don't\n // render spawn-start/spawn-end markers. Feeds the open-once /\n // close-after-last-subtree-turn logic in `eventsFromTurns`\n // (`./store`), so neither depth ≥ 2\n // (grandchild) sequences nor interleaved concurrent siblings emit\n // duplicate `spawn-start` / premature `spawn-end` markers.\n return (turnRunId: string | undefined): SessionRun[] => {\n if (!turnRunId)\n return []\n const chain: SessionRun[] = []\n let cursor: SessionRun | undefined = runById.get(turnRunId)\n const seen = new Set<string>()\n while (cursor) {\n // Defensive cycle guard — a corrupt run row pointing at its own\n // ancestor would otherwise loop forever.\n if (seen.has(cursor.id))\n break\n seen.add(cursor.id)\n if ((cursor.depth ?? 0) > 0)\n chain.unshift(cursor)\n cursor = cursor.parentRunId ? runById.get(cursor.parentRunId) : undefined\n }\n return chain\n }\n}\n\nexport interface SubtreeTotals { cost: number, tokensIn: number, tokensOut: number, cacheRead: number, cacheCreation: number }\n\n/** A run's totals summed over its whole subtree (itself and every descendant). */\nexport function runSubtreeTotals(runById: ReadonlyMap<string, SessionRun>): (root: SessionRun) => SubtreeTotals {\n // Children index for subtree aggregation. Run records are\n // deliberately per-run additive (children persist as their own runs —\n // folding cumulative totals into the parent would double-count, see the\n // session ledger in `finishRun`, `src/agent-run-finalize.ts`), while the\n // live spawn-end marker reports subtree totals (children folded into\n // `finalStats` there). Walk descendants here so live and reloaded markers\n // agree for nested spawns — on BOTH numbers, not just cost.\n const childRunsOf = new Map<string, SessionRun[]>()\n for (const run of runById.values()) {\n if (!run.parentRunId)\n continue\n const siblings = childRunsOf.get(run.parentRunId) ?? []\n siblings.push(run)\n childRunsOf.set(run.parentRunId, siblings)\n }\n return (root: SessionRun): SubtreeTotals => {\n const totals = { cost: 0, tokensIn: 0, tokensOut: 0, cacheRead: 0, cacheCreation: 0 }\n const queue = [root]\n const seen = new Set<string>()\n while (queue.length > 0) {\n const run = queue.pop() as SessionRun\n if (seen.has(run.id))\n continue\n seen.add(run.id)\n totals.cost += run.cost ?? run.turnUsage?.reduce((sum, u) => sum + (u.cost ?? 0), 0) ?? 0\n totals.tokensIn += run.tokensIn ?? run.totalUsage?.input ?? 0\n totals.tokensOut += run.tokensOut ?? run.totalUsage?.output ?? 0\n totals.cacheRead += run.totalUsage?.cacheRead ?? 0\n totals.cacheCreation += run.totalUsage?.cacheCreation ?? 0\n queue.push(...(childRunsOf.get(run.id) ?? []))\n }\n return totals\n }\n}\n\n/** The `spawn-end` marker closing a child run, carrying its subtree totals. */\nexport function spawnEndEvent(run: SessionRun, totals: SubtreeTotals, labelFor: (runId: string) => string, parentChildId?: string): StreamEvent {\n // Same caching gotcha as the live path: `tokensIn` mirrors\n // `AgentStats.totalIn` (new uncached only). `formatTokenUsage` folds\n // cache reads/creations into the headline `in` number. Totals span\n // the run's whole subtree — the live marker formats the child's\n // `finalStats`, which already folds grandchildren in.\n const usage = formatTokenUsage({\n totalIn: totals.tokensIn,\n totalOut: totals.tokensOut,\n totalCacheRead: totals.cacheRead,\n totalCacheCreation: totals.cacheCreation,\n })\n // Same facts as the live `spawn:complete` hook (`buildSpawnEndEvent`,\n // `src/chat/spawn-markers.ts`) so a reloaded transcript renders\n // byte-identical markers. `endedAt` is absent on runs synthesized from bare\n // turns (legacy sessions), so the duration drops. Zero/unknown cost is\n // omitted (no \"$0.00\" lies).\n const end: SpawnEndInfo = {\n status: run.status === 'aborted' || run.status === 'error' ? run.status : 'done',\n ...(run.endedAt !== undefined ? { durationMs: run.endedAt - run.startedAt } : {}),\n ...(totals.cost > 0 ? { costUsd: totals.cost } : {}),\n tokensIn: totals.tokensIn + totals.cacheRead + totals.cacheCreation,\n tokensOut: totals.tokensOut,\n }\n const model = run.turnUsage?.find(u => u.modelId)?.modelId\n const turns = run.turns ?? run.turnUsage?.length\n return {\n kind: 'spawn-end',\n text: spawnEndText(end, usage),\n childId: labelFor(run.id),\n depth: run.depth ?? 1,\n spawn: {\n startedAt: run.startedAt,\n task: previewLine(run.prompt, 80),\n ...(turns !== undefined ? { turns } : {}),\n ...(parentChildId ? { parentChildId } : {}),\n end,\n },\n ...(model ? { model } : {}),\n }\n}\n\n/** The `spawn-start` marker opening a child run. */\nexport function spawnStartEvent(run: SessionRun, labelFor: (runId: string) => string, parentChildId?: string): StreamEvent {\n // Single-line preview — newlines / tabs in the prompt collapse to\n // single spaces so the spawn-start marker stays on one visual row.\n // See the matching comment on `buildSpawnStartEvent` (`spawn-markers.ts`) for\n // the failure mode (multi-line prompt prints the marker across\n // multiple visual rows, the next event lands far right).\n const taskPreview = previewLine(run.prompt, 80)\n // Which model the child ran on — mirrors the live `spawn:before` hook's\n // `model`. Reconstructed from the first turn that recorded a model id\n // (providers echo it per turn; the run-level `totalUsage` aggregate\n // deliberately drops `modelId`, so per-turn usage is the only source).\n const model = run.turnUsage?.find(u => u.modelId)?.modelId\n // A resumed child (spawn `resume`) is tagged with the label of the run\n // it continues — mirrors the live `spawn:before` hook's `resumesChildId`.\n const resumes = run.resumedFromRunId ? labelFor(run.resumedFromRunId) : undefined\n return {\n kind: 'spawn-start',\n text: taskPreview,\n childId: labelFor(run.id),\n depth: run.depth ?? 1,\n spawn: {\n startedAt: run.startedAt,\n task: taskPreview,\n prompt: spawnPrompt(run.prompt),\n ...(resumes ? { resumes } : {}),\n ...(parentChildId ? { parentChildId } : {}),\n ...(run.parentCallId ? { callId: run.parentCallId } : {}),\n },\n ...(model ? { model } : {}),\n }\n}\n\n/**\n * Place each finished top-level run's receipt (and its error) after the last\n * event of its last turn. Returns the merged events (`events` itself when there\n * is nothing to place) and remaps `turnStarts` (ascending indexes into\n * `events`) in place to the merged array, in one walk.\n */\nexport function withRunReceipts(events: StreamEvent[], turns: SessionTurn[], runs: readonly SessionRun[], turnStarts: number[]): StreamEvent[] {\n /*\n * Per-run receipts. For every finished TOP-LEVEL run, a `run-summary` lands\n * right after the last event of that run — the same record and the same\n * formatter the live path uses at `run:end`, so a reloaded session shows\n * exactly the line the user saw when the run ended. Child runs (subagents)\n * get no receipt of their own: their spawn-end marker already carries their\n * totals, and a receipt per child would bury the parent's.\n */\n const lastTurnIdByRun = new Map<string, string>()\n for (const turn of turns) {\n if (turn.runId)\n lastTurnIdByRun.set(turn.runId, turn.id)\n }\n const receiptAfterTurn = new Map<string, StreamEvent[]>()\n let previousTopLevel: SessionRun | undefined\n for (const run of runs) {\n if (run.parentRunId !== undefined)\n continue\n // `/` command runs record no agent: compare with the last run that did.\n const previous = previousTopLevel\n if (run.agent)\n previousTopLevel = run\n if (run.endedAt === undefined)\n continue\n const lastTurnId = lastTurnIdByRun.get(run.id)\n if (!lastTurnId)\n continue\n const ending: StreamEvent[] = []\n if (run.status === 'error' && run.error)\n ending.push({ kind: 'error', text: run.error, ...(run.errorDetail ? { detail: run.errorDetail } : {}), turnId: lastTurnId })\n const receipt = runReceiptFromRun(run, Date.now(), previous)\n if (hasRunReceipt(receipt))\n ending.push({ kind: 'run-summary', text: formatRunReceiptLine(receipt), runSummary: receipt, turnId: lastTurnId })\n if (ending.length > 0)\n receiptAfterTurn.set(lastTurnId, ending)\n }\n if (receiptAfterTurn.size === 0)\n return events\n // Insert after the LAST event carrying that turn id (a turn can emit\n // several events — text, tool calls, spawn markers).\n const receiptAfterIndex = new Map<number, StreamEvent[]>()\n const lastIndexByTurn = new Map<string, number>()\n events.forEach((e, idx) => {\n if (e.turnId && receiptAfterTurn.has(e.turnId))\n lastIndexByTurn.set(e.turnId, idx)\n })\n if (lastIndexByTurn.size === 0)\n return events\n for (const [turnId, idx] of lastIndexByTurn)\n receiptAfterIndex.set(idx, receiptAfterTurn.get(turnId)!)\n // Splicing each receipt in place costs O(events) per run (quadratic on long\n // sessions); one walk into a fresh array keeps the same order.\n const out: StreamEvent[] = []\n let k = 0\n for (let i = 0; i < events.length; i++) {\n while (k < turnStarts.length && turnStarts[k]! <= i)\n turnStarts[k++] = out.length\n out.push(events[i]!)\n const ending = receiptAfterIndex.get(i)\n if (ending)\n out.push(...ending)\n }\n while (k < turnStarts.length)\n turnStarts[k++] = out.length\n return out\n}\n","import type { SessionData, SessionMetaRow, SessionRun, SessionStore } from '../session'\nimport type { JsonValue, SessionContentBlock, SessionTurn, ThinkingLevel, ToolResultContent } from '../types'\nimport type { ProviderKey } from './auth'\nimport type { ModelChainEntry } from './model-chain'\nimport type { OnboardingState } from './onboarding/types'\nimport type { ReplayedResult } from './replay-runs'\nimport type { EditOutcome, SessionEnvironmentSummary, SessionHeader, SessionMeta, Settings, StreamEvent } from './types'\nimport { Buffer } from 'node:buffer'\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\nimport { errorMessage } from '../errors'\nimport { mergeReplyText } from '../session/reply-text'\nimport { isSystemNoteBlock } from '../system-notes'\nimport { unwrapCallTool } from '../tools/call-tool'\nimport { toolResultToText } from '../types'\nimport { commandNoteText, commandStepLabel, parseCommandHeader } from './command-notes'\nimport { parseEditOutcomesFromResult } from './edit-approval'\nimport { extractEditPayload } from './edit-diff'\nimport { ENVIRONMENT_METADATA_KEY, LOCAL_ENVIRONMENT, readSessionEnvironment } from './environment'\nimport { nestedRecordEvents } from './nested-call-events'\nimport { isMainSession, isSideSession, parseSessionEventXml } from './orchestration'\nimport { inferPromptRefs } from './prompt-segments'\nimport { childLabeler, indexReplayRuns, resultsByCallId, runAncestry, runSubtreeTotals, spawnEndEvent, spawnerLabeler, spawnStartEvent, toolNamesByCallId, withRunReceipts } from './replay-runs'\nimport { oneLineTitle, titleFromText } from './session-title-text'\nimport { readSessionSpaceId } from './spaces'\n\n// NOTE: `createTuiStore` lives in `src/session/sqlite.ts` so this module —\n// the public `zidane/chat` entry — does NOT statically import `bun:sqlite`.\n// A GUI consumer that imports `zidane/chat` does not transitively\n// pull in the Bun-only sqlite binding. Hosts construct their own store\n// (e.g. via `createTuiStore` from `zidane/session/sqlite` for terminal hosts)\n// and pass it through `runTui({ store })` / `resolveConfig({ store })`.\n\nfunction ensureStateDir(path: string): void {\n const dir = dirname(path)\n if (existsSync(dir))\n return\n try {\n mkdirSync(dir, { recursive: true })\n }\n catch (err) {\n throw new Error(\n `Could not create TUI state directory at \"${dir}\". `\n + `Override the location via \\`runTui({ storageDir, prefix })\\` or the `\n + `\\`ZIDANE_STORAGE_DIR\\` env var. Original error: ${errorMessage(err)}`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Persisted UI state — what to resume on next launch.\n//\n// `StateStoreApi` is a thin facade that binds load/save to a specific JSON\n// path, so the rest of the UI can persist state without knowing the layout.\n// ---------------------------------------------------------------------------\n\n/** Settings the terminal UI remembers between launches. */\nexport interface TuiState {\n /** Provider key used last. */\n lastProvider?: ProviderKey\n /** Id of the session open last. Resume reopens it. */\n lastSessionId?: string\n /**\n * The host key of the saved server `lastSessionId` lives on (for a server,\n * its host id: `src/chat/hosts/session-key`), absent for a session on this\n * computer. Resume reopens it there, and only with remote hosts on. A\n * `--host` session writes neither field (see `lastSessionId`).\n * Read from the older `lastSessionHostId` too ({@link loadState}).\n */\n lastSessionHostKey?: string\n /** The space the TUI's sessions list last showed (Spaces on; see `./spaces`). Per surface: the GUI keeps its own. */\n lastSpaceId?: string\n /** Per-provider last-used model id. */\n lastModelByProvider?: Partial<Record<ProviderKey, string>>\n /**\n * Per-provider last-used account (a credential's stable id), next to\n * {@link lastModelByProvider}; absent for a provider = its active account.\n */\n lastAccountByProvider?: Partial<Record<ProviderKey, string>>\n /**\n * Last-applied model fallback chain (entry 0 = primary, mirroring\n * `lastProvider` + `lastModelByProvider`; entries 1+ = fallbacks).\n * Additive — absent for single-model users and older state files.\n * Restored on launch only when entry 0 still matches the resolved\n * primary; entries are re-validated against the live registry.\n */\n lastModelChain?: ModelChainEntry[]\n /**\n * Per-model last-picked reasoning effort. Keyed by model id (not provider)\n * so switching the same model across providers preserves the user's\n * intent. Models without reasoning support are absent from the map.\n */\n lastEffortByModel?: Record<string, ThinkingLevel>\n /**\n * Per-model last-picked custom options (e.g. `{ fast: true }`). Keyed by\n * model id like {@link lastEffortByModel}, so a toggle follows the model\n * across providers. Models with no options are absent from the map.\n */\n lastModelOptionsByModel?: Record<string, Record<string, boolean>>\n /**\n * Agent profile id saved by earlier builds.\n *\n * @deprecated No longer read: the agent profile is per-session and fresh\n * sessions always start in the default. Kept so older state files parse.\n */\n lastAgent?: string\n /** User-toggled transcript filters. Persisted so they outlive a launch. */\n settings?: Partial<Settings>\n /**\n * The setup wizard's records: a run in progress, the shared steps, and each\n * app's own steps (`docs/ONBOARDING.md`). Both apps read and write it.\n */\n onboarding?: OnboardingState\n}\n\n/** Reads and writes the persisted {@link TuiState}. */\nexport interface StateStoreApi {\n /** Reads the saved state. */\n load: () => TuiState\n /** Writes the state. */\n save: (state: TuiState) => void\n /** The file, when the state lives in one: hosts watch it for changes other processes make. */\n path?: string\n}\n\n/**\n * Creates a {@link StateStoreApi} backed by the JSON file at `path`.\n *\n * @param path - Path of the state file.\n */\nexport function createStateStore(path: string): StateStoreApi {\n return {\n load: () => loadState(path),\n save: state => saveState(path, state),\n path,\n }\n}\n\n/**\n * Reads the TUI state file. Returns `{}` when it is missing or unreadable.\n *\n * @param path - Path of the state file.\n */\nexport function loadState(path: string): TuiState {\n if (!existsSync(path))\n return {}\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf-8'))\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))\n return withLegacyFields(parsed as TuiState & { lastSessionHostId?: unknown })\n }\n catch {\n // Corrupt state file → treat as absent so the user can reach the auth screen.\n }\n return {}\n}\n\n/** `lastSessionHostId` (earlier builds) is read as `lastSessionHostKey`, and leaves the state on its next save. */\nfunction withLegacyFields(state: TuiState & { lastSessionHostId?: unknown }): TuiState {\n const { lastSessionHostId: legacy, ...rest } = state\n if (rest.lastSessionHostKey === undefined && typeof legacy === 'string')\n return { ...rest, lastSessionHostKey: legacy }\n return rest\n}\n\n/**\n * Writes the TUI state file atomically, creating its directory when needed.\n *\n * @param path - Path of the state file.\n * @param state - The state to persist.\n */\nexport function saveState(path: string, state: TuiState): void {\n ensureStateDir(path)\n writeFileAtomic(path, JSON.stringify(state, null, 2))\n}\n\n// Re-export the model-info shape used by views below so callers don't have to\n// pull from `./config` for transcript helpers alone.\nexport type { ModelInfo } from './config'\n\n// ---------------------------------------------------------------------------\n// Session view helpers — derive UI-friendly shapes from persisted turns.\n// ---------------------------------------------------------------------------\n\n/**\n * Load every session and project it to the compact `SessionMeta` shape used by\n * the picker. Sorted by recency via the underlying store's `list()` contract\n * (sqlite store returns by `updated_at DESC`). `/side` sessions are left out,\n * like every session list (`isSideSession`).\n *\n * Robust to per-row failures: `Promise.allSettled` so a single corrupt or\n * unreadable row (malformed JSON, partial write, transient I/O error)\n * doesn't take down the entire picker. Failed rows are silently skipped;\n * a stderr line is emitted under `ZIDANE_DEBUG` for diagnosis.\n *\n * @param store - Session store to read from.\n * @param filter - Optional filter. `projectRoot` restricts the list to one project.\n * @param filter.projectRoot - Project to list. Omit to ignore the axis, `null` for untagged sessions.\n */\nexport async function listSessionMeta(\n store: SessionStore,\n filter?: {\n /**\n * Restrict to sessions belonging to this project. Omit to ignore\n * the axis; pass `null` to ask for untagged (legacy) sessions\n * specifically. The TUI defaults to passing the current git root /\n * cwd here so each project sees only its own conversations.\n */\n projectRoot?: string | null\n },\n): Promise<SessionMeta[]> {\n // Fast path: stores with an indexed summary query (sqlite) answer in a\n // single round-trip without materializing any session. Falls back to\n // the generic fan-out below on error so a store with a broken/legacy\n // index degrades to slow-but-correct instead of an empty picker.\n if (store.listMeta) {\n try {\n const rows = await store.listMeta(filter)\n return rows.filter(row => !isSideSession(row.metadata)).map(metaFromRow)\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG) {\n const cause = err instanceof Error ? err.message : String(err)\n process.stderr.write(`[zidane/chat] store.listMeta failed, falling back to full loads: ${cause}\\n`)\n }\n }\n }\n const ids = await store.list(filter)\n const settled = await Promise.allSettled(ids.map(async (id) => {\n const data = await store.load(id)\n return data && !isSideSession(data.metadata) ? metaFromData(id, data) : null\n }))\n const metas: SessionMeta[] = []\n for (let i = 0; i < settled.length; i++) {\n const result = settled[i]\n if (result.status === 'fulfilled' && result.value) {\n metas.push(result.value)\n }\n else if (result.status === 'rejected' && process.env.ZIDANE_DEBUG) {\n const cause = result.reason instanceof Error ? result.reason.message : String(result.reason)\n process.stderr.write(`[zidane/chat] failed to load session \"${ids[i]}\": ${cause}\\n`)\n }\n }\n return metas\n}\n\n/** Project a store header row to the picker's shape. */\nfunction metaFromRow(row: SessionMetaRow): SessionMeta {\n return {\n id: row.id,\n title: deriveSessionTitle(row.firstUserTurn ? [row.firstUserTurn] : [], row.metadata),\n turnCount: row.turnCount,\n userMessageCount: row.userMessageCount,\n runCount: row.runCount,\n ...(isMainSession(row.metadata) ? { isMain: true } : {}),\n ...metaSpace(row.metadata),\n ...(row.metadata?.favorite === true ? { favorite: true } : {}),\n ...(row.projectRoot ? { projectRoot: row.projectRoot } : {}),\n ...metaCwd(row.metadata),\n ...metaEnvironment(row.metadata),\n ...(typeof row.artifactCount === 'number' ? { artifactCount: row.artifactCount } : {}),\n updatedAt: row.updatedAt,\n }\n}\n\n/** Same projection from a fully materialized session (no header index). */\nfunction metaFromData(id: string, data: SessionData): SessionMeta {\n return {\n id,\n title: deriveSessionTitle(data.turns, data.metadata),\n // The out-of-band prompt snapshot (role `system`) is not a turn of the conversation.\n turnCount: data.turns.reduce((n, t) => t.role === 'system' ? n : n + 1, 0),\n userMessageCount: data.turns.reduce((n, t) => t.role === 'user' ? n + 1 : n, 0),\n runCount: data.runs.length,\n ...(isMainSession(data.metadata) ? { isMain: true } : {}),\n ...metaSpace(data.metadata),\n ...(data.metadata?.favorite === true ? { favorite: true } : {}),\n ...(data.projectRoot ? { projectRoot: data.projectRoot } : {}),\n ...metaCwd(data.metadata),\n ...metaEnvironment(data.metadata),\n updatedAt: data.updatedAt,\n }\n}\n\n/** The row's space id, when one other than Default is stamped (see `./spaces`). */\nfunction metaSpace(metadata: Record<string, unknown> | undefined): { spaceId?: string } {\n const spaceId = readSessionSpaceId(metadata)\n return spaceId ? { spaceId } : {}\n}\n\n/**\n * The environment summary for list rows — omitted for local sessions (the common case).\n *\n * @param metadata - Session metadata that may hold the environment record.\n */\nexport function sessionEnvironmentSummary(metadata: Record<string, unknown> | undefined): SessionEnvironmentSummary | undefined {\n if (!metadata?.[ENVIRONMENT_METADATA_KEY])\n return undefined\n const environment = readSessionEnvironment(metadata)\n if (environment.kind === LOCAL_ENVIRONMENT.kind && environment.state === 'ready')\n return undefined\n return {\n kind: environment.kind,\n state: environment.state,\n ...(environment.branch ? { branch: environment.branch } : {}),\n ...(environment.error ? { error: environment.error } : {}),\n ...(environment.suspended ? { suspended: true } : {}),\n ...(environment.execCwd ? { workspaceRoot: environment.execCwd } : {}),\n }\n}\n\nfunction metaEnvironment(metadata: Record<string, unknown> | undefined): { environment?: SessionEnvironmentSummary } {\n const environment = sessionEnvironmentSummary(metadata)\n return environment ? { environment } : {}\n}\n\nfunction metaCwd(metadata: Record<string, unknown> | undefined): { cwd?: string } {\n const cwd = metadata?.cwd\n return typeof cwd === 'string' && cwd.length > 0 ? { cwd } : {}\n}\n\n/**\n * One session's `SessionMeta` BY ID — the by-id complement to\n * `listSessionMeta`.\n *\n * For rows a filtered list cannot return: the orchestration main is\n * directory-less, so no project-scoped query yields it, and re-listing the\n * whole store on every sessions refresh just to find that one row is O(store).\n * Uses the store's indexed header read when it has one; degrades to a full\n * load otherwise. `null` = no such session.\n */\nexport async function getSessionMeta(store: SessionStore, id: string): Promise<SessionMeta | null> {\n if (store.getMeta) {\n try {\n const row = await store.getMeta(id)\n return row ? metaFromRow(row) : null\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG) {\n const cause = err instanceof Error ? err.message : String(err)\n process.stderr.write(`[zidane/chat] store.getMeta failed, falling back to a full load: ${cause}\\n`)\n }\n }\n }\n const data = await store.load(id)\n return data ? metaFromData(id, data) : null\n}\n\n/** The header fields both a store's `getMeta` row and a full load carry. */\nexport type SessionHeaderRead = Pick<SessionData, 'metadata' | 'projectRoot' | 'agentId' | 'updatedAt'> & { status?: SessionData['status'] }\n\n/**\n * One session's stored header (metadata, project root, agent, status)\n * without parsing its turns when the store keeps a header index\n * (`getMeta`); a full load otherwise, or when the indexed read fails.\n * Still a store read, so it sees other processes' writes. `null` = no such\n * session.\n *\n * @param store - Session store to read from.\n * @param id - Session id.\n */\nexport async function readSessionHeader(store: SessionStore, id: string): Promise<SessionHeaderRead | null> {\n if (store.getMeta) {\n try {\n return await store.getMeta(id)\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] store.getMeta failed, falling back to a full load: ${errorMessage(err)}\\n`)\n }\n }\n return await store.load(id)\n}\n\n/**\n * {@link SessionHeader} of a fully materialized session.\n *\n * @param data - The loaded session.\n */\nexport function headerFromData(data: SessionData): SessionHeader {\n return {\n ...metaFromData(data.id, data),\n ...(data.agentId ? { agentId: data.agentId } : {}),\n status: data.status,\n metadata: data.metadata,\n createdAt: data.createdAt,\n }\n}\n\n/**\n * One session's header, without reading its turns when the store keeps a\n * header index (`getMeta`). A full load when it doesn't, or when the title\n * still has to be derived from the first message.\n *\n * @param store - Session store to read from.\n * @param id - Session id.\n */\nexport async function getSessionHeader(store: SessionStore, id: string): Promise<SessionHeader | null> {\n const row = store.getMeta ? await store.getMeta(id).catch(() => null) : null\n const titled = typeof row?.metadata.title === 'string' && row.metadata.title.trim() !== ''\n if (row && row.status !== undefined && (titled || row.firstUserTurn !== undefined || row.userMessageCount === 0)) {\n return {\n ...metaFromRow(row),\n ...(row.agentId ? { agentId: row.agentId } : {}),\n status: row.status,\n metadata: row.metadata,\n ...(row.createdAt !== undefined ? { createdAt: row.createdAt } : {}),\n }\n }\n const data = await store.load(id)\n return data ? headerFromData({ ...data, id }) : null\n}\n\n/**\n * Derive a short title from the first user message — returns null when empty.\n *\n * @param turns - Persisted turns in order.\n */\nexport function titleFromTurns(turns: SessionTurn[]): string | null {\n const first = turns.find(t => t.role === 'user')\n if (!first)\n return null\n for (const block of first.content) {\n const title = block.type === 'text' && !block.systemNote ? titleFromText(block.text) : null\n if (title)\n return title\n }\n return null\n}\n\n/**\n * Fallback title for a session with no stored `metadata.title` and no\n * turns to derive one from yet — i.e. a freshly created session. Shown\n * in the sessions list, chat header, and details modal until the first\n * message (or an explicitly generated title) gives it a real name.\n */\nexport const DEFAULT_SESSION_TITLE = 'New session'\n\n/**\n * Display title for a session — preferred sources, in order:\n *\n * 1. `metadata.title` if it's a non-empty string (typically set by\n * the session-details modal's \"generate title\" action), on one line\n * ({@link oneLineTitle}): whatever wrote it, a title never wraps.\n * 2. {@link titleFromTurns} — the first user message, one-line + clipped.\n * 3. {@link DEFAULT_SESSION_TITLE} (`'New session'`).\n *\n * Total / pure / defensive on non-string metadata values.\n *\n * @param turns - The session's turns, used when no title is stored.\n * @param metadata - Session metadata that may hold a stored `title`.\n */\nexport function deriveSessionTitle(\n turns: SessionTurn[],\n metadata?: Record<string, unknown>,\n): string {\n const stored = typeof metadata?.title === 'string' ? oneLineTitle(metadata.title) : ''\n if (stored.length > 0)\n return stored\n return titleFromTurns(turns) ?? DEFAULT_SESSION_TITLE\n}\n\n/**\n * Map a `compact-summary` content block to its `compact-summary` StreamEvent —\n * the single source of truth for the compaction boundary card. Used by\n * {@link eventsFromTurns} (reload / TUI live repaint) AND by chat hosts that\n * surface the boundary live without a full reload (the GUI broadcasts this\n * from the loop's `compact:end` hook), so the live card and the reloaded card\n * are byte-identical regardless of who created the marker (manual compaction or\n * loop-native auto-compaction — both share this block shape).\n *\n * @param block - The `compact-summary` content block.\n * @param turnId - Id of the turn holding the block, attached to the event when set.\n */\nexport function compactSummaryEvent(\n block: Extract<SessionContentBlock, { type: 'compact-summary' }>,\n turnId?: string,\n): StreamEvent {\n return {\n kind: 'compact-summary',\n text: block.summary,\n compact: {\n replacedCount: block.replacesTurnIds.length,\n model: block.model,\n ...(block.usage.provider ? { providerKey: block.usage.provider } : {}),\n compactedAt: block.compactedAt,\n inputTokens: block.usage.input ?? 0,\n outputTokens: block.usage.output ?? 0,\n cacheReadTokens: block.usage.cacheRead ?? 0,\n cacheCreationTokens: block.usage.cacheCreation ?? 0,\n ...(typeof block.usage.cost === 'number' && block.usage.cost > 0 ? { costUsd: block.usage.cost } : {}),\n },\n ...(turnId ? { turnId } : {}),\n }\n}\n\n/**\n * Decoded byte size of a base64 payload, computed arithmetically.\n * `Buffer.from(data, 'base64').length` decodes the whole attachment —\n * a full allocation per media block on EVERY `eventsFromTurns` replay\n * (session load/switch/export/search) just to label a chip with a size.\n */\nfunction base64DecodedSize(data: string): number {\n const padding = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0\n return Math.floor(data.length * 3 / 4) - padding\n}\n\n/** Severity values a persisted notice may carry. Anything else is ignored. */\nconst NOTICE_SEVERITIES = new Set(['info', 'warn', 'error'])\n\n/** A persisted notice, plus the origin the live event carried. */\ninterface PersistedNotice {\n notice: NonNullable<StreamEvent['notice']>\n /** Subagent that produced it, when the live event named one. */\n childId?: string\n /** Indent depth the live event rendered at. */\n depth?: number\n}\n\n/**\n * Read a persisted `metadata.command.notice` back into the event shape, or\n * `null` when the block is an ordinary command detail. Defensive because the\n * value round-tripped through JSON written by a possibly older build.\n */\nfunction readNoticeMeta(value: unknown): PersistedNotice | null {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return null\n const { severity, label, childId, depth } = value as {\n severity?: unknown\n label?: unknown\n childId?: unknown\n depth?: unknown\n }\n if (typeof severity !== 'string' || !NOTICE_SEVERITIES.has(severity))\n return null\n if (typeof label !== 'string' || label === '')\n return null\n return {\n notice: { severity: severity as NonNullable<StreamEvent['notice']>['severity'], label },\n ...(typeof childId === 'string' && childId !== '' ? { childId } : {}),\n ...(typeof depth === 'number' && Number.isFinite(depth) && depth >= 0 ? { depth: Math.floor(depth) } : {}),\n }\n}\n\n/** The notice a command block carries, or `null` for an ordinary detail block. */\nfunction noticeFromBlock(block: SessionTurn['content'][number]): PersistedNotice | null {\n if (block.type !== 'tool_result')\n return null\n const meta = block.metadata?.command\n if (!meta || typeof meta !== 'object' || Array.isArray(meta))\n return null\n return readNoticeMeta((meta as Record<string, unknown>).notice)\n}\n\n/**\n * Persisted form of a harness notice: a display-only `role: 'command'` turn,\n * which is the existing carrier for content that must survive a reload without\n * ever reaching the model.\n *\n * Pairs with the `readNoticeMeta` branch in {@link eventsFromTurns}. Callers\n * pre-mint `turnId` and emit the live event with the same id so the live row\n * and the reloaded row are the same row.\n *\n * `origin` carries the live event's `childId` / `depth`. A subagent notice is\n * persisted by the PARENT's depth-0 run, so without it the run-derived tag in\n * `eventsFromTurns` would reload the row un-indented and unattributed.\n */\nexport function noticeTurnContent(\n notice: NonNullable<StreamEvent['notice']>,\n text: string,\n origin: { childId?: string, depth?: number } = {},\n): SessionTurn['content'] {\n return [{\n type: 'tool_result' as const,\n callId: `notice_${crypto.randomUUID()}`,\n output: text,\n metadata: {\n command: {\n notice: {\n severity: notice.severity,\n label: notice.label,\n ...(origin.childId ? { childId: origin.childId } : {}),\n ...(origin.depth !== undefined ? { depth: origin.depth } : {}),\n },\n },\n },\n }]\n}\n\n/** The tag every event of one turn carries: its `turnId`, plus its subagent lane when nested. */\ninterface ReplayTag { turnId: string, childId?: string, depth?: number }\n\n/** What the per-turn replay reads and advances, turn after turn. */\ninterface ReplayState {\n events: StreamEvent[]\n runById: ReadonlyMap<string, SessionRun>\n toolByCallId: ReadonlyMap<string, string>\n resultByCallId: ReadonlyMap<string, ReplayedResult>\n /** Depth of the last turn that emitted events, `-1` before any. Decides separators between top-level turns. */\n lastDepthAtEmission: number\n // The running `/` command's invocation (`/commit args`) until the next typed prompt.\n commandInvocation: string | null\n}\n\n/** Attachment chips for a typed prompt, reconstructed from the turn's sibling blocks. */\nfunction promptAttachments(turn: SessionTurn, block: SessionContentBlock): { name: string, mediaType: string, size: number }[] {\n // Reconstruct attachment metadata from sibling content\n // blocks so the transcript shows chips after session reload.\n const attachments: { name: string, mediaType: string, size: number }[] = []\n for (const sibling of turn.content) {\n if (sibling.type === 'image' || sibling.type === 'audio' || sibling.type === 'video') {\n attachments.push({\n name: sibling.name ?? sibling.type,\n mediaType: sibling.mediaType,\n size: typeof sibling.data === 'string'\n ? base64DecodedSize(sibling.data)\n : 0,\n })\n }\n else if (sibling.type === 'document') {\n // Documents are structured blocks, not <attachment>-tagged\n // text — surface their own chip. Covers base64 (e.g. a PDF\n // prompt) AND text encoding (the shape `paste.txt` /\n // dragged-text-file attachments persist as); text-encoded\n // ones get a chip too.\n attachments.push({\n name: sibling.name ?? 'document',\n mediaType: sibling.mediaType,\n size: typeof sibling.data !== 'string'\n ? 0\n : sibling.encoding === 'text'\n ? Buffer.byteLength(sibling.data)\n : base64DecodedSize(sibling.data),\n })\n }\n else if (sibling !== block && sibling.type === 'text') {\n const m = sibling.text.match(/^<attachment\\s+(?:name=\"([^\"]+)\"\\s*)?(?:media_type=\"([^\"]+)\")?/)\n if (m) {\n attachments.push({\n name: m[1] ?? 'attachment',\n mediaType: m[2] ?? 'text/plain',\n size: sibling.text.length,\n })\n }\n }\n }\n return attachments\n}\n\n/** Replay one non-blank text block of a user turn. */\nfunction replayUserText(\n state: ReplayState,\n turn: SessionTurn,\n block: Extract<SessionContentBlock, { type: 'text' }>,\n depth: number,\n tag: ReplayTag,\n marker: { emitted: boolean },\n): void {\n const { events } = state\n // A system note is the harness talking to the model, not the user: no\n // row at all (the run receipt names the agent; the rest is the model's).\n if (isSystemNoteBlock(block))\n return\n\n // Background-task completion notifications ride as text\n // blocks carrying `<task-notification>` XML (the format\n // `renderTaskNotificationXml` produces). Detect and emit\n // them as their own structured event so the TUI can render\n // a banner — and IMPORTANT: DON'T also emit a `user-prompt`\n // event for the same text, otherwise the raw XML would\n // double-paint alongside the banner. Same dedupe pattern\n // the compact-summary path uses.\n // Pass the FULL tag (turnId + optional childId/depth) so a\n // subagent's task notification renders under the right\n // `child-N` lane instead of bleeding up to depth 0.\n const taskEvent = parseTaskNotificationBlock(block.text, tag)\n if (taskEvent) {\n events.push(taskEvent)\n return\n }\n\n // Cross-session event delivered to a main session. Same dedupe\n // rule as the task notification above: emit the structured row,\n // and DON'T also emit the `user-prompt` echo of the same text —\n // the model-facing `<session-event>` block is not a user message\n // and must not paint as one.\n const sessionEvent = parseSessionEventXml(block.text)\n if (sessionEvent) {\n events.push({ kind: 'session-event', text: sessionEvent.summary, sessionEvent, ...tag })\n return\n }\n\n // For child runs the \"user\" text is the spawn task itself, already\n // rendered as the `spawn-start` marker above. Drop the duplicate.\n //\n // `user-prompt` carries the **raw** text; the renderer prepends the\n // `❯ ` chevron. No prefix in the payload means `refs` offsets stay\n // raw (no shift), and consumers building user-message indices or\n // history can read `event.text` directly.\n if (depth === 0) {\n // Inside a `/` command nothing opens a new exchange: the prompt\n // a command sub-run was given is a step of the command's work\n // (label = its short `display`, body = the full prompt), and a\n // note the command left for the model (`turn.display`, no run)\n // is its receipt line, or nothing for a model-only note (`''`).\n // Same shapes the live hosts emit.\n const commandOrigin = turn.runId ? state.runById.get(turn.runId)?.origin : undefined\n if (commandOrigin?.kind === 'command') {\n events.push({\n kind: 'command-detail',\n label: commandOrigin.display ? commandStepLabel(commandOrigin.display) : 'prompt',\n text: block.text,\n ...tag,\n })\n return\n }\n if (turn.display !== undefined && !turn.runId) {\n if (!marker.emitted && turn.display !== '')\n events.push({ kind: 'command-note', text: commandNoteText(turn.display, block.text, state.commandInvocation ?? undefined), ...tag })\n marker.emitted = true\n return\n }\n state.commandInvocation = null\n\n // Separator BEFORE the user prompt — matches the live\n // behavior, which only appends a separator when committing a\n // new prompt. Firing on every depth-0 turn pair would insert a\n // separator between every mid-exchange assistant tool turn and\n // its paired user-side tool_result turn, producing a 2-row gap\n // between consecutive `todowrite` calls in the replayed\n // transcript that is absent during the live cascade.\n if (state.lastDepthAtEmission === 0)\n events.push({ kind: 'separator', text: '' })\n\n const attachments = promptAttachments(turn, block)\n\n // Mentions typed through a completion are not persisted: recover the file chips from the text.\n const refs = inferPromptRefs(block.text)\n events.push({\n kind: 'user-prompt',\n text: block.text,\n turnId: turn.id,\n ...(refs.length > 0 ? { refs } : {}),\n ...(attachments.length > 0 ? { attachments } : {}),\n })\n }\n}\n\n/** Replay a user turn: typed prompts, notifications, tool results, compaction markers. */\nfunction replayUserTurn(state: ReplayState, turn: SessionTurn, depth: number, tag: ReplayTag): void {\n const { events } = state\n // A display-marker turn (command sub-run prompt, or a context note)\n // emits its `❯ marker` echo ONCE, even across multiple text blocks.\n const marker = { emitted: false }\n const attachmentOnly = !turn.content.some(block => block.type === 'text' && !block.systemNote && block.text.trim())\n && turn.content.some(block => ['document', 'image', 'audio', 'video'].includes(block.type))\n const displayContent: SessionContentBlock[] = attachmentOnly\n ? [{ type: 'text', text: '[Attachment]' }, ...turn.content]\n : turn.content\n for (const block of displayContent) {\n if (block.type === 'text' && (block.text.trim() || block.systemNote?.label)) {\n replayUserText(state, turn, block, depth, tag, marker)\n }\n else if (block.type === 'tool_result') {\n const tool = state.toolByCallId.get(block.callId)\n // Spawn tool-results duplicate the spawn-end marker's `Tokens: …`\n // line — and on older sessions its \"N in / M out\" shape\n // disagrees with the cache-aware spawn-end. Strip it at the\n // display layer; the persisted output stays intact.\n const raw = toolResultText(block.output)\n const text = tool === 'spawn' ? stripSpawnTokensLine(raw) : raw\n // Calls the tool made (codemode's `tools.*`), as the live run streamed them.\n if (block.calls?.length)\n events.push(...nestedRecordEvents(block.calls, block.callId, tag))\n events.push({\n kind: 'tool-result',\n text,\n ...(tool ? { tool } : {}),\n callId: block.callId,\n // Same error bit as the live event, so a resync doesn't flip a failed call to ok.\n ...(block.isError ? { isError: true as const } : {}),\n ...(block.durationMs !== undefined ? { durationMs: block.durationMs } : {}),\n ...tag,\n })\n }\n else if (block.type === 'compact-summary') {\n // Compaction boundary — single event per marker block, via the\n // shared `compactSummaryEvent` mapper so the reloaded card matches\n // the live one byte-for-byte. The renderer reads `compact` for the\n // metadata badge; `text` carries the summary (the TUI keeps it\n // hidden by default so it doesn't read like new dialogue). Subagents\n // never produce these markers (compaction targets the top-level\n // conversation), so the ancestry tag isn't threaded — but `turnId`\n // is, so select-turn mode still highlights.\n events.push(compactSummaryEvent(block, turn.id))\n }\n }\n}\n\n/** The event a command turn's `tool_result` block replays as: a harness notice, a custom render, or a detail. */\nfunction commandToolResultEvent(block: Extract<SessionContentBlock, { type: 'tool_result' }>, tag: ReplayTag): StreamEvent {\n const meta = block.metadata?.command\n const commandMeta = (meta && typeof meta === 'object' && !Array.isArray(meta))\n ? meta as Record<string, unknown>\n : undefined\n // Harness notice (model fallback, repeat-guard trip, run error).\n // Replayed into the same `info` / `error` event the live path\n // emits, so a reopened conversation shows the identical row in\n // the same place instead of losing every harness event.\n const persistedNotice = readNoticeMeta(commandMeta?.notice)\n if (persistedNotice) {\n return {\n kind: persistedNotice.notice.severity === 'error' ? 'error' : 'info',\n text: toolResultText(block.output),\n notice: persistedNotice.notice,\n ...tag,\n // Origin wins over the run-derived tag: a subagent notice is\n // persisted by the parent's depth-0 run, so the tag alone would\n // reload it flat instead of indented under its child.\n ...(persistedNotice.childId ? { childId: persistedNotice.childId } : {}),\n ...(persistedNotice.depth !== undefined ? { depth: persistedNotice.depth } : {}),\n }\n }\n // Custom-rendered block: re-bind the renderer id + data; the\n // persisted output is the fallback (see commandBlocksToContent).\n const customMeta = commandMeta?.custom\n if (customMeta && typeof customMeta === 'object' && !Array.isArray(customMeta)\n && typeof (customMeta as { renderer?: unknown }).renderer === 'string') {\n // Persisted metadata is JsonValue by construction (see\n // commandBlocksToContent) — the cast re-narrows what JSON\n // round-tripping already guarantees.\n const bound = customMeta as { renderer: string, data?: JsonValue }\n return {\n kind: 'custom',\n text: toolResultText(block.output),\n custom: { renderer: bound.renderer, ...(bound.data !== undefined ? { data: bound.data } : {}) },\n ...tag,\n }\n }\n const label = typeof commandMeta?.label === 'string' ? commandMeta.label : undefined\n return {\n kind: 'command-detail',\n text: toolResultText(block.output),\n ...(label ? { label } : {}),\n ...(commandMeta?.syntax === 'diff' ? { syntax: 'diff' as const } : {}),\n ...tag,\n }\n}\n\n/** Replay a display-only `role: 'command'` turn. */\nfunction replayCommandTurn(state: ReplayState, turn: SessionTurn, tag: ReplayTag): void {\n const { events } = state\n // Display-only local output from a `/` command (never sent to the\n // model). Delimit from the prior exchange, then replay each block in\n // the same shape the live `emit()` path appends, so reloaded and live\n // command output look identical: plain text → full markdown;\n // `tool_result` blocks (detail output — diffs, logs) → a\n // `command-detail` event: dim label + line-capped body.\n //\n // Notice turns are exempt from the delimiter: live they are appended\n // straight into the running transcript with no separator, and\n // `marginTopFor` collapses consecutive ones into a single block. Pushing\n // one here would reload a burst of harness events as a double-spaced\n // ladder, and put a break mid-exchange that never existed live.\n //\n // The invocation header (`` `❯ /commit` ``, the first block of a\n // command's first turn) replays as the user's prompt; the command's\n // later turns continue that exchange, so they open no new one.\n const noticeOnly = turn.content.length > 0 && turn.content.every(block => noticeFromBlock(block) !== null)\n const first = turn.content[0]\n const invocation = first?.type === 'text' ? parseCommandHeader(first.text) : null\n if (invocation !== null)\n state.commandInvocation = invocation\n if (state.lastDepthAtEmission === 0 && !noticeOnly && (invocation !== null || state.commandInvocation === null))\n events.push({ kind: 'separator', text: '' })\n for (const block of turn.content) {\n if (invocation !== null && block === first) {\n events.push({ kind: 'user-prompt', text: invocation, synthetic: true, turnId: turn.id })\n }\n else if (block.type === 'text' && block.text.trim()) {\n events.push({ kind: 'markdown', text: block.text, streaming: false, ...tag })\n }\n else if (block.type === 'tool_result') {\n events.push(commandToolResultEvent(block, tag))\n }\n }\n}\n\n/** The `tool` event an assistant `tool_call` block replays as. */\nfunction toolCallEvent(\n block: Extract<SessionContentBlock, { type: 'tool_call' }>,\n resultByCallId: ReadonlyMap<string, ReplayedResult>,\n tag: ReplayTag,\n): StreamEvent {\n // Stable-tools dispatch persists the `call_tool { name, arguments }`\n // envelope (wire-valid history), but the LIVE `tool:before` hook\n // fired on the unwrapped inner call. Unwrap here too so a reloaded\n // transcript shows \"Coolify projects\" with its real arguments, not\n // \"Call tool\" — byte-for-byte the same event the live run emitted.\n const inner = unwrapCallTool(block.name, block.input)\n const { name, input } = inner ?? block\n // Historical replay has no pre-write snapshot for `write_file`\n // (the captured priorContent only exists at live-hook time).\n // `extractEditPayload` falls back to `oldString: ''` so the\n // diff renders as an all-add view — matches git's \"new file\"\n // convention and is the most honest reconstruction available.\n let edit = extractEditPayload(name, input)\n if (edit) {\n // Attach per-edit outcomes parsed out of the paired tool_result\n // body so a replayed transcript shows the same applied / denied\n // / skipped badges the live run displayed. Falls through when\n // the body is unstructured (legacy results, error preamble) —\n // the renderer treats absent outcomes as \"all applied\".\n const resultText = resultByCallId.get(block.id)?.text\n if (resultText) {\n const outcomes = parseEditOutcomesFromResult(resultText)\n if (outcomes && outcomes.length === edit.hunks.length)\n edit = { ...edit, outcomes }\n }\n }\n // Model decision: the verdict (approve or deny) is on the call's result metadata.\n const approval = resultByCallId.get(block.id)?.approval\n return {\n kind: 'tool',\n text: toolCallPreview(name, input),\n tool: name,\n input,\n ...(edit ? { edit } : {}),\n callId: block.id,\n ...(approval ? { approval } : {}),\n ...tag,\n }\n}\n\n/** Replay an assistant turn: text, reasoning, tool calls, server-tool blocks. */\nfunction replayAssistantTurn(state: ReplayState, turn: SessionTurn, tag: ReplayTag): void {\n const { events } = state\n // A cited Claude reply is persisted one text block per cited span: replay\n // it as the single message the live stream showed (`mergeReplyText`), not\n // one message per fragment.\n for (const block of mergeReplyText(turn)) {\n if (block.type === 'text' && block.text.trim()) {\n // Persisted turns are finalized — `streaming: false` so the markdown\n // renderer commits to the final layout (no heal pass needed).\n events.push({ kind: 'markdown', text: block.text, streaming: false, ...tag })\n }\n else if (block.type === 'thinking' && block.text.trim()) {\n // Reasoning is persisted as part of the assistant content (the\n // provider needs it echoed back), so replay it the same way the\n // live `stream:thinking` hook does: one event per line. The TUI\n // renderer gives consecutive thinking rows a fixed icon gutter so\n // replayed and streamed reasoning look identical. Without this,\n // reloading a session drops every trace.\n //\n // Blank lines are SKIPPED: a paragraph break inside one thinking\n // block would otherwise replay as an empty thinking event — a\n // bare icon row with no text, one per paragraph, in every\n // renderer (the GUI's \"double icon\" artifact).\n for (const line of block.text.split('\\n')) {\n if (line.trim())\n events.push({ kind: 'thinking', text: line, ...tag })\n }\n }\n else if (block.type === 'tool_call') {\n events.push(toolCallEvent(block, state.resultByCallId, tag))\n }\n else if (block.type === 'server_tool_use') {\n // Native server-tool invocation (Anthropic `web_search`). Live runs\n // emit the same shape from `stream:server_tool_use`; replay this\n // way so the transcript looks identical on reload.\n events.push({\n kind: 'tool',\n text: toolCallPreview(block.name, block.input),\n tool: block.name,\n input: block.input,\n callId: block.id,\n ...tag,\n })\n }\n else if (block.type === 'server_tool_result') {\n events.push({\n kind: 'tool-result',\n text: serverToolResultSummary(block.content),\n tool: block.toolName,\n callId: block.toolUseId,\n ...tag,\n })\n }\n }\n}\n\n/**\n * Replays persisted turns as a viewable transcript. Equivalent to `replayTurns(turns, runs).events`.\n * Mirrors the event shape produced live by the agent hooks so loaded and\n * streaming history render identically, including subagent ancestry when\n * `runs` is supplied:\n * - Every turn carries a `runId`, looked up in `runs` to get the run's\n * `depth`. The resulting events carry `{ depth, childId }`, the same\n * shape the live `child:*` bubble hooks produce.\n * - `spawn-start` / `spawn-end` markers land at each child-run boundary,\n * so the transcript reads the same as a live run did.\n * - For child runs (`depth > 0`), the user-role \"task\" text is suppressed\n * because `spawn-start` already shows it.\n *\n * @param turns - Persisted turns in order.\n * @param runs - Runs the turns belong to. Without them every event has depth 0.\n */\nexport function eventsFromTurns(\n turns: SessionTurn[],\n runs: readonly SessionRun[] = [],\n): StreamEvent[] {\n return replayTurns(turns, runs).events\n}\n\n/**\n * The whole replay, and where each turn's events start in it\n * (`turnStarts[turns.length]` = the end). A page of turns `[from, to)` is\n * `events.slice(turnStarts[from], turnStarts[to])`: consecutive pages\n * concatenate to exactly {@link eventsFromTurns} (exchange separators, run\n * receipts, subagent markers, tool names and command state as the whole\n * replay has them), because what a run or the turn before decides lands in\n * the page of the turn whose replay emitted it.\n *\n * @param turns - Persisted turns in order.\n * @param runs - Runs the turns belong to. Pass `[]` for none.\n */\nexport function replayTurns(\n turns: SessionTurn[],\n runs: readonly SessionRun[],\n): { events: StreamEvent[], turnStarts: number[] } {\n const runById = indexReplayRuns(turns, runs)\n const labelFor = childLabeler(runById)\n const toolByCallId = toolNamesByCallId(turns)\n const resultByCallId = resultsByCallId(turns)\n const ancestryOf = runAncestry(runById)\n const subtreeTotals = runSubtreeTotals(runById)\n const spawnerOf = spawnerLabeler(runById, labelFor)\n\n // Index of the last turn in each child run's subtree (its own turns and\n // its descendants'). Concurrent siblings interleave their turns, so\n // \"the run changed\" is no boundary: a run's `spawn-end` lands once, after\n // this turn, and its `spawn-start` once, at its first turn. Diffing an\n // open-run stack per turn would close and reopen siblings at every\n // switch — one \"Finished\" marker (all carrying the final totals) per switch.\n const chainByTurn = turns.map(turn => ancestryOf(turn.runId))\n const lastSubtreeTurn = new Map<string, number>()\n chainByTurn.forEach((chain, idx) => {\n for (const run of chain)\n lastSubtreeTurn.set(run.id, idx)\n })\n\n const events: StreamEvent[] = []\n /** Child runs whose `spawn-start` has been emitted. */\n const startedRuns = new Set<string>()\n /** Started runs still awaiting their `spawn-end`, in open order. */\n const openRuns: SessionRun[] = []\n const state: ReplayState = { events, runById, toolByCallId, resultByCallId, lastDepthAtEmission: -1, commandInvocation: null }\n\n const pushSpawnEnd = (run: SessionRun) => {\n events.push(spawnEndEvent(run, subtreeTotals(run), labelFor, spawnerOf(run)))\n }\n\n const pushSpawnStart = (run: SessionRun) => {\n events.push(spawnStartEvent(run, labelFor, spawnerOf(run)))\n }\n\n /**\n * Emit `spawn-end` for every open run whose subtree ended before turn\n * `beforeTurn` — innermost (most recently opened) first, so a depth ≥ 2\n * chain closes child before parent. Lands right before the next turn's\n * events.\n */\n const closeFinishedRuns = (beforeTurn: number) => {\n for (let k = openRuns.length - 1; k >= 0; k--) {\n const run = openRuns[k]\n // Still running (a window reloaded mid-run): no \"finished\" marker, its\n // live events keep landing in an open box. Loads demote runs that died\n // with their process to `aborted` first (`demoteStaleRunningStatus`).\n if (run.status === 'running')\n continue\n if ((lastSubtreeTurn.get(run.id) ?? -1) < beforeTurn) {\n openRuns.splice(k, 1)\n pushSpawnEnd(run)\n }\n }\n }\n\n const turnStarts: number[] = []\n for (let i = 0; i < turns.length; i++) {\n turnStarts.push(events.length)\n const turn = turns[i]\n const targetChain = chainByTurn[i]\n const depth = targetChain.length // 0 for top-level, otherwise innermost depth\n\n closeFinishedRuns(i)\n // Open every not-yet-started run in the chain, outermost first.\n for (const run of targetChain) {\n if (startedRuns.has(run.id))\n continue\n startedRuns.add(run.id)\n openRuns.push(run)\n pushSpawnStart(run)\n }\n\n const subagentTag = depth > 0 && turn.runId ? { childId: labelFor(turn.runId), depth } : undefined\n // `turnId` tags every event so the TUI's select-turn mode can highlight\n // all events emitted by a single SessionTurn. Subagent ancestry stays in\n // a separate object so the spread order keeps existing tag overrides\n // (childId/depth) wins-last semantics intact.\n const tag = { turnId: turn.id, ...subagentTag }\n\n if (turn.role === 'user') {\n replayUserTurn(state, turn, depth, tag)\n state.lastDepthAtEmission = depth\n continue\n }\n\n if (turn.role === 'command') {\n replayCommandTurn(state, turn, tag)\n state.lastDepthAtEmission = depth\n continue\n }\n\n if (turn.role === 'assistant') {\n replayAssistantTurn(state, turn, tag)\n state.lastDepthAtEmission = depth\n }\n }\n\n // Drain any trailing open child runs (e.g. session ended mid-spawn). Pop\n // innermost-first so depth ≥ 2 chains emit `spawn-end` in the right order.\n closeFinishedRuns(turns.length)\n turnStarts.push(events.length)\n\n // A receipt lands after its run's last event: every turn starting past it moves down.\n return { events: withRunReceipts(events, turns, runs, turnStarts), turnStarts }\n}\n\n/**\n * Pattern matching the leading `<task-notification>` shape that the\n * agent's notification-injection path produces (see\n * `renderTaskNotificationXml` in `src/agent-task-notifications.ts`). Anchored so a stray\n * notification-shaped phrase mid-prompt doesn't trigger a false-positive\n * detection — the model's wire format always starts the user-turn\n * content block with this exact opening tag.\n */\nconst TASK_NOTIFICATION_RE = /^\\s*<task-notification>([\\s\\S]*?)<\\/task-notification>\\s*$/\n\n/**\n * Per-field extractors. Compiled once at module load (parsing happens\n * on every session replay, and a fresh `new RegExp` per pick burns\n * cycles for no reason).\n */\nconst TASK_NOTIFICATION_FIELD_RES = {\n taskId: /<task-id>([\\s\\S]*?)<\\/task-id>/,\n status: /<status>([\\s\\S]*?)<\\/status>/,\n exitCode: /<exit-code>([\\s\\S]*?)<\\/exit-code>/,\n command: /<command>([\\s\\S]*?)<\\/command>/,\n outputFile: /<output-file>([\\s\\S]*?)<\\/output-file>/,\n durationMs: /<duration-ms>([\\s\\S]*?)<\\/duration-ms>/,\n summary: /<summary>([\\s\\S]*?)<\\/summary>/,\n} as const\n\n/**\n * Detect a `<task-notification>` text block on replay and convert it\n * into a structured `task-notification` StreamEvent so the TUI's banner\n * renderer can paint it cleanly. Returns `null` when the block isn't a\n * notification (the caller falls back to the normal user-prompt path).\n *\n * Reads every field DIRECTLY from its tag — does NOT parse `<summary>`\n * for the underlying data. The summary is a derived display string,\n * not a source of truth: changing its format (localization, signal in\n * the label, whatever) must NEVER break replay. Each field falls back\n * to a safe default so older transcripts pre-dating a tag addition\n * still render.\n */\nfunction parseTaskNotificationBlock(\n text: string,\n tag: { turnId: string, childId?: string, depth?: number },\n): StreamEvent | null {\n const m = text.match(TASK_NOTIFICATION_RE)\n if (!m)\n return null\n\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\n const taskId = pick(TASK_NOTIFICATION_FIELD_RES.taskId) ?? '?'\n const statusRaw = pick(TASK_NOTIFICATION_FIELD_RES.status) ?? 'exited'\n const status: 'exited' | 'killed' = statusRaw === 'killed' ? 'killed' : 'exited'\n const exitCode = Number.parseInt(pick(TASK_NOTIFICATION_FIELD_RES.exitCode) ?? '0', 10) || 0\n const command = pick(TASK_NOTIFICATION_FIELD_RES.command) ?? ''\n const outputPath = pick(TASK_NOTIFICATION_FIELD_RES.outputFile) ?? ''\n const durationMs = Number.parseInt(pick(TASK_NOTIFICATION_FIELD_RES.durationMs) ?? '0', 10) || 0\n const summary = pick(TASK_NOTIFICATION_FIELD_RES.summary)\n ?? `${taskId} ${statusRaw}${exitCode ? ` ${exitCode}` : ''}`\n\n return {\n kind: 'task-notification',\n text: summary,\n task: { taskId, status, exitCode, outputPath, command, durationMs },\n turnId: tag.turnId,\n ...(tag.childId !== undefined ? { childId: tag.childId } : {}),\n ...(tag.depth !== undefined ? { depth: tag.depth } : {}),\n }\n}\n\n/**\n * Reverse of `escapeXml` for the small set of entities the writer\n * emits. Not a full HTML entity decoder — just enough to round-trip\n * `<` / `>` / `&` / quotes through persistence.\n *\n * `&amp;` MUST be replaced last, otherwise `&amp;lt;` becomes `&lt;`\n * then `<` (data corruption — silent double-decode).\n */\nfunction unescapeXml(s: string): string {\n return s\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, '\\'')\n .replace(/&amp;/g, '&')\n}\n\n/**\n * Formats a tool call as `name(json args)`, or just the name when the input is empty.\n *\n * @param name - Tool name.\n * @param input - The tool call's input.\n */\nexport function toolCallPreview(name: string, input: Record<string, unknown>): string {\n const args = JSON.stringify(input)\n return args && args !== '{}' ? `${name}(${args})` : name\n}\n\n/**\n * One-line display summary for a server-tool-result block (currently only\n * Anthropic's `web_search_tool_result`). `content` is either an error object\n * (`{ error_code: '...' }`) or an array of result entries — we count the\n * latter and surface the code for the former. Kept terse on purpose; richer\n * formatting (titles + URLs per hit) is a separate UI pass.\n *\n * Falls back to `(opaque result)` for unrecognized shapes rather than an\n * empty string — an empty `text` field would render as a blank row in the\n * transcript with no signal that anything happened.\n */\nexport function serverToolResultSummary(content: unknown): string {\n if (Array.isArray(content)) {\n const n = content.length\n return n === 1 ? '1 result' : `${n} results`\n }\n if (content && typeof content === 'object') {\n const code = (content as { error_code?: unknown }).error_code\n if (typeof code === 'string')\n return `error: ${code}`\n }\n return '(opaque result)'\n}\n\n/**\n * Update the trailing `'tool'` event matching `callId` so its\n * `edit.outcomes` reflects the canonical merged outcomes the chat\n * layer's `tool:transform` hook just computed. Without this, the live\n * diff badges stay frozen at whatever the gate set (approval-side\n * outcomes) and never reflect body-side failures from `multi_edit`'s\n * best-effort run — only a session reload re-attaches the merged\n * outcomes via {@link eventsFromTurns}.\n *\n * Walks back-to-front because the matching event is overwhelmingly the\n * most recent (we're updating right after the body returned). Returns\n * the same reference when no match is found (or the event already\n * carries the target outcomes), so React skips the re-render.\n *\n * @param events - The current events.\n * @param callId - Call id of the tool event to update.\n * @param outcomes - The merged edit outcomes.\n */\nexport function updateToolEventOutcomes(\n events: readonly StreamEvent[],\n callId: string,\n outcomes: readonly EditOutcome[],\n): StreamEvent[] {\n for (let i = events.length - 1; i >= 0; i--) {\n const e = events[i]\n if (e.kind !== 'tool' || e.callId !== callId || !e.edit)\n continue\n if (e.edit.outcomes === outcomes)\n return events as StreamEvent[]\n const next = events.slice()\n next[i] = { ...e, edit: { ...e.edit, outcomes } }\n return next\n }\n return events as StreamEvent[]\n}\n\n/**\n * Render tool output as plain text, whether it's a string or structured content.\n *\n * @param output - A tool result, as a string or structured content.\n */\nexport function toolResultText(output: string | ToolResultContent[]): string {\n return typeof output === 'string' ? output : toolResultToText(output)\n}\n\n/**\n * Strip the `Tokens: …` line from a spawn tool-result. The spawn-end marker\n * displayed right above already shows the same stats; keeping the line in the\n * rendered tool-result body just produces a visible duplicate (and, on\n * reloaded older sessions, an *inconsistent* duplicate — the persisted line\n * can use the `13 in / 4075 out` shape while the freshly synthesized\n * spawn-end uses the cache-aware `in 92615 (cache 92602) / 4075 out` shape).\n *\n * Display-only: the persisted tool_result content is untouched, so the LLM\n * still sees the full string in its context window. Anchored to start-of-line\n * and matches both `Tokens: 13 in / 4075 out` (older) and `Tokens: in 13 …`\n * (`formatTokenUsage`) shapes.\n *\n * @param text - A spawn tool-result body.\n */\nexport function stripSpawnTokensLine(text: string): string {\n // Strict: only strip when the `Tokens: …` line is **immediately preceded\n // by** the spawn-tool header (`[sub-agent <id>] (Completed|Aborted|Failed|Timed) …`).\n // A bare `^Tokens:` multi-line regex would eat any body line that\n // happened to start with `Tokens:` — e.g. a model summarizing\n // \"Tokens: …\" inside its response. Anchored matching keeps the strip\n // local to the spawn header even if the model's response continues with\n // arbitrary content.\n //\n // `m` + `g` so every header in a multi-spawn result is handled, not just\n // the one at index 0. Every spawn tool-result is single-spawn so\n // `g` is a no-op. The multiline anchor would also strip every header's\n // tokens line in a body that concatenates multiple spawn outputs.\n return text.replace(\n /(^\\[sub-agent [^\\n]+\\n)Tokens:[^\\n]*\\n?/gm,\n '$1',\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcript visibility + select-turn navigation live in the node-free\n// `./turn-selection` leaf so a browser-context renderer can import them via\n// `zidane/chat/pure`. Re-exported here (and through `zidane/tui`) so existing\n// `zidane/chat` consumers keep their import paths.\n// ---------------------------------------------------------------------------\nexport {\n BASH_CALL_ID_PREFIX,\n CACHE_NOTICE_LABEL,\n EDIT_TOOL_NAMES,\n isCacheNotice,\n isEditErrorResult,\n isTurnHighlighted,\n isUserBashEvent,\n isVisible,\n isVisibleDuringSearch,\n messageSelectionOwnership,\n promptHistoryFromEvents,\n retagUserPromptEvents,\n selectableTurnIds,\n selectionAfterTurnRemoval,\n streamEventSearchText,\n turnSelectionOwnership,\n} from './turn-selection'\n\n/**\n * Default top-margin per kind (in rows). Spacing intent:\n * - `info` / `markdown` / `tool` / `error` / `spawn-start` open a new\n * block, so they each get one row of breathing room above.\n * - `thinking` / `tool-result` / `spawn-end` continue the previous\n * block and stay flush.\n *\n * Context-aware overrides live in {@link marginTopFor}.\n */\nconst MARGIN_TOP: Record<StreamEvent['kind'], number> = {\n 'separator': 0,\n 'user-prompt': 1,\n 'info': 1,\n // A reasoning run opens with breathing room (like a markdown block); the\n // run's continuation lines collapse to 0 via the rule in `marginTopFor`.\n 'thinking': 1,\n 'tool': 1,\n 'tool-result': 0,\n // Command details open a labeled block of their own (like a tool call\n // line does), so they get the same one-row gap above.\n 'command-detail': 1,\n 'error': 1,\n 'markdown': 1,\n 'spawn-start': 1,\n 'spawn-end': 0,\n // Draws no row of its own (its spawn call's row shows the wait).\n 'spawn-queued': 0,\n // Compact-summary opens its own block; visually it's the \"before /\n // after\" hinge between elided history and current context, so it\n // deserves the breathing room a markdown / user-prompt event gets.\n 'compact-summary': 1,\n // Task-notification banners open a new logical block (a task is done,\n // here's what happened) so they get the same gap a `tool` line gets.\n // Consecutive task-notifications stack tight via the rule in\n // `marginTopFor` — a burst of completed tasks reads as one stacked\n // log column rather than scattered banners.\n 'task-notification': 1,\n // A worker report opens its own block, like a task notification does.\n 'session-event': 1,\n // One blank line above: the receipt is a footer set off from the prose it\n // totals. It paints its own blank line BELOW (see `RunSummaryLine`), and\n // `marginTopFor` gives the next block 0 so the gap stays exactly one row.\n 'run-summary': 1,\n // Custom-rendered rows are command output in spirit (a command or\n // extension chose the presentation) — same breathing room as\n // `command-detail` / `markdown`.\n 'custom': 1,\n // The command's receipt closes its output with one row of air, like the run receipt.\n 'command-note': 1,\n}\n\nconst TOOL_KINDS: ReadonlySet<StreamEvent['kind']> = new Set(['tool', 'tool-result'])\n\n/**\n * Resolve the top margin (in rows) for an event given the one rendered\n * just before it. Context-aware rules:\n *\n * - A `tool` / `tool-result` event right after another\n * `tool` / `tool-result` collapses to a tight list — call→result\n * pairs and back-to-back calls read as one logical block.\n * - Consecutive `task-notification` banners stack tight — a burst of\n * background-task completions reads as one log column rather than\n * scattered banners.\n *\n * NB: parent-level events (`depth === 0`) following a subagent block\n * (`depth > 0`) get their normal default margin. The subagent's\n * `🌳` end-marker does not provide enough visual separation to\n * collapse the transition to 0: a 2-cell emoji on the same row as the\n * close marker doesn't create a vertical break, and in\n * hide-subagent-output mode the SubagentBlock box isn't rendered\n * either, so the parent's follow-up markdown would be glued directly\n * against the close line. Subagents should READ like\n * tool calls: open with breathing room, close tight inside, and open\n * a clean gap before the parent's next thought — that's the contract\n * the tool-call → markdown transition uses, mirrored here.\n *\n * Renderer-agnostic — TUI uses it as Yoga `marginTop`; a CSS host can\n * use the same number as the row's top margin in `em` / `rem`.\n *\n * @param event - The event being rendered.\n * @param previous - The event rendered just above it, if any.\n */\nexport function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number {\n // First-block exception: spacing is single-direction (marginTop-only),\n // so the very first rendered block has no neighbor above to space\n // against — a gap there would just indent the transcript from its\n // container edge. Matches `SubagentBlock`'s `previous ? 1 : 0` rule so\n // every block kind starts flush.\n if (!previous)\n return 0\n // NB: an event directly after a `separator` keeps its own margin. A\n // separator is a semantic exchange boundary and paints zero rows (see\n // `EventLineImpl`'s `'separator'` case), so inter-block spacing is\n // exclusively the next block's margin. Collapsing the margin here too\n // would leave prompts with no gap at all.\n if (TOOL_KINDS.has(event.kind) && TOOL_KINDS.has(previous.kind))\n return 0\n // A spawn marker directly below the `spawn` tool call that created it —\n // or stacked under a sibling marker — reads as one fluid block, same rule\n // as consecutive tool rows. The gap only opens after prose/other kinds.\n if (event.kind === 'spawn-start' && previous\n && (TOOL_KINDS.has(previous.kind) || previous.kind === 'spawn-start' || previous.kind === 'spawn-end')) {\n return 0\n }\n if (event.kind === 'task-notification' && previous?.kind === 'task-notification')\n return 0\n // The run receipt paints its own trailing blank row so the last run of a\n // transcript still ends with one; the block after it must not add a second.\n if (previous.kind === 'run-summary')\n return 0\n // Consecutive notice rows stack tight, same rule as task banners. A burst of\n // harness events (a fallback, then the repeat-guard trip, then the run error)\n // reads as one block instead of a double-spaced ladder.\n if (event.notice && previous?.notice)\n return 0\n // Continuation lines of one reasoning run stack tight — only the first\n // thinking line (run start) gets the top gap.\n if (event.kind === 'thinking' && previous?.kind === 'thinking' && previous.childId === event.childId)\n return 0\n return MARGIN_TOP[event.kind] ?? 0\n}\n\n/**\n * Effective context size of the most recent assistant turn — drives the footer\n * indicator. Walk from the end of `turns` and return the cache-aware input-token total\n * of the most recent **parent** (depth-0) assistant turn. Subagent turns\n * are skipped because their context window is the child's, not the parent's\n * — surfacing a subagent's `usage.input` as the footer's \"context used\"\n * after a session ends mid-spawn would be misleading. The next prompt\n * feeds the parent, so the parent's last view is what matters.\n *\n * Zero-context placeholder turns are also skipped. `executeTurn`\n * synthesizes an assistant turn with `usage: { input: 0, output: 0 }`\n * when the provider stream throws / aborts before any usage is reported\n * (see `src/loop-turn-stream.ts`'s catch path) — that turn is purely a shape\n * contract for resume, not a real context-window measurement. Without\n * the skip, an abort mid-stream shadows the prior real turn and the\n * footer reads `ctx 0` on the next session load even though earlier\n * turns accumulated meaningful context (and `run.cost`).\n *\n * `runs` is optional. Without it depth is unknown, so every assistant turn\n * qualifies.\n *\n * @param turns - Persisted turns in order.\n * @param runs - Runs the turns belong to, used to skip subagent turns.\n */\nexport function lastContextSizeFromTurns(\n turns: SessionTurn[],\n runs: readonly SessionRun[] = [],\n): number {\n const childRunIds = new Set<string>()\n for (const run of runs) {\n if ((run.depth ?? 0) > 0)\n childRunIds.add(run.id)\n }\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]\n if (turn.role !== 'assistant' || !turn.usage)\n continue\n if (turn.runId && childRunIds.has(turn.runId))\n continue\n const size = (turn.usage.input ?? 0)\n + (turn.usage.cacheRead ?? 0)\n + (turn.usage.cacheCreation ?? 0)\n if (size === 0)\n continue\n return size\n }\n return 0\n}\n\n/**\n * Sum provider-reported USD cost across every run in a session. Used to\n * seed the live cost indicator on session activation. Returns 0 when no\n * run carries a `cost` (most providers — only OpenRouter reports today).\n *\n * @param runs - The session's runs.\n */\nexport function sumRunCosts(runs: readonly SessionRun[]): number {\n let total = 0\n for (const run of runs) {\n if (run.cost)\n total += run.cost\n }\n return total\n}\n\n/** What a run readout shows of the run in progress: see {@link runProgress}. */\nexport interface RunProgress {\n /** When the run began (epoch ms, its run record). */\n startedAt: number\n /** Output tokens of the turns it has settled, its subagents' left out (the live count adds parent `turn:end`s). */\n outputTokens: number\n}\n\n/**\n * The session's top-level run in progress: the latest `running` run without\n * a parent (subagent runs share the session).\n *\n * @param runs - The session's runs.\n */\nexport function runInProgress(runs: readonly SessionRun[]): SessionRun | undefined {\n return runs.findLast(run => run.status === 'running' && !run.parentRunId)\n}\n\n/**\n * The top-level run in progress as its agent saved it: when it began and the\n * output its settled turns produced. A window that missed `run:start`\n * (opened or reattached mid-run) starts its run readout from these, so the\n * time and the count don't restart at zero. `undefined` when no top-level\n * run is in progress.\n *\n * @param data - The session's runs and turns.\n */\nexport function runProgress(data: Pick<SessionData, 'runs' | 'turns'>): RunProgress | undefined {\n const run = runInProgress(data.runs)\n if (!run)\n return undefined\n let outputTokens = 0\n for (const turn of data.turns) {\n if (turn.runId === run.id)\n outputTokens += turn.usage?.output ?? 0\n }\n return { startedAt: run.startedAt, outputTokens }\n}\n\n/** Per-model token/spend totals across a session — see {@link usageByModel}. */\nexport interface ModelUsageSummary {\n /** Provider-echoed model id, or `\"unknown\"` for turns that never recorded one. */\n modelId: string\n /** Number of turns on this model. */\n turns: number\n /** Fresh (uncached) input tokens. */\n input: number\n /** Output tokens. */\n output: number\n /** Tokens read from the prompt cache. */\n cacheRead: number\n /** Tokens written to the prompt cache. */\n cacheCreation: number\n /** Summed USD cost across turns that carried one (provider-reported or estimated). */\n cost: number\n /**\n * `false` when at least one turn had no cost figure (no published rate\n * for the model) — `cost` is then a floor, not a total.\n */\n costComplete: boolean\n}\n\n/**\n * Aggregate per-model token + spend totals across every run in a session.\n * Parent and subagent runs alike: each turn carries its own `modelId`, so\n * cost-tiered delegation (cheap spawn workers) shows up as separate rows\n * priced at their own rates. Sorted by cost (desc), then total tokens, so\n * the biggest spender reads first.\n *\n * @param runs - The session's runs.\n */\nexport function usageByModel(runs: readonly SessionRun[]): ModelUsageSummary[] {\n const byModel = new Map<string, ModelUsageSummary>()\n for (const run of runs) {\n for (const u of run.turnUsage ?? []) {\n const key = u.modelId ?? 'unknown'\n let row = byModel.get(key)\n if (!row) {\n row = { modelId: key, turns: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0, cost: 0, costComplete: true }\n byModel.set(key, row)\n }\n row.turns += 1\n row.input += u.input\n row.output += u.output\n row.cacheRead += u.cacheRead ?? 0\n row.cacheCreation += u.cacheCreation ?? 0\n if (u.cost !== undefined)\n row.cost += u.cost\n else\n row.costComplete = false\n }\n }\n const totalTokens = (r: ModelUsageSummary) => r.input + r.output + r.cacheRead + r.cacheCreation\n return [...byModel.values()].sort((a, b) => (b.cost - a.cost) || (totalTokens(b) - totalTokens(a)))\n}\n"],"mappings":"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"}