@iowarp/clio-coder 0.3.2 → 0.3.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 (259) hide show
  1. package/CHANGELOG.md +269 -458
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +3 -3
  4. package/dist/{acp-BIYHVZIM.js → acp-S5R4RR5B.js} +7 -6
  5. package/dist/{agents-YT6SSRIT.js → agents-P6DMMVZY.js} +24 -21
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5TWEIYDN.js → auth-2XCZLPKS.js} +12 -8
  8. package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
  9. package/dist/{chunk-WMSVI4G2.js → chunk-2LZI5CAG.js} +133 -13
  10. package/dist/{chunk-OAO4GE4M.js → chunk-2TZWSW76.js} +2 -2
  11. package/dist/{chunk-OOJYHWRB.js → chunk-34475P3I.js} +2 -2
  12. package/dist/{chunk-WVO7V2QY.js → chunk-35MKKU5R.js} +4 -4
  13. package/dist/{chunk-LBNRH5WM.js → chunk-3HZ5RWN2.js} +5 -5
  14. package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
  15. package/dist/{chunk-MBS4V7ZP.js → chunk-4JUF2NNX.js} +7 -7
  16. package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
  17. package/dist/chunk-5M54SPOL.js +926 -0
  18. package/dist/{chunk-STBPMHSX.js → chunk-7RXG6QRZ.js} +51 -11
  19. package/dist/{chunk-77VKQEHF.js → chunk-A2GZF7DC.js} +5 -5
  20. package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
  21. package/dist/chunk-AOCYTWAV.js +449 -0
  22. package/dist/chunk-BEY543CS.js +258 -0
  23. package/dist/{chunk-6N5PTWMY.js → chunk-BP4OYD6A.js} +32 -13
  24. package/dist/chunk-BPGS2WCQ.js +612 -0
  25. package/dist/{chunk-J5HN4RYU.js → chunk-BRXQQJFP.js} +8 -8
  26. package/dist/chunk-CFGTUFWB.js +67 -0
  27. package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
  28. package/dist/{chunk-G2DE3C7R.js → chunk-EDRHSCIE.js} +4 -4
  29. package/dist/{chunk-4KLWL3UC.js → chunk-EFADSJET.js} +2 -2
  30. package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
  31. package/dist/chunk-FYYLNIL5.js +313 -0
  32. package/dist/{chunk-EPVUXGXG.js → chunk-HV5X7OR2.js} +14 -12
  33. package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
  34. package/dist/{chunk-IGLFWIYI.js → chunk-K6WL7QZT.js} +3 -3
  35. package/dist/chunk-K7VKOLQQ.js +15 -0
  36. package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
  37. package/dist/{chunk-V4RXGQ5Q.js → chunk-KRPY7NTG.js} +10 -7
  38. package/dist/chunk-LL4KHSZI.js +22 -0
  39. package/dist/{chunk-KJ5LWLOE.js → chunk-MEQ45TQ4.js} +15 -9
  40. package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
  41. package/dist/{chunk-AO4RKG4M.js → chunk-N4CZJQRK.js} +5 -5
  42. package/dist/{chunk-ARBGF5F7.js → chunk-NILBFAPG.js} +14 -8
  43. package/dist/chunk-OZNBF4L3.js +23 -0
  44. package/dist/{verify-G6V4D2G7.js → chunk-PCZJO5TI.js} +127 -42
  45. package/dist/chunk-QQK64KLB.js +1360 -0
  46. package/dist/{chunk-6EJV5X2W.js → chunk-QQL5RT5M.js} +979 -1619
  47. package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
  48. package/dist/{chunk-2EHAIA3X.js → chunk-RD5U66HV.js} +3 -3
  49. package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
  50. package/dist/{chunk-OQ33BKR3.js → chunk-TTNYS3EA.js} +3 -60
  51. package/dist/chunk-TW3WDMVS.js +677 -0
  52. package/dist/chunk-TZSKNMZG.js +434 -0
  53. package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
  54. package/dist/{chunk-7EYHLWU7.js → chunk-UZHIZC5S.js} +7 -7
  55. package/dist/{chunk-QTYWRVRA.js → chunk-VAWWTKDP.js} +8 -8
  56. package/dist/{chunk-X75S7HFS.js → chunk-VEZEGCGW.js} +214 -20
  57. package/dist/{chunk-OHHN2SO4.js → chunk-VMNQ6OZA.js} +98 -202
  58. package/dist/chunk-VSNATDE6.js +122 -0
  59. package/dist/chunk-W6GROXXM.js +69 -0
  60. package/dist/chunk-WPQLXFOZ.js +375 -0
  61. package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
  62. package/dist/{chunk-3ZXDFGR5.js → chunk-X6COSD2O.js} +5 -5
  63. package/dist/chunk-ZGVHUX3M.js +66 -0
  64. package/dist/{chunk-MAW544W2.js → chunk-ZWMF7253.js} +4 -4
  65. package/dist/{chunk-MQSRRFWA.js → chunk-ZYKPLLNQ.js} +563 -546
  66. package/dist/cli/index.js +27 -23
  67. package/dist/{clio-4LY5K2AC.js → clio-J5JIOIDS.js} +7 -6
  68. package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
  69. package/dist/{config-GTLUW2PR.js → config-OEBMIN2U.js} +37 -27
  70. package/dist/{configure-R6A64DHX.js → configure-PUQOSIXQ.js} +16 -13
  71. package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
  72. package/dist/{context-RW5HC47S.js → context-MGSE4Z2T.js} +33 -23
  73. package/dist/{context-JFZEJ7W5.js → context-URSXPBCK.js} +17 -9
  74. package/dist/{context-clear-6ZHBAZZT.js → context-clear-KDAJRNUK.js} +33 -23
  75. package/dist/context-working-set-SBKMPPI2.js +1552 -0
  76. package/dist/{dispatch-runner-VKBRCWQC.js → dispatch-runner-MSWN72NK.js} +43 -29
  77. package/dist/{doctor-KI767GSN.js → doctor-7BSE27PJ.js} +10 -10
  78. package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
  79. package/dist/{evidence-UA6AWDQQ.js → evidence-SR7WXB5B.js} +51 -23
  80. package/dist/{evolve-QNTFGV6Z.js → evolve-K7VE2CBX.js} +30 -20
  81. package/dist/{fleet-Q7UOMUSG.js → fleet-7XMJNQNF.js} +48 -38
  82. package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
  83. package/dist/{init-WBB65ZHQ.js → init-JGNPAYXT.js} +41 -31
  84. package/dist/{memory-MD3O64RI.js → memory-4ALKDJ4Q.js} +32 -22
  85. package/dist/{models-BZU34YWD.js → models-ZMMLFJNN.js} +22 -19
  86. package/dist/{monitor-MEQA5C3I.js → monitor-2F3T5KHP.js} +55 -43
  87. package/dist/{orchestrator-CGFKEP27.js → orchestrator-ORHT43JB.js} +2507 -1896
  88. package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
  89. package/dist/{run-IV4Q6RLN.js → run-RF4WJGMT.js} +51 -41
  90. package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
  91. package/dist/{skills-LQEKRDTN.js → skills-PSACKC5Q.js} +2 -2
  92. package/dist/{skills-eval-3DC4HEWS.js → skills-eval-WJSI55RZ.js} +34 -24
  93. package/dist/{targets-C4SSGQOB.js → targets-PIIRAOYS.js} +23 -20
  94. package/dist/{terminal-lease-IT5JW2NR.js → terminal-lease-ULWXWNVY.js} +5 -4
  95. package/dist/{upgrade-7TT7SQ3G.js → upgrade-346TZ6AV.js} +18 -17
  96. package/dist/{usage-GV4PKT3M.js → usage-6KKXR32N.js} +34 -24
  97. package/dist/verifiers-4UUM6TEE.js +1214 -0
  98. package/dist/verify-X5HDROLA.js +25 -0
  99. package/dist/{wiki-generate-DQF6Z66B.js → wiki-generate-7STOCIFZ.js} +42 -31
  100. package/dist/worker/entry.js +33 -24
  101. package/docs/README.md +8 -7
  102. package/docs/acp.md +1 -1
  103. package/docs/alcf-provider.md +1 -1
  104. package/docs/architecture.md +2 -2
  105. package/docs/artifact-versions.md +1 -1
  106. package/docs/built-in-agents.md +1 -1
  107. package/docs/capacity-and-scheduling.md +1 -1
  108. package/docs/commands-and-modes.md +53 -21
  109. package/docs/config-knobs-audit.md +1 -2
  110. package/docs/configuration-and-targets.md +15 -1
  111. package/docs/context-engine.md +64 -12
  112. package/docs/context-working-set.md +194 -0
  113. package/docs/development-pipeline.md +1 -1
  114. package/docs/documentation-coverage.md +5 -5
  115. package/docs/documentation-guide.md +6 -5
  116. package/docs/environment-variables.md +2 -1
  117. package/docs/eval-runner.md +1 -1
  118. package/docs/evals-internal.md +14 -1
  119. package/docs/evidence-and-memory.md +74 -2
  120. package/docs/evolution.md +1 -1
  121. package/docs/exit-codes-and-output.md +1 -1
  122. package/docs/extensions-and-sharing.md +2 -2
  123. package/docs/fleet-dispatch.md +22 -7
  124. package/docs/glossary.md +21 -1
  125. package/docs/installation-and-lifecycle.md +6 -6
  126. package/docs/middleware-and-components.md +1 -1
  127. package/docs/model-catalog.md +7 -9
  128. package/docs/observability.md +4 -4
  129. package/docs/performance-methodology.md +2 -2
  130. package/docs/proactive-memory.md +1 -1
  131. package/docs/prompt-envelope-and-tools.md +4 -4
  132. package/docs/provider-adapter-cookbook.md +1 -1
  133. package/docs/release-cut-checklist.md +35 -35
  134. package/docs/safety-model.md +23 -4
  135. package/docs/scientific-validation.md +21 -3
  136. package/docs/session-lifecycle.md +3 -3
  137. package/docs/skills-marketplace.md +1 -1
  138. package/docs/tool-usage.md +79 -12
  139. package/docs/trace-store.md +1 -1
  140. package/docs/troubleshooting.md +1 -1
  141. package/docs/tui-design.md +2 -2
  142. package/docs/worker-dispatch-mechanics.md +11 -1
  143. package/package.json +8 -11
  144. package/skills/meta/clio-test/SKILL.md +20 -17
  145. package/skills/meta/clio-test/evals.md +3 -3
  146. package/skills/meta/clio-test/references/harness.md +35 -6
  147. package/skills/meta/clio-test/references/test-map.md +20 -10
  148. package/skills/registry.yaml +2 -2
  149. package/skills/skill-marketplace.json +1 -1
  150. package/src/cli/context-working-set.ts +513 -0
  151. package/src/cli/context.ts +8 -0
  152. package/src/cli/evidence.ts +20 -2
  153. package/src/cli/index.ts +4 -0
  154. package/src/cli/verifiers.ts +325 -0
  155. package/src/core/bash-exec.ts +39 -14
  156. package/src/core/bus-events.ts +19 -4
  157. package/src/core/config.ts +54 -0
  158. package/src/core/defaults.ts +50 -3
  159. package/src/core/git-commit-attribution.ts +46 -21
  160. package/src/core/verification-scripts.ts +6 -0
  161. package/src/domains/agents/builtins/verifier.md +3 -0
  162. package/src/domains/config/classify.ts +1 -0
  163. package/src/domains/config/keybindings.ts +3 -3
  164. package/src/domains/context/working-set/contract.ts +161 -0
  165. package/src/domains/context/working-set/defaults.ts +28 -0
  166. package/src/domains/context/working-set/engine.ts +203 -0
  167. package/src/domains/context/working-set/fold.ts +62 -0
  168. package/src/domains/context/working-set/horizon.ts +38 -0
  169. package/src/domains/context/working-set/marker.ts +103 -0
  170. package/src/domains/context/working-set/path-index.ts +436 -0
  171. package/src/domains/context/working-set/payload.ts +152 -0
  172. package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
  173. package/src/domains/context/working-set/policies/index.ts +21 -0
  174. package/src/domains/context/working-set/policies/structural.ts +160 -0
  175. package/src/domains/context/working-set/project.ts +132 -0
  176. package/src/domains/context/working-set/protect.ts +109 -0
  177. package/src/domains/context/working-set/recall.ts +177 -0
  178. package/src/domains/context/working-set/replay/controls.ts +112 -0
  179. package/src/domains/context/working-set/replay/load-clio.ts +199 -0
  180. package/src/domains/context/working-set/replay/metrics.ts +185 -0
  181. package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
  182. package/src/domains/context/working-set/replay/report.ts +139 -0
  183. package/src/domains/context/working-set/replay/runner.ts +325 -0
  184. package/src/domains/context/working-set/replay/synthetic.ts +422 -0
  185. package/src/domains/context/working-set/replay/trace.ts +21 -0
  186. package/src/domains/context/working-set/visible.ts +54 -0
  187. package/src/domains/evidence/build.ts +112 -45
  188. package/src/domains/evidence/eval.ts +24 -7
  189. package/src/domains/evidence/index.ts +53 -0
  190. package/src/domains/evidence/ordering.ts +12 -0
  191. package/src/domains/evidence/run-trust.ts +221 -0
  192. package/src/domains/evidence/store.ts +46 -6
  193. package/src/domains/evidence/trust-status.ts +854 -0
  194. package/src/domains/evidence/types.ts +26 -0
  195. package/src/domains/middleware/memory-intervention.ts +3 -0
  196. package/src/domains/middleware/stalled-turn.ts +165 -4
  197. package/src/domains/safety/autonomy.ts +1 -1
  198. package/src/domains/safety/default-path-policy.ts +8 -0
  199. package/src/domains/safety/finish-contract.ts +4 -3
  200. package/src/domains/safety/policy-engine.ts +48 -6
  201. package/src/domains/session/compaction/compact.ts +23 -1
  202. package/src/domains/session/compaction/cut-point.ts +2 -0
  203. package/src/domains/session/compaction/tokens.ts +16 -1
  204. package/src/domains/session/context-ledger.ts +2 -0
  205. package/src/domains/session/entries.ts +107 -1
  206. package/src/domains/session/manager.ts +9 -2
  207. package/src/domains/session/migrations/index.ts +22 -3
  208. package/src/engine/acp/server.ts +3 -0
  209. package/src/engine/agent.ts +18 -1
  210. package/src/engine/session.ts +9 -3
  211. package/src/entry/orchestrator.ts +16 -4
  212. package/src/interactive/chat-loop-messages.ts +18 -6
  213. package/src/interactive/chat-panel.ts +571 -244
  214. package/src/interactive/chat-renderer.ts +79 -39
  215. package/src/interactive/context-meter.ts +10 -0
  216. package/src/interactive/context-overlay.ts +81 -6
  217. package/src/interactive/context-recall-command.ts +110 -0
  218. package/src/interactive/editor-submit.ts +26 -1
  219. package/src/interactive/footer/widgets.ts +22 -20
  220. package/src/interactive/footer-panel.ts +6 -1
  221. package/src/interactive/interactive-application.ts +2 -0
  222. package/src/interactive/interactive-event-projection.ts +12 -0
  223. package/src/interactive/interactive-slash-runtime.ts +49 -8
  224. package/src/interactive/model-session-replay.ts +21 -0
  225. package/src/interactive/overlay-general-openers.ts +6 -0
  226. package/src/interactive/overlay-session-lifecycle.ts +8 -4
  227. package/src/interactive/overlays/ask-user.ts +146 -24
  228. package/src/interactive/renderers/tool-execution.ts +167 -56
  229. package/src/interactive/session-transcript.ts +2 -2
  230. package/src/interactive/slash-commands.ts +29 -2
  231. package/src/interactive/status/index.ts +12 -1
  232. package/src/interactive/status/reasoning.ts +87 -0
  233. package/src/interactive/status/summary.ts +13 -2
  234. package/src/interactive/transcript-detail.ts +120 -0
  235. package/src/interactive/turn-context.ts +238 -88
  236. package/src/interactive/turn-middleware.ts +6 -6
  237. package/src/tools/agent-tools.ts +11 -4
  238. package/src/tools/bash.ts +144 -82
  239. package/src/tools/builtin-tool-catalog.ts +18 -6
  240. package/src/tools/context/index.ts +105 -3
  241. package/src/tools/context/surface.ts +3 -2
  242. package/src/tools/core-bootstrap.ts +21 -0
  243. package/src/tools/dispatch-runner.ts +9 -7
  244. package/src/tools/monitor.ts +28 -20
  245. package/src/tools/presentation.ts +107 -0
  246. package/src/tools/registry.ts +65 -7
  247. package/src/tools/result-disposition.ts +550 -0
  248. package/src/tools/result-shaping.ts +262 -19
  249. package/src/tools/safe-exec.ts +2 -0
  250. package/src/tools/verify/authoring.ts +1119 -0
  251. package/src/tools/verify/catalog.ts +346 -0
  252. package/src/tools/verify/index.ts +13 -3
  253. package/src/tools/verify/scripts.ts +135 -37
  254. package/src/tools/verify/surface.ts +9 -5
  255. package/src/tools/worker-evidence.ts +35 -12
  256. package/dist/chunk-MNA4JGU4.js +0 -255
  257. package/dist/chunk-SRF2PJNW.js +0 -184
  258. package/dist/chunk-T6YILFSB.js +0 -80
  259. package/dist/chunk-VAKQQHWR.js +0 -434
@@ -0,0 +1,1360 @@
1
+ import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
+ import {
3
+ probeWorkspace
4
+ } from "./chunk-G4BMMOKF.js";
5
+ import {
6
+ entryBelongsToPath
7
+ } from "./chunk-W6GROXXM.js";
8
+ import {
9
+ protectArtifact
10
+ } from "./chunk-MV3K5QF2.js";
11
+ import {
12
+ isSessionEntry,
13
+ isSessionHeader
14
+ } from "./chunk-BEY543CS.js";
15
+ import {
16
+ stripTokenizerSentinels
17
+ } from "./chunk-CFGTUFWB.js";
18
+ import {
19
+ CURRENT_SESSION_FORMAT_VERSION,
20
+ appendSessionFileEntry,
21
+ atomicWrite,
22
+ createSession,
23
+ cwdHash,
24
+ openSession,
25
+ readSessionFileEntries,
26
+ readSessionMeta,
27
+ resumeSession,
28
+ sessionPaths
29
+ } from "./chunk-FO5ZOVUY.js";
30
+ import {
31
+ clioStateDir,
32
+ stateRootRemoved
33
+ } from "./chunk-BNAZZHFG.js";
34
+ import {
35
+ BusChannels
36
+ } from "./chunk-TTNYS3EA.js";
37
+ import {
38
+ init_esm_shims
39
+ } from "./chunk-3R73A4XB.js";
40
+
41
+ // src/domains/session/compaction/session-entries.ts
42
+ init_esm_shims();
43
+ function collectSessionEntries(turns, filePath) {
44
+ const out = [];
45
+ for (const raw of turns) {
46
+ if (!isSessionEntry(raw)) {
47
+ throw new Error(
48
+ `session ledger contains an unreadable entry (missing kind discriminant): ${filePath}. Remove or compact the session to reset.`
49
+ );
50
+ }
51
+ out.push(raw);
52
+ }
53
+ return out;
54
+ }
55
+
56
+ // src/domains/session/archive-readers.ts
57
+ init_esm_shims();
58
+ import { readdir, readFile } from "node:fs/promises";
59
+ import { join as join3 } from "node:path";
60
+
61
+ // src/domains/session/index.ts
62
+ init_esm_shims();
63
+
64
+ // src/domains/session/extension.ts
65
+ init_esm_shims();
66
+
67
+ // src/domains/session/checkpoint.ts
68
+ init_esm_shims();
69
+ async function performCheckpoint(state, reason) {
70
+ await state.writer.persistTree();
71
+ if (stateRootRemoved()) return;
72
+ const paths = sessionPaths(state.meta);
73
+ const at = (/* @__PURE__ */ new Date()).toISOString();
74
+ const enriched = {
75
+ ...state.meta,
76
+ lastCheckpointAt: at,
77
+ lastCheckpointReason: reason ?? null
78
+ };
79
+ atomicWrite(paths.meta, JSON.stringify(enriched, null, 2));
80
+ Object.assign(state.meta, enriched);
81
+ }
82
+
83
+ // src/domains/session/history.ts
84
+ init_esm_shims();
85
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
86
+ import { join } from "node:path";
87
+ function listSessionsForCwd(cwd) {
88
+ const hash = cwdHash(cwd);
89
+ const dir = join(clioStateDir(), "sessions", hash);
90
+ if (!existsSync(dir)) return [];
91
+ const metas = [];
92
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
93
+ if (!entry.isDirectory()) continue;
94
+ const sessionDir = join(dir, entry.name);
95
+ const metaPath = join(sessionDir, "meta.json");
96
+ if (!existsSync(metaPath)) continue;
97
+ let meta;
98
+ try {
99
+ if (!statSync(metaPath).isFile()) continue;
100
+ const raw = readFileSync(metaPath, "utf8");
101
+ meta = JSON.parse(raw);
102
+ } catch {
103
+ continue;
104
+ }
105
+ enrichMetaForListing(meta, join(sessionDir, "current.jsonl"));
106
+ metas.push(meta);
107
+ }
108
+ metas.sort((a, b) => {
109
+ const aKey = a.lastActivityAt ?? a.createdAt ?? "";
110
+ const bKey = b.lastActivityAt ?? b.createdAt ?? "";
111
+ return aKey === bKey ? 0 : aKey > bKey ? -1 : 1;
112
+ });
113
+ return metas;
114
+ }
115
+ var PREVIEW_MAX_CHARS = 240;
116
+ function collapseWhitespace(text) {
117
+ return text.replace(/\s+/g, " ").trim();
118
+ }
119
+ function extractUserText(payload) {
120
+ if (typeof payload === "string") {
121
+ const collapsed = collapseWhitespace(payload);
122
+ return collapsed.length > 0 ? collapsed : null;
123
+ }
124
+ if (!payload || typeof payload !== "object") return null;
125
+ const obj = payload;
126
+ if (typeof obj.text === "string") {
127
+ const collapsed = collapseWhitespace(obj.text);
128
+ if (collapsed.length > 0) return collapsed;
129
+ }
130
+ if (Array.isArray(obj.content)) {
131
+ const parts = [];
132
+ for (const part of obj.content) {
133
+ if (part && typeof part === "object" && part.type === "text" && typeof part.text === "string") {
134
+ parts.push(part.text);
135
+ }
136
+ }
137
+ const joined = collapseWhitespace(parts.join(" "));
138
+ if (joined.length > 0) return joined;
139
+ }
140
+ return null;
141
+ }
142
+ function scanCurrentJsonl(currentPath) {
143
+ const result = {
144
+ firstUserMessage: null,
145
+ messageCount: 0,
146
+ lastTimestamp: null,
147
+ name: null,
148
+ labels: /* @__PURE__ */ new Map()
149
+ };
150
+ if (!existsSync(currentPath)) return result;
151
+ const records = readSessionFileEntries(currentPath).filter((entry) => !isSessionHeader(entry));
152
+ for (const parsed of collectSessionEntries(records, currentPath)) {
153
+ if (result.lastTimestamp === null || parsed.timestamp > result.lastTimestamp) {
154
+ result.lastTimestamp = parsed.timestamp;
155
+ }
156
+ if (parsed.kind === "message" && parsed.role === "user") {
157
+ result.messageCount += 1;
158
+ if (result.firstUserMessage === null) {
159
+ const text = extractUserText(parsed.payload);
160
+ if (text !== null) result.firstUserMessage = text;
161
+ }
162
+ }
163
+ if (parsed.kind === "sessionInfo") {
164
+ const info = parsed;
165
+ if (info.name !== void 0) {
166
+ const trimmed = info.name.trim();
167
+ result.name = trimmed.length > 0 ? trimmed : null;
168
+ }
169
+ if (info.targetTurnId && info.label !== void 0) {
170
+ const existing = result.labels.get(info.targetTurnId);
171
+ if (!existing || existing.timestamp <= info.timestamp) {
172
+ result.labels.set(info.targetTurnId, { label: info.label, timestamp: info.timestamp });
173
+ }
174
+ }
175
+ }
176
+ if (parsed.kind === "label") {
177
+ const label = parsed;
178
+ const existing = result.labels.get(label.targetTurnId);
179
+ if (!existing || existing.timestamp <= label.timestamp) {
180
+ result.labels.set(label.targetTurnId, { label: label.label ?? "", timestamp: label.timestamp });
181
+ }
182
+ }
183
+ }
184
+ return result;
185
+ }
186
+ function enrichMetaForListing(meta, currentPath) {
187
+ const scan = scanCurrentJsonl(currentPath);
188
+ if (scan.name) meta.name = scan.name;
189
+ const labelValues = [...scan.labels.values()].map((entry) => entry.label.trim()).filter((label) => label.length > 0);
190
+ if (labelValues.length > 0) meta.labels = labelValues;
191
+ if (scan.firstUserMessage) {
192
+ meta.firstMessagePreview = scan.firstUserMessage.length > PREVIEW_MAX_CHARS ? `${scan.firstUserMessage.slice(0, PREVIEW_MAX_CHARS - 1)}\u2026` : scan.firstUserMessage;
193
+ }
194
+ if (scan.messageCount > 0) meta.messageCount = scan.messageCount;
195
+ const fallbackMtime = readMtimeIso(currentPath);
196
+ const activity = scan.lastTimestamp ?? fallbackMtime ?? meta.endedAt ?? meta.createdAt ?? null;
197
+ if (activity) meta.lastActivityAt = activity;
198
+ }
199
+ function readMtimeIso(path) {
200
+ try {
201
+ return statSync(path).mtime.toISOString();
202
+ } catch {
203
+ return null;
204
+ }
205
+ }
206
+ function enrichForkMeta(meta, parentSessionId, parentTurnId) {
207
+ Object.assign(meta, { parentSessionId, parentTurnId });
208
+ const paths = sessionPaths(meta);
209
+ atomicWrite(paths.meta, JSON.stringify(meta, null, 2));
210
+ }
211
+
212
+ // src/domains/session/manager.ts
213
+ init_esm_shims();
214
+
215
+ // node_modules/uuid/dist-node/index.js
216
+ init_esm_shims();
217
+
218
+ // node_modules/uuid/dist-node/stringify.js
219
+ init_esm_shims();
220
+ var byteToHex = [];
221
+ for (let i = 0; i < 256; ++i) {
222
+ byteToHex.push((i + 256).toString(16).slice(1));
223
+ }
224
+ function unsafeStringify(arr, offset = 0) {
225
+ return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
226
+ }
227
+
228
+ // node_modules/uuid/dist-node/rng.js
229
+ init_esm_shims();
230
+ var rnds8 = new Uint8Array(16);
231
+ function rng() {
232
+ return crypto.getRandomValues(rnds8);
233
+ }
234
+
235
+ // node_modules/uuid/dist-node/v7.js
236
+ init_esm_shims();
237
+ var _state = {};
238
+ function v7(options, buf, offset) {
239
+ let bytes;
240
+ if (options) {
241
+ bytes = v7Bytes(options.random ?? options.rng?.() ?? rng(), options.msecs, options.seq, buf, offset);
242
+ } else {
243
+ const now = Date.now();
244
+ const rnds = rng();
245
+ updateV7State(_state, now, rnds);
246
+ bytes = v7Bytes(rnds, _state.msecs, _state.seq, buf, offset);
247
+ }
248
+ return buf ?? unsafeStringify(bytes);
249
+ }
250
+ function updateV7State(state, now, rnds) {
251
+ state.msecs ??= -Infinity;
252
+ state.seq ??= 0;
253
+ if (now > state.msecs) {
254
+ state.seq = rnds[6] << 23 | rnds[7] << 16 | rnds[8] << 8 | rnds[9];
255
+ state.msecs = now;
256
+ } else {
257
+ state.seq = state.seq + 1 | 0;
258
+ if (state.seq === 0) {
259
+ state.msecs++;
260
+ }
261
+ }
262
+ return state;
263
+ }
264
+ function v7Bytes(rnds, msecs, seq, buf, offset = 0) {
265
+ if (rnds.length < 16) {
266
+ throw new Error("Random bytes length must be >= 16");
267
+ }
268
+ if (!buf) {
269
+ buf = new Uint8Array(16);
270
+ offset = 0;
271
+ } else {
272
+ if (offset < 0 || offset + 16 > buf.length) {
273
+ throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
274
+ }
275
+ }
276
+ msecs ??= Date.now();
277
+ seq ??= rnds[6] * 127 << 24 | rnds[7] << 16 | rnds[8] << 8 | rnds[9];
278
+ buf[offset++] = msecs / 1099511627776 & 255;
279
+ buf[offset++] = msecs / 4294967296 & 255;
280
+ buf[offset++] = msecs / 16777216 & 255;
281
+ buf[offset++] = msecs / 65536 & 255;
282
+ buf[offset++] = msecs / 256 & 255;
283
+ buf[offset++] = msecs & 255;
284
+ buf[offset++] = 112 | seq >>> 28 & 15;
285
+ buf[offset++] = seq >>> 20 & 255;
286
+ buf[offset++] = 128 | seq >>> 14 & 63;
287
+ buf[offset++] = seq >>> 6 & 255;
288
+ buf[offset++] = seq << 2 & 255 | rnds[10] & 3;
289
+ buf[offset++] = rnds[11];
290
+ buf[offset++] = rnds[12];
291
+ buf[offset++] = rnds[13];
292
+ buf[offset++] = rnds[14];
293
+ buf[offset++] = rnds[15];
294
+ return buf;
295
+ }
296
+ var v7_default = v7;
297
+
298
+ // src/domains/session/migrations/index.ts
299
+ init_esm_shims();
300
+ var OLDEST_READABLE_SESSION_FORMAT_VERSION = 3;
301
+ function runMigrations(meta, sessionPath) {
302
+ const from = meta.sessionFormatVersion ?? 1;
303
+ if (from < OLDEST_READABLE_SESSION_FORMAT_VERSION) {
304
+ throw new Error(
305
+ `session metadata has an unsupported format version (expected version ${CURRENT_SESSION_FORMAT_VERSION}, got ${from}): ${sessionPath}. Remove the session directory to start a new session.`
306
+ );
307
+ }
308
+ if (from > CURRENT_SESSION_FORMAT_VERSION) {
309
+ throw new Error(
310
+ `session was written by a newer Clio (format version ${from}, this build reads version ${CURRENT_SESSION_FORMAT_VERSION}): ${sessionPath}. Upgrade clio-coder to resume this session.`
311
+ );
312
+ }
313
+ if (from < CURRENT_SESSION_FORMAT_VERSION) {
314
+ return { migrated: true, from, to: CURRENT_SESSION_FORMAT_VERSION };
315
+ }
316
+ return { migrated: false, from, to: from };
317
+ }
318
+
319
+ // src/domains/session/manager.ts
320
+ function newTurnId() {
321
+ return v7_default();
322
+ }
323
+ function startSession(input) {
324
+ const { meta, writer } = createSession({
325
+ cwd: input.cwd,
326
+ model: input.model ?? null,
327
+ target: input.target ?? null,
328
+ ...input.initialEntries !== void 0 ? { initialEntries: input.initialEntries } : {},
329
+ ...input.initialTree !== void 0 ? { initialTree: input.initialTree } : {},
330
+ ...input.parentSession !== void 0 ? { parentSession: input.parentSession } : {},
331
+ ...input.parentTurnId !== void 0 ? { parentTurnId: input.parentTurnId } : {}
332
+ });
333
+ return { meta, writer };
334
+ }
335
+ function persistSessionMeta(state) {
336
+ if (stateRootRemoved()) return;
337
+ atomicWrite(sessionPaths(state.meta).meta, JSON.stringify(state.meta, null, 2));
338
+ }
339
+ function resumeSessionState(sessionId) {
340
+ const persistedMeta = readSessionMeta(sessionId);
341
+ const paths = sessionPaths(persistedMeta);
342
+ const migration = runMigrations(persistedMeta, paths.meta);
343
+ const { meta, writer, tree } = resumeSession(sessionId);
344
+ const state = { meta, writer };
345
+ if (migration.migrated) {
346
+ state.meta.sessionFormatVersion = migration.to;
347
+ persistSessionMeta(state);
348
+ }
349
+ return { state, nodes: tree };
350
+ }
351
+ function appendTurn(state, input) {
352
+ const record = {
353
+ id: input.id ?? newTurnId(),
354
+ parentId: input.parentId,
355
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
356
+ kind: input.kind,
357
+ payload: input.payload
358
+ };
359
+ const entry = {
360
+ kind: "message",
361
+ turnId: record.id,
362
+ parentTurnId: record.parentId,
363
+ timestamp: record.at,
364
+ role: record.kind,
365
+ payload: record.payload
366
+ };
367
+ state.writer.appendEntry(entry, {
368
+ treeNode: {
369
+ id: record.id,
370
+ parentId: record.parentId,
371
+ at: record.at,
372
+ kind: record.kind
373
+ }
374
+ });
375
+ return record;
376
+ }
377
+ function appendEntry(state, input) {
378
+ const turnId = input.turnId ?? newTurnId();
379
+ const timestamp = input.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
380
+ const entry = { ...input, turnId, timestamp };
381
+ if (!isSessionEntry(entry)) throw new Error(`session.appendEntry: invalid ${String(input.kind)} entry`);
382
+ state.writer.appendEntry(entry);
383
+ return entry;
384
+ }
385
+ function replaceEntries(state, entries) {
386
+ for (const entry of entries) {
387
+ if (!isSessionEntry(entry)) throw new Error("session.replaceEntries: invalid entry");
388
+ }
389
+ state.writer.replaceEntries(entries);
390
+ }
391
+
392
+ // src/domains/session/tree/fork.ts
393
+ init_esm_shims();
394
+ function linkedRecordFromEntry(entry) {
395
+ return {
396
+ id: entry.turnId,
397
+ parentId: entry.parentTurnId,
398
+ timestamp: entry.timestamp,
399
+ treeKind: entry.kind === "message" ? entry.role : null,
400
+ raw: entry
401
+ };
402
+ }
403
+ function traceAncestry(records, leafTurnId) {
404
+ const byId = /* @__PURE__ */ new Map();
405
+ for (const record of records) byId.set(record.id, record);
406
+ const path = [];
407
+ let current = byId.get(leafTurnId);
408
+ if (!current) throw new Error(`session.fork: parent turn not found: ${leafTurnId}`);
409
+ const seen = /* @__PURE__ */ new Set();
410
+ while (current) {
411
+ if (seen.has(current.id)) throw new Error(`session.fork: cycle in parent chain at ${current.id}`);
412
+ seen.add(current.id);
413
+ path.unshift(current);
414
+ if (current.parentId === null) break;
415
+ const next = byId.get(current.parentId);
416
+ if (!next) throw new Error(`session.fork: broken parent chain at ${current.id}`);
417
+ current = next;
418
+ }
419
+ return path;
420
+ }
421
+ function treeFromLinearPath(path) {
422
+ return path.filter((record) => record.treeKind !== null).map((record) => ({
423
+ id: record.id,
424
+ parentId: record.parentId,
425
+ at: record.timestamp,
426
+ kind: record.treeKind
427
+ }));
428
+ }
429
+ function sessionEntryBelongsToPath(entry, pathIds, atOrBeforeForkPoint) {
430
+ if (entry.kind === "compactionSummary" && !atOrBeforeForkPoint) return false;
431
+ return entryBelongsToPath(entry, pathIds, atOrBeforeForkPoint);
432
+ }
433
+ function branchEntriesFromParent(parentMeta, leafTurnId) {
434
+ const parentCurrentPath = sessionPaths(parentMeta).current;
435
+ const records = readSessionFileEntries(parentCurrentPath).filter((entry) => !isSessionHeader(entry));
436
+ const parsed = collectSessionEntries(records, parentCurrentPath);
437
+ const linked = parsed.map(linkedRecordFromEntry);
438
+ const path = traceAncestry(linked, leafTurnId);
439
+ const pathIds = new Set(path.map((record) => record.id));
440
+ const forkPointIndex = parsed.findIndex((entry) => linkedRecordFromEntry(entry).id === leafTurnId);
441
+ const entries = parsed.filter(
442
+ (entry, index) => sessionEntryBelongsToPath(entry, pathIds, forkPointIndex >= 0 && index <= forkPointIndex)
443
+ );
444
+ return {
445
+ parentCurrentPath,
446
+ entries,
447
+ tree: treeFromLinearPath(path)
448
+ };
449
+ }
450
+ function forkFromState(input) {
451
+ const parentMeta = input.from.meta;
452
+ input.from.writer.flushAppends();
453
+ const branch = branchEntriesFromParent(parentMeta, input.parentTurnId);
454
+ const cwd = input.cwd ?? parentMeta.cwd;
455
+ const next = startSession({
456
+ cwd,
457
+ model: parentMeta.model,
458
+ target: parentMeta.target,
459
+ initialEntries: branch.entries,
460
+ initialTree: branch.tree,
461
+ parentSession: branch.parentCurrentPath,
462
+ parentTurnId: input.parentTurnId
463
+ });
464
+ enrichForkMeta(next.meta, parentMeta.id, input.parentTurnId);
465
+ void input.from.writer.close();
466
+ return { next, parentMeta, nodes: branch.tree };
467
+ }
468
+
469
+ // src/domains/session/tree/manager.ts
470
+ init_esm_shims();
471
+ import { existsSync as existsSync2, renameSync, rmSync } from "node:fs";
472
+ import { dirname, join as join2 } from "node:path";
473
+ function readTreeBundle(sessionId) {
474
+ const reader = openSession(sessionId);
475
+ const meta = reader.meta();
476
+ const paths = sessionPaths(meta);
477
+ const nodes = [...reader.tree()];
478
+ const entries = collectSessionEntries(reader.turns(), paths.current);
479
+ const labels = resolveLabelMap(entries);
480
+ return { sessionId, meta, nodes, entries, labels };
481
+ }
482
+ function resolveLabelMap(entries) {
483
+ const out = /* @__PURE__ */ new Map();
484
+ for (const entry of entries) {
485
+ const info = labelFields(entry);
486
+ if (!info) continue;
487
+ const existing = out.get(info.targetTurnId);
488
+ if (existing && existing.timestamp > info.timestamp) continue;
489
+ if (info.label === void 0 || info.label === "") {
490
+ out.set(info.targetTurnId, { label: "", timestamp: info.timestamp });
491
+ continue;
492
+ }
493
+ out.set(info.targetTurnId, { label: info.label, timestamp: info.timestamp });
494
+ }
495
+ return out;
496
+ }
497
+ function labelFields(entry) {
498
+ if (entry.kind === "label") return entry;
499
+ if (entry.kind !== "sessionInfo") return null;
500
+ const info = entry;
501
+ if (!info.targetTurnId) return null;
502
+ const out = {
503
+ targetTurnId: info.targetTurnId,
504
+ timestamp: info.timestamp
505
+ };
506
+ if (info.label !== void 0) out.label = info.label;
507
+ return out;
508
+ }
509
+ function appendEntryToSessionFile(sessionId, entry) {
510
+ const meta = readSessionMeta(sessionId);
511
+ const paths = sessionPaths(meta);
512
+ appendSessionFileEntry(paths.current, entry);
513
+ }
514
+ function removeSessionDirectory(sessionId) {
515
+ const meta = readSessionMeta(sessionId);
516
+ const paths = sessionPaths(meta);
517
+ const dir = dirname(paths.meta);
518
+ rmSync(dir, { recursive: true, force: true });
519
+ }
520
+ function tombstoneSession(sessionId) {
521
+ const meta = readSessionMeta(sessionId);
522
+ const paths = sessionPaths(meta);
523
+ if (!existsSync2(paths.meta)) return;
524
+ const tombstone = join2(dirname(paths.meta), "meta.deleted.json");
525
+ renameSync(paths.meta, tombstone);
526
+ }
527
+
528
+ // src/domains/session/tree/navigator.ts
529
+ init_esm_shims();
530
+ function pickMeta(meta) {
531
+ const out = {
532
+ id: meta.id,
533
+ cwd: meta.cwd,
534
+ createdAt: meta.createdAt,
535
+ endedAt: meta.endedAt,
536
+ model: meta.model,
537
+ target: meta.target
538
+ };
539
+ if (meta.parentSessionId !== void 0) out.parentSessionId = meta.parentSessionId;
540
+ if (meta.parentTurnId !== void 0) out.parentTurnId = meta.parentTurnId;
541
+ return out;
542
+ }
543
+ function computeLeafId(nodes) {
544
+ if (nodes.length === 0) return null;
545
+ const hasChild = /* @__PURE__ */ new Set();
546
+ for (const node of nodes) {
547
+ if (node.parentId) hasChild.add(node.parentId);
548
+ }
549
+ let leaf = null;
550
+ for (const node of nodes) {
551
+ if (hasChild.has(node.id)) continue;
552
+ if (!leaf || node.at >= leaf.at) leaf = node;
553
+ }
554
+ return leaf?.id ?? null;
555
+ }
556
+ function buildTreeSnapshot(input) {
557
+ const nodesById = {};
558
+ for (const node of input.nodes) {
559
+ const entry = {
560
+ id: node.id,
561
+ parentId: node.parentId,
562
+ at: node.at,
563
+ kind: node.kind,
564
+ children: []
565
+ };
566
+ const resolved = input.labels.get(node.id);
567
+ if (resolved && resolved.label !== "") entry.label = resolved.label;
568
+ const preview = input.previews?.get(node.id);
569
+ if (preview !== void 0 && preview.length > 0) entry.preview = preview;
570
+ nodesById[node.id] = entry;
571
+ }
572
+ const rootIds = [];
573
+ for (const node of input.nodes) {
574
+ const parent = node.parentId ? nodesById[node.parentId] : void 0;
575
+ if (!parent) {
576
+ rootIds.push(node.id);
577
+ } else {
578
+ parent.children.push(node.id);
579
+ }
580
+ }
581
+ const byTime = (a, b) => {
582
+ const na = nodesById[a];
583
+ const nb = nodesById[b];
584
+ if (!na || !nb) return a < b ? -1 : a > b ? 1 : 0;
585
+ if (na.at === nb.at) return na.id < nb.id ? -1 : na.id > nb.id ? 1 : 0;
586
+ return na.at < nb.at ? -1 : 1;
587
+ };
588
+ rootIds.sort(byTime);
589
+ for (const key of Object.keys(nodesById)) {
590
+ const entry = nodesById[key];
591
+ if (entry) entry.children.sort(byTime);
592
+ }
593
+ const leafId = input.leafId !== void 0 ? input.leafId : computeLeafId(input.nodes);
594
+ return {
595
+ sessionId: input.meta.id,
596
+ meta: pickMeta(input.meta),
597
+ leafId,
598
+ nodesById,
599
+ rootIds
600
+ };
601
+ }
602
+
603
+ // src/domains/session/tree/preview.ts
604
+ init_esm_shims();
605
+ var TURN_PREVIEW_MAX_CHARS = 60;
606
+ var ESC = String.fromCharCode(27);
607
+ var ANSI_CSI_RE = new RegExp(`${ESC}\\[[0-9;?]*[ -/]*[@-~]`, "g");
608
+ var ANSI_FALLBACK_RE = new RegExp(`${ESC}[@-_]`, "g");
609
+ function stripAnsi(text) {
610
+ return text.replace(ANSI_CSI_RE, "").replace(ANSI_FALLBACK_RE, "");
611
+ }
612
+ function collapseSingleLine(text) {
613
+ return text.replace(/\s+/g, " ").trim();
614
+ }
615
+ function sanitize(text) {
616
+ return collapseSingleLine(stripTokenizerSentinels(stripAnsi(text)));
617
+ }
618
+ function clamp(text, max) {
619
+ if (text.length <= max) return text;
620
+ if (max <= 1) return text.slice(0, max);
621
+ return `${text.slice(0, max - 1)}\u2026`;
622
+ }
623
+ function extractPlainText(payload) {
624
+ if (typeof payload === "string") {
625
+ const sanitized = sanitize(payload);
626
+ return sanitized.length > 0 ? sanitized : null;
627
+ }
628
+ if (!payload || typeof payload !== "object") return null;
629
+ const obj = payload;
630
+ if (typeof obj.text === "string") {
631
+ const sanitized = sanitize(obj.text);
632
+ if (sanitized.length > 0) return sanitized;
633
+ }
634
+ if (Array.isArray(obj.content)) {
635
+ const parts = [];
636
+ for (const block of obj.content) {
637
+ if (!block || typeof block !== "object") continue;
638
+ const b = block;
639
+ if (b.type === "text" && typeof b.text === "string") parts.push(b.text);
640
+ }
641
+ if (parts.length > 0) {
642
+ const sanitized = sanitize(parts.join(" "));
643
+ if (sanitized.length > 0) return sanitized;
644
+ }
645
+ }
646
+ return null;
647
+ }
648
+ function pickToolArg(toolName, args) {
649
+ if (!args || typeof args !== "object") return "";
650
+ const a = args;
651
+ const preferred = {
652
+ read: ["path"],
653
+ write: ["path"],
654
+ edit: ["path"],
655
+ bash: ["command"],
656
+ code_nav: ["query", "mode"],
657
+ git: ["op"],
658
+ verify: ["check"],
659
+ grep: ["pattern", "query"],
660
+ search: ["query", "pattern"],
661
+ web_fetch: ["url"],
662
+ web_search: ["query"]
663
+ };
664
+ const order = preferred[toolName] ?? [];
665
+ for (const key of order) {
666
+ const value = a[key];
667
+ if (typeof value === "string" && value.length > 0) return sanitize(value);
668
+ if (typeof value === "number") return String(value);
669
+ }
670
+ for (const [, value] of Object.entries(a)) {
671
+ if (typeof value === "string" && value.length > 0) return sanitize(value);
672
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
673
+ }
674
+ return "";
675
+ }
676
+ function extractToolCall(payload) {
677
+ if (!payload || typeof payload !== "object") return null;
678
+ const p = payload;
679
+ const toolName = typeof p.name === "string" ? p.name : typeof p.toolName === "string" ? p.toolName : null;
680
+ if (!toolName) return null;
681
+ const args = p.args !== void 0 ? p.args : p.arguments ?? p.input;
682
+ return { toolName, args };
683
+ }
684
+ function extractToolResult(payload) {
685
+ if (!payload || typeof payload !== "object") return null;
686
+ const p = payload;
687
+ if (!("toolCallId" in p) && !("result" in p) && !("isError" in p)) return null;
688
+ const isError = p.isError === true;
689
+ let text = "";
690
+ const result = p.result;
691
+ if (typeof result === "string") text = result;
692
+ else if (result && typeof result === "object") {
693
+ const r = result;
694
+ if (Array.isArray(r.content)) {
695
+ const parts = [];
696
+ for (const block of r.content) {
697
+ if (!block || typeof block !== "object") continue;
698
+ const b = block;
699
+ if (b.type === "text" && typeof b.text === "string") parts.push(b.text);
700
+ }
701
+ text = parts.join(" ");
702
+ } else if (typeof r.text === "string") {
703
+ text = r.text;
704
+ }
705
+ }
706
+ if (text.length === 0 && typeof p.errorMessage === "string") text = p.errorMessage;
707
+ return { isError, text: sanitize(text) };
708
+ }
709
+ function assistantStopMarker(payload) {
710
+ if (!payload || typeof payload !== "object") return null;
711
+ const sr = payload.stopReason;
712
+ if (sr === "aborted") return "(aborted)";
713
+ if (sr === "error") return "(error)";
714
+ return null;
715
+ }
716
+ function buildTurnPreview(turn, max = TURN_PREVIEW_MAX_CHARS) {
717
+ const budget = Math.max(1, max);
718
+ switch (turn.kind) {
719
+ case "user": {
720
+ const text = extractPlainText(turn.payload);
721
+ return text === null ? "(empty)" : clamp(text, budget);
722
+ }
723
+ case "assistant": {
724
+ const stop = assistantStopMarker(turn.payload);
725
+ const text = extractPlainText(turn.payload);
726
+ if (text !== null) return clamp(text, budget);
727
+ if (stop !== null) return stop;
728
+ if (turn.payload && typeof turn.payload === "object" && Array.isArray(turn.payload.content)) {
729
+ const content = turn.payload.content;
730
+ const toolCalls = content.filter(
731
+ (b) => b && typeof b === "object" && b.type === "toolCall"
732
+ );
733
+ if (toolCalls.length > 0) {
734
+ const names = toolCalls.map((b) => b.name).filter((n) => typeof n === "string");
735
+ if (names.length > 0) return clamp(`(tool calls) ${names.join(", ")}`, budget);
736
+ }
737
+ }
738
+ return "(empty)";
739
+ }
740
+ case "tool_call": {
741
+ const call = extractToolCall(turn.payload);
742
+ if (!call) return "(empty)";
743
+ const arg = pickToolArg(call.toolName, call.args);
744
+ const inner = arg.length > 0 ? `"${clamp(arg, Math.max(8, budget - call.toolName.length - 4))}"` : "";
745
+ const composed = `${call.toolName}(${inner})`;
746
+ return clamp(composed, budget);
747
+ }
748
+ case "tool_result": {
749
+ const result = extractToolResult(turn.payload);
750
+ if (!result) return "(empty)";
751
+ const status = result.isError ? "err" : "ok";
752
+ const head = `[${status}]`;
753
+ if (result.text.length === 0) return head;
754
+ const remaining = Math.max(4, budget - head.length - 1);
755
+ return `${head} ${clamp(result.text, remaining)}`;
756
+ }
757
+ case "system": {
758
+ const text = extractPlainText(turn.payload);
759
+ return text === null ? "(system)" : clamp(text, budget);
760
+ }
761
+ case "checkpoint":
762
+ return "(checkpoint)";
763
+ default:
764
+ return `(${String(turn.kind)})`;
765
+ }
766
+ }
767
+
768
+ // src/domains/session/extension.ts
769
+ function createSessionBundle(context) {
770
+ let state = null;
771
+ let currentTurnId = null;
772
+ function emitPark(sessionId, reason) {
773
+ context.bus.emit(BusChannels.SessionParked, { sessionId, reason, at: Date.now() });
774
+ }
775
+ function emitResume(sessionId, via) {
776
+ context.bus.emit(BusChannels.SessionResumed, { sessionId, via, at: Date.now() });
777
+ }
778
+ function emitTurnSwitched(sessionId, turnId) {
779
+ context.bus.emit(BusChannels.SessionTurnSwitched, { sessionId, turnId, at: Date.now() });
780
+ }
781
+ function resolveLeafOnOpen(resumed) {
782
+ const pinned = resumed.state.meta.pinnedLeafTurnId;
783
+ if (pinned && resumed.nodes.some((node) => node.id === pinned)) return pinned;
784
+ return computeLeafId(resumed.nodes);
785
+ }
786
+ async function closeCurrent(reason = "close") {
787
+ if (!state) return;
788
+ const s = state;
789
+ emitPark(s.meta.id, reason);
790
+ state = null;
791
+ currentTurnId = null;
792
+ await s.writer.close();
793
+ }
794
+ async function flushIfCurrent(sessionId) {
795
+ if (state?.meta.id === sessionId) {
796
+ await state.writer.persistTree();
797
+ }
798
+ }
799
+ function structuralTreeNodes(entries, previews) {
800
+ const nodes = [];
801
+ for (const entry of entries) {
802
+ if (entry.kind === "compactionSummary") {
803
+ nodes.push({ id: entry.turnId, parentId: entry.parentTurnId, at: entry.timestamp, kind: "compaction" });
804
+ const summarized = entry.messagesSummarized === void 0 ? "history" : `${entry.messagesSummarized} entries`;
805
+ const after = entry.tokensAfter === void 0 ? "" : ` -> ~${entry.tokensAfter}`;
806
+ previews.set(entry.turnId, `${summarized} summarized, ~${entry.tokensBefore}${after} tokens`);
807
+ continue;
808
+ }
809
+ if (entry.kind === "branchSummary") {
810
+ nodes.push({ id: entry.turnId, parentId: entry.parentTurnId, at: entry.timestamp, kind: "branch" });
811
+ const text = entry.summary.split("\n", 1)[0] ?? "";
812
+ previews.set(
813
+ entry.turnId,
814
+ text.length > 0 ? `returned from ${entry.fromTurnId}: ${text}` : `returned from ${entry.fromTurnId}`
815
+ );
816
+ }
817
+ }
818
+ return nodes;
819
+ }
820
+ function snapshotFor(sessionId) {
821
+ const bundle = readTreeBundle(sessionId);
822
+ const meta = state?.meta.id === sessionId ? state.meta : bundle.meta;
823
+ const previews = /* @__PURE__ */ new Map();
824
+ let structural = [];
825
+ try {
826
+ for (const entry of bundle.entries) {
827
+ if (entry.kind !== "message") continue;
828
+ const text = buildTurnPreview({ kind: entry.role, payload: entry.payload });
829
+ if (text.length > 0) previews.set(entry.turnId, text);
830
+ }
831
+ structural = structuralTreeNodes(bundle.entries, previews);
832
+ } catch {
833
+ }
834
+ const persistedLeaf = bundle.meta.pinnedLeafTurnId && bundle.nodes.some((node) => node.id === bundle.meta.pinnedLeafTurnId) ? bundle.meta.pinnedLeafTurnId : computeLeafId(bundle.nodes);
835
+ return buildTreeSnapshot({
836
+ meta,
837
+ nodes: [...bundle.nodes, ...structural],
838
+ labels: bundle.labels,
839
+ previews,
840
+ // The leaf is the next append point, so it is computed over turn nodes
841
+ // only: a structural node marks a moment in the timeline, never a place
842
+ // the session continues from.
843
+ leafId: state?.meta.id === sessionId ? currentTurnId : persistedLeaf
844
+ });
845
+ }
846
+ const contract = {
847
+ current: () => state?.meta ?? null,
848
+ create(input) {
849
+ const cwd = input?.cwd ?? process.cwd();
850
+ const startInput = { cwd };
851
+ if (input?.model !== void 0) startInput.model = input.model;
852
+ if (input?.target !== void 0) startInput.target = input.target;
853
+ if (state) {
854
+ const prior = state;
855
+ emitPark(prior.meta.id, "create_new");
856
+ state = null;
857
+ void prior.writer.close();
858
+ }
859
+ const next = startSession(startInput);
860
+ next.meta.workspace = probeWorkspace(cwd);
861
+ persistSessionMeta(next);
862
+ state = next;
863
+ currentTurnId = null;
864
+ return next.meta;
865
+ },
866
+ append(turn) {
867
+ if (!state) throw new Error("session.append: no current session");
868
+ if (turn.parentId !== currentTurnId) {
869
+ throw new Error(
870
+ `session.append: turn parentId ${turn.parentId ?? "null"} is not this session's current leaf (expected ${currentTurnId ?? "null"}); refusing to parent a turn onto another session's turn`
871
+ );
872
+ }
873
+ const record = appendTurn(state, turn);
874
+ currentTurnId = record.id;
875
+ if (state.meta.pinnedLeafTurnId !== void 0 && state.meta.pinnedLeafTurnId !== null) {
876
+ state.meta.pinnedLeafTurnId = null;
877
+ persistSessionMeta(state);
878
+ }
879
+ return record;
880
+ },
881
+ appendEntry(entry) {
882
+ if (!state) throw new Error("session.appendEntry: no current session");
883
+ return appendEntry(state, entry);
884
+ },
885
+ replaceEntries(entries) {
886
+ if (!state) throw new Error("session.replaceEntries: no current session");
887
+ replaceEntries(state, entries);
888
+ },
889
+ recordSkillActivation(activation) {
890
+ if (!state) throw new Error("session.recordSkillActivation: no current session");
891
+ appendEntry(state, {
892
+ kind: "skillActivation",
893
+ parentTurnId: activation.turnId ?? null,
894
+ activation
895
+ });
896
+ const existing = state.meta.skillActivations ?? [];
897
+ state.meta.skillActivations = [...existing, activation];
898
+ persistSessionMeta(state);
899
+ return activation;
900
+ },
901
+ async checkpoint(reason) {
902
+ if (!state) throw new Error("session.checkpoint: no current session");
903
+ await performCheckpoint(state, reason);
904
+ },
905
+ flushAppends() {
906
+ if (!state) throw new Error("session.flushAppends: no current session");
907
+ state.writer.flushAppends();
908
+ },
909
+ resume(sessionId) {
910
+ if (state && state.meta.id === sessionId) return state.meta;
911
+ const resumed = resumeSessionState(sessionId);
912
+ if (state) {
913
+ const prior = state;
914
+ emitPark(prior.meta.id, "resume_other");
915
+ void prior.writer.close();
916
+ }
917
+ state = resumed.state;
918
+ currentTurnId = resolveLeafOnOpen(resumed);
919
+ emitResume(resumed.state.meta.id, "resume");
920
+ return resumed.state.meta;
921
+ },
922
+ fork(parentTurnId, input) {
923
+ if (!state) throw new Error("session.fork: no current session to fork from");
924
+ const prior = state;
925
+ const { next, nodes } = forkFromState({
926
+ from: prior,
927
+ parentTurnId,
928
+ ...input?.cwd !== void 0 ? { cwd: input.cwd } : {}
929
+ });
930
+ emitPark(prior.meta.id, "fork");
931
+ state = next;
932
+ currentTurnId = computeLeafId(nodes);
933
+ return next.meta;
934
+ },
935
+ tree(sessionId) {
936
+ const id = sessionId ?? state?.meta.id;
937
+ if (!id) throw new Error("session.tree: no sessionId provided and no current session");
938
+ if (state?.meta.id === id) void flushIfCurrent(id);
939
+ return snapshotFor(id);
940
+ },
941
+ switchBranch(sessionId) {
942
+ if (state?.meta.id === sessionId) return state.meta;
943
+ const resumed = resumeSessionState(sessionId);
944
+ if (state) {
945
+ const prior = state;
946
+ emitPark(prior.meta.id, "switch_branch");
947
+ void prior.writer.close();
948
+ }
949
+ state = resumed.state;
950
+ currentTurnId = resolveLeafOnOpen(resumed);
951
+ emitResume(resumed.state.meta.id, "switch_branch");
952
+ return resumed.state.meta;
953
+ },
954
+ switchTurn(turnId) {
955
+ if (!state) throw new Error("session.switchTurn: no current session");
956
+ void flushIfCurrent(state.meta.id);
957
+ const nodes = readTreeBundle(state.meta.id).nodes;
958
+ if (!nodes.some((node) => node.id === turnId)) {
959
+ throw new Error(`session.switchTurn: turn not found: ${turnId}`);
960
+ }
961
+ currentTurnId = turnId;
962
+ state.meta.pinnedLeafTurnId = turnId;
963
+ persistSessionMeta(state);
964
+ emitTurnSwitched(state.meta.id, turnId);
965
+ return state.meta;
966
+ },
967
+ editLabel(turnId, label, sessionId) {
968
+ const targetId = sessionId ?? state?.meta.id;
969
+ if (!targetId) throw new Error("session.editLabel: no sessionId provided and no current session");
970
+ if (state && state.meta.id === targetId) {
971
+ appendEntry(state, {
972
+ kind: "label",
973
+ parentTurnId: null,
974
+ targetTurnId: turnId,
975
+ label
976
+ });
977
+ return;
978
+ }
979
+ const entry = {
980
+ kind: "label",
981
+ turnId: newTurnId(),
982
+ parentTurnId: null,
983
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
984
+ targetTurnId: turnId,
985
+ label
986
+ };
987
+ appendEntryToSessionFile(targetId, entry);
988
+ },
989
+ setName(name, sessionId) {
990
+ const targetId = sessionId ?? state?.meta.id;
991
+ if (!targetId) throw new Error("session.setName: no sessionId provided and no current session");
992
+ if (state && state.meta.id === targetId) {
993
+ appendEntry(state, {
994
+ kind: "sessionInfo",
995
+ parentTurnId: null,
996
+ name
997
+ });
998
+ return;
999
+ }
1000
+ const entry = {
1001
+ kind: "sessionInfo",
1002
+ turnId: newTurnId(),
1003
+ parentTurnId: null,
1004
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1005
+ name
1006
+ };
1007
+ appendEntryToSessionFile(targetId, entry);
1008
+ },
1009
+ deleteSession(id, opts) {
1010
+ if (state?.meta.id === id) {
1011
+ throw new Error("session.deleteSession: refusing to delete the currently open session; close() first");
1012
+ }
1013
+ const options = opts ?? {};
1014
+ if (options.keepFiles) {
1015
+ tombstoneSession(id);
1016
+ } else {
1017
+ removeSessionDirectory(id);
1018
+ }
1019
+ },
1020
+ history() {
1021
+ const cwd = state?.meta.cwd ?? process.cwd();
1022
+ return listSessionsForCwd(cwd);
1023
+ },
1024
+ async close() {
1025
+ await closeCurrent();
1026
+ }
1027
+ };
1028
+ const extension = {
1029
+ async start() {
1030
+ },
1031
+ async stop() {
1032
+ if (!state) return;
1033
+ try {
1034
+ await performCheckpoint(state, "shutdown");
1035
+ } catch (err) {
1036
+ process.stderr.write(
1037
+ `[clio:session] shutdown checkpoint failed: ${err instanceof Error ? err.message : String(err)}
1038
+ `
1039
+ );
1040
+ }
1041
+ await closeCurrent("shutdown");
1042
+ }
1043
+ };
1044
+ return { extension, contract };
1045
+ }
1046
+
1047
+ // src/domains/session/manifest.ts
1048
+ init_esm_shims();
1049
+ var SessionManifest = {
1050
+ name: "session",
1051
+ dependsOn: ["config"]
1052
+ };
1053
+
1054
+ // src/domains/session/protected-artifacts.ts
1055
+ init_esm_shims();
1056
+ function protectedArtifactEntryFromArtifact(artifact, context) {
1057
+ const entry = {
1058
+ kind: "protectedArtifact",
1059
+ parentTurnId: context.parentTurnId,
1060
+ action: "protect",
1061
+ artifact: artifactSnapshot(artifact)
1062
+ };
1063
+ if (context.toolName !== void 0) entry.toolName = context.toolName;
1064
+ if (context.toolCallId !== void 0) entry.toolCallId = context.toolCallId;
1065
+ if (context.runId !== void 0) entry.runId = context.runId;
1066
+ if (context.correlationId !== void 0) entry.correlationId = context.correlationId;
1067
+ return entry;
1068
+ }
1069
+ function protectedArtifactFromSessionEntry(entry) {
1070
+ return protectedArtifactFromSnapshot(entry.artifact);
1071
+ }
1072
+ function protectedArtifactStateFromSessionEntries(entries) {
1073
+ let state = { artifacts: [] };
1074
+ const protectedEntries = entries.map((entry, index) => ({ entry, index })).filter((item) => item.entry.kind === "protectedArtifact").sort(compareProtectedEntryPositions);
1075
+ for (const item of protectedEntries) {
1076
+ if (item.entry.action !== "protect") continue;
1077
+ state = protectArtifact(state, protectedArtifactFromSessionEntry(item.entry));
1078
+ }
1079
+ return state;
1080
+ }
1081
+ function artifactSnapshot(artifact) {
1082
+ const snapshot = {
1083
+ path: artifact.path,
1084
+ protectedAt: artifact.protectedAt,
1085
+ reason: artifact.reason,
1086
+ source: artifact.source
1087
+ };
1088
+ if (artifact.validationCommand !== void 0) snapshot.validationCommand = artifact.validationCommand;
1089
+ if (artifact.validationExitCode !== void 0) snapshot.validationExitCode = artifact.validationExitCode;
1090
+ return snapshot;
1091
+ }
1092
+ function protectedArtifactFromSnapshot(snapshot) {
1093
+ const artifact = {
1094
+ path: snapshot.path,
1095
+ protectedAt: snapshot.protectedAt,
1096
+ reason: snapshot.reason,
1097
+ source: snapshot.source
1098
+ };
1099
+ if (snapshot.validationCommand !== void 0) artifact.validationCommand = snapshot.validationCommand;
1100
+ if (snapshot.validationExitCode !== void 0) artifact.validationExitCode = snapshot.validationExitCode;
1101
+ return artifact;
1102
+ }
1103
+ function compareProtectedEntryPositions(left, right) {
1104
+ return compareStrings(left.entry.timestamp, right.entry.timestamp) || compareStrings(left.entry.turnId, right.entry.turnId) || left.index - right.index;
1105
+ }
1106
+ function compareStrings(left, right) {
1107
+ if (left < right) return -1;
1108
+ if (left > right) return 1;
1109
+ return 0;
1110
+ }
1111
+
1112
+ // src/domains/session/usage.ts
1113
+ init_esm_shims();
1114
+ function numberAt(source, key) {
1115
+ const value = source[key];
1116
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
1117
+ }
1118
+ function reasoningTokensOf(usage) {
1119
+ const direct = numberAt(usage, "reasoningTokens");
1120
+ if (direct > 0) return direct;
1121
+ const details = usage.outputTokensDetails ?? usage.completionTokensDetails;
1122
+ if (details && typeof details === "object") return numberAt(details, "reasoningTokens");
1123
+ return 0;
1124
+ }
1125
+ function stringAt(source, ...keys) {
1126
+ for (const key of keys) {
1127
+ const value = source[key];
1128
+ if (typeof value === "string" && value.trim().length > 0) return value;
1129
+ }
1130
+ return null;
1131
+ }
1132
+ function ledgerUsageCalls(entries, defaults = {}) {
1133
+ const calls = [];
1134
+ let currentTarget = defaults.target ?? null;
1135
+ let currentModel = defaults.model ?? null;
1136
+ for (const entry of entries) {
1137
+ if (entry?.kind === "compactionSummary") {
1138
+ const usage2 = entry.usage;
1139
+ if (!usage2 || usage2.totalTokens <= 0) continue;
1140
+ calls.push({
1141
+ providerId: currentTarget ?? "unknown",
1142
+ modelId: currentModel ?? "unknown",
1143
+ input: usage2.input,
1144
+ output: usage2.output,
1145
+ cacheRead: usage2.cacheRead,
1146
+ cacheWrite: usage2.cacheWrite,
1147
+ reasoningTokens: usage2.reasoning,
1148
+ totalTokens: usage2.totalTokens,
1149
+ costUsd: usage2.cost.total,
1150
+ // A split turn runs two summarization streams under one entry; the
1151
+ // provider served that many calls even though one row records them.
1152
+ apiCalls: Math.max(1, Math.round(usage2.apiCalls))
1153
+ });
1154
+ continue;
1155
+ }
1156
+ if (entry?.kind === "modelChange") {
1157
+ const change = entry;
1158
+ if (typeof change.target === "string" && change.target.length > 0) currentTarget = change.target;
1159
+ else if (typeof change.provider === "string" && change.provider.length > 0) currentTarget = change.provider;
1160
+ if (typeof change.modelId === "string" && change.modelId.length > 0) currentModel = change.modelId;
1161
+ continue;
1162
+ }
1163
+ if (entry?.kind !== "message" || entry.role !== "assistant") continue;
1164
+ const payload = entry.payload;
1165
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) continue;
1166
+ const record = payload;
1167
+ const stopReason = record.stopReason;
1168
+ if (stopReason === "aborted" || stopReason === "error") continue;
1169
+ const rawUsage = record.usage;
1170
+ if (!rawUsage || typeof rawUsage !== "object" || Array.isArray(rawUsage)) continue;
1171
+ const usage = rawUsage;
1172
+ const input = numberAt(usage, "input");
1173
+ const output = numberAt(usage, "output");
1174
+ const cacheRead = numberAt(usage, "cacheRead");
1175
+ const cacheWrite = numberAt(usage, "cacheWrite");
1176
+ const totalTokens = numberAt(usage, "totalTokens") || input + output + cacheRead + cacheWrite;
1177
+ if (totalTokens === 0) continue;
1178
+ const cost = usage.cost;
1179
+ const costUsd = cost && typeof cost === "object" ? numberAt(cost, "total") : 0;
1180
+ calls.push({
1181
+ providerId: currentTarget ?? stringAt(record, "provider", "api") ?? "unknown",
1182
+ modelId: currentModel ?? stringAt(record, "responseModel", "model") ?? "unknown",
1183
+ input,
1184
+ output,
1185
+ cacheRead,
1186
+ cacheWrite,
1187
+ reasoningTokens: reasoningTokensOf(usage),
1188
+ totalTokens,
1189
+ costUsd
1190
+ });
1191
+ }
1192
+ return calls;
1193
+ }
1194
+
1195
+ // src/domains/session/index.ts
1196
+ var SessionDomainModule = {
1197
+ manifest: SessionManifest,
1198
+ createExtension: createSessionBundle
1199
+ };
1200
+
1201
+ // src/domains/session/archive-readers.ts
1202
+ async function readAuditRows(stateDir) {
1203
+ const root = join3(stateDir, "audit");
1204
+ let files;
1205
+ try {
1206
+ files = await readdir(root);
1207
+ } catch (error) {
1208
+ const err = error;
1209
+ if (err.code === "ENOENT") return { rows: [], errors: [] };
1210
+ return { rows: [], errors: [`audit root read error: ${err.message ?? String(err)}`] };
1211
+ }
1212
+ const rows = [];
1213
+ const errors = [];
1214
+ for (const file of files.filter((name) => name.endsWith(".jsonl")).sort(compareStrings2)) {
1215
+ const path = join3(root, file);
1216
+ let raw;
1217
+ try {
1218
+ raw = await readFile(path, "utf8");
1219
+ } catch (error) {
1220
+ const err = error;
1221
+ errors.push(`${path}: ${err.message ?? String(err)}`);
1222
+ continue;
1223
+ }
1224
+ const lines = raw.split("\n");
1225
+ for (let index = 0; index < lines.length; index += 1) {
1226
+ const line = lines[index];
1227
+ if (line === void 0 || line.length === 0) continue;
1228
+ let parsed;
1229
+ try {
1230
+ parsed = JSON.parse(line);
1231
+ } catch (error) {
1232
+ const message = error instanceof Error ? error.message : String(error);
1233
+ errors.push(`${path}:${index + 1}: invalid JSON: ${message}`);
1234
+ continue;
1235
+ }
1236
+ if (!isRecord(parsed)) {
1237
+ errors.push(`${path}:${index + 1}: expected object`);
1238
+ continue;
1239
+ }
1240
+ const auditKind = readOptionalString(parsed.kind) ?? "tool_call";
1241
+ rows.push({
1242
+ file,
1243
+ line: index + 1,
1244
+ row: parsed,
1245
+ ts: readOptionalString(parsed.ts),
1246
+ auditKind,
1247
+ correlationId: readOptionalString(parsed.correlationId) ?? ""
1248
+ });
1249
+ }
1250
+ }
1251
+ return { rows, errors };
1252
+ }
1253
+ async function readSessionEntriesForId(stateDir, sessionId) {
1254
+ const root = join3(stateDir, "sessions");
1255
+ let cwdHashes;
1256
+ try {
1257
+ cwdHashes = await readdir(root);
1258
+ } catch (error) {
1259
+ const err = error;
1260
+ if (err.code === "ENOENT") return { entries: [], missing: true, errors: [] };
1261
+ return { entries: [], missing: false, errors: [`session root read error: ${err.message ?? String(err)}`] };
1262
+ }
1263
+ for (const cwdHash2 of cwdHashes.sort(compareStrings2)) {
1264
+ const currentPath = join3(root, cwdHash2, sessionId, "current.jsonl");
1265
+ let raw;
1266
+ let source = currentPath;
1267
+ try {
1268
+ raw = await readFile(currentPath, "utf8");
1269
+ } catch (error) {
1270
+ const err = error;
1271
+ if (err.code === "ENOENT") {
1272
+ const tmpPath = `${currentPath}.tmp`;
1273
+ try {
1274
+ raw = await readFile(tmpPath, "utf8");
1275
+ source = tmpPath;
1276
+ } catch {
1277
+ continue;
1278
+ }
1279
+ } else if (err.code === "ENOTDIR") {
1280
+ continue;
1281
+ } else {
1282
+ return { entries: [], missing: false, errors: [`${currentPath}: ${err.message ?? String(err)}`] };
1283
+ }
1284
+ }
1285
+ return parseSessionEntries(raw, source);
1286
+ }
1287
+ return { entries: [], missing: true, errors: [] };
1288
+ }
1289
+ function parseSessionEntries(raw, source) {
1290
+ const entries = [];
1291
+ const errors = [];
1292
+ const lines = raw.split("\n");
1293
+ for (let index = 0; index < lines.length; index += 1) {
1294
+ const line = lines[index];
1295
+ if (line === void 0 || line.trim().length === 0) continue;
1296
+ let parsed;
1297
+ try {
1298
+ parsed = JSON.parse(line);
1299
+ } catch (error) {
1300
+ const message = error instanceof Error ? error.message : String(error);
1301
+ errors.push(`${source}:${index + 1}: invalid JSON: ${message}`);
1302
+ continue;
1303
+ }
1304
+ if (isSessionHeader(parsed)) continue;
1305
+ if (isSessionEntry(parsed)) {
1306
+ entries.push(parsed);
1307
+ continue;
1308
+ }
1309
+ errors.push(
1310
+ `session ledger contains an unreadable entry (missing kind discriminant): ${source}. Remove or compact the session to reset.`
1311
+ );
1312
+ }
1313
+ return { entries, missing: false, errors };
1314
+ }
1315
+ async function listSessionLedgerRefs(stateDir, onlyCwdHash) {
1316
+ const root = join3(stateDir, "sessions");
1317
+ let cwdHashes;
1318
+ try {
1319
+ cwdHashes = await readdir(root);
1320
+ } catch {
1321
+ return [];
1322
+ }
1323
+ const refs = [];
1324
+ for (const cwdHash2 of cwdHashes.sort(compareStrings2)) {
1325
+ if (onlyCwdHash !== void 0 && cwdHash2 !== onlyCwdHash) continue;
1326
+ let sessionIds;
1327
+ try {
1328
+ sessionIds = await readdir(join3(root, cwdHash2));
1329
+ } catch {
1330
+ continue;
1331
+ }
1332
+ for (const sessionId of sessionIds.sort(compareStrings2)) {
1333
+ refs.push({ cwdHash: cwdHash2, sessionId, path: join3(root, cwdHash2, sessionId, "current.jsonl") });
1334
+ }
1335
+ }
1336
+ return refs;
1337
+ }
1338
+ function readOptionalString(value) {
1339
+ return typeof value === "string" && value.length > 0 ? value : null;
1340
+ }
1341
+ function isRecord(value) {
1342
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1343
+ }
1344
+ function compareStrings2(a, b) {
1345
+ return a.localeCompare(b);
1346
+ }
1347
+
1348
+ export {
1349
+ collectSessionEntries,
1350
+ readAuditRows,
1351
+ readSessionEntriesForId,
1352
+ parseSessionEntries,
1353
+ listSessionLedgerRefs,
1354
+ protectedArtifactEntryFromArtifact,
1355
+ protectedArtifactFromSessionEntry,
1356
+ protectedArtifactStateFromSessionEntries,
1357
+ ledgerUsageCalls,
1358
+ SessionDomainModule
1359
+ };
1360
+ //# sourceMappingURL=chunk-QQK64KLB.js.map