@iowarp/clio-coder 0.5.0 → 0.5.2

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 (312) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/CHANGELOG.md +35 -0
  3. package/CONTRIBUTING.md +9 -0
  4. package/README.md +12 -9
  5. package/ROADMAP.md +30 -5
  6. package/dist/{acp-KS7ARGV6.js → acp-F6PGOFZ4.js} +2 -2
  7. package/dist/{agents-YOGJ2YEJ.js → agents-ZHP4R6EX.js} +22 -21
  8. package/dist/assets/codewiki.json +1 -1
  9. package/dist/{auth-CEDCO6O6.js → auth-N75U2H3Z.js} +4 -4
  10. package/dist/{chunk-D3KWFOUT.js → chunk-23UIXH6Z.js} +3 -3
  11. package/dist/{chunk-ZR5YA6ZF.js → chunk-36XVK5P5.js} +5 -5
  12. package/dist/{chunk-AVSR2HKI.js → chunk-3DZ4LPLS.js} +6 -6
  13. package/dist/{chunk-LYJJ6546.js → chunk-3GSPCWFX.js} +12 -12
  14. package/dist/{chunk-CRHPCPZP.js → chunk-4KC4CHKJ.js} +2 -2
  15. package/dist/{chunk-4VDHLCF5.js → chunk-4WJBWKMI.js} +2 -2
  16. package/dist/{chunk-KBUTSJSR.js → chunk-4XBYHXGK.js} +2 -2
  17. package/dist/{chunk-6W75D37F.js → chunk-53P775GE.js} +6 -3
  18. package/dist/{chunk-XZWSP67B.js → chunk-5APYAXCB.js} +2 -2
  19. package/dist/{chunk-D65E25JG.js → chunk-67HPYNTJ.js} +2 -2
  20. package/dist/{chunk-5JQR5B4D.js → chunk-6KV4KCQZ.js} +4 -4
  21. package/dist/{chunk-V4XJWDHR.js → chunk-6UKFRTXI.js} +2 -1
  22. package/dist/{chunk-NDPF2N2L.js → chunk-7DW7CJW2.js} +2 -2
  23. package/dist/{chunk-5KQEUIWM.js → chunk-7LUEQODV.js} +4 -4
  24. package/dist/{chunk-LNOXCGN2.js → chunk-7UFDIOBT.js} +3 -3
  25. package/dist/{chunk-DXSETN72.js → chunk-7WUGNN43.js} +2 -2
  26. package/dist/{chunk-V35KZDUZ.js → chunk-AA3GRZWS.js} +2 -2
  27. package/dist/{chunk-X4CEFOSG.js → chunk-ADO3OQIC.js} +4 -4
  28. package/dist/{chunk-UX2L5CUD.js → chunk-AI36V3ON.js} +3 -3
  29. package/dist/{chunk-B7GY6QER.js → chunk-B26OUMJW.js} +2 -2
  30. package/dist/{chunk-LAR7DX5E.js → chunk-B5TASTCK.js} +153 -62
  31. package/dist/{chunk-TGWZXJ7L.js → chunk-BEMQK5Q7.js} +92 -84
  32. package/dist/{chunk-RNDCROSG.js → chunk-CNXU7TR2.js} +3 -3
  33. package/dist/{chunk-3JLTRPN7.js → chunk-CVMUXT64.js} +35 -66
  34. package/dist/{chunk-733ZBT5P.js → chunk-E74MJF6G.js} +4 -4
  35. package/dist/{chunk-T2MDZES3.js → chunk-EBMTBE76.js} +3 -3
  36. package/dist/{chunk-OZ76TLGU.js → chunk-EEITU7S6.js} +2 -2
  37. package/dist/{chunk-GRHOXDKB.js → chunk-ELDJWJKM.js} +97 -14
  38. package/dist/{chunk-GCQFN7YI.js → chunk-EMYEQLWO.js} +2 -2
  39. package/dist/{chunk-LTWH2ACR.js → chunk-EQPFEDFV.js} +222 -127
  40. package/dist/{chunk-TDGYTWM6.js → chunk-F4I3H56H.js} +2 -2
  41. package/dist/chunk-F6KCJO3U.js +523 -0
  42. package/dist/{chunk-FGSQHUCK.js → chunk-F6VKWTTY.js} +5 -5
  43. package/dist/{chunk-UJD5MPP6.js → chunk-FLNXQQ5B.js} +2 -2
  44. package/dist/{chunk-EX57VVWW.js → chunk-FOSRJ2VZ.js} +4 -4
  45. package/dist/{chunk-ZARZ4POR.js → chunk-G5ZDQQE3.js} +3 -3
  46. package/dist/{chunk-HQVN7G4F.js → chunk-G62JBPXN.js} +7 -7
  47. package/dist/{chunk-ZJPORZTC.js → chunk-GIAI7A2K.js} +2 -2
  48. package/dist/chunk-GPTR6OTU.js +2551 -0
  49. package/dist/{chunk-EZD2BXGP.js → chunk-GUFQPTQK.js} +2 -2
  50. package/dist/{chunk-OPD7GL6F.js → chunk-H6FQVNZC.js} +8 -7
  51. package/dist/chunk-H7REJ7L5.js +80 -0
  52. package/dist/{chunk-EYTLV3W3.js → chunk-HTWTORF7.js} +2 -2
  53. package/dist/{chunk-DF6PI6GN.js → chunk-I6UKQPGS.js} +3 -3
  54. package/dist/{chunk-ISHUS7HC.js → chunk-JRW236JT.js} +3 -3
  55. package/dist/{chunk-DE2LU267.js → chunk-KKAJGT3B.js} +3 -2
  56. package/dist/{chunk-OV62D6KO.js → chunk-KQMYL5GR.js} +3 -3
  57. package/dist/{chunk-QDUUOZ3S.js → chunk-KWMFC7FN.js} +3 -3
  58. package/dist/{chunk-R7FGRZNF.js → chunk-LXYHCCIZ.js} +9 -9
  59. package/dist/{chunk-NFSK2VNU.js → chunk-LZMTVVN4.js} +2 -2
  60. package/dist/{chunk-62UK7AGW.js → chunk-M5PMRZSR.js} +397 -44
  61. package/dist/chunk-MMZMM6SW.js +131 -0
  62. package/dist/{chunk-FFYTC2KQ.js → chunk-MNABNEBX.js} +2 -2
  63. package/dist/{chunk-XMDBTYCH.js → chunk-N3CWOGVF.js} +3 -3
  64. package/dist/{chunk-VYRQHORQ.js → chunk-NMQSO6Z6.js} +4 -3
  65. package/dist/{chunk-R2UUNSNQ.js → chunk-O3ZAF7UY.js} +18 -5
  66. package/dist/{chunk-4SYHGVDE.js → chunk-OQNCLZI4.js} +4 -4
  67. package/dist/{chunk-EG4ARXPC.js → chunk-OX6QTA4N.js} +3 -3
  68. package/dist/{chunk-P4GNZPXO.js → chunk-OYUS3UZP.js} +2 -2
  69. package/dist/{chunk-OQ2EOAHA.js → chunk-PF36KOGI.js} +291 -112
  70. package/dist/{chunk-55RMDDFM.js → chunk-PGQG7ZMW.js} +2 -2
  71. package/dist/{chunk-CBAQPTDU.js → chunk-PIU6BXXW.js} +2 -2
  72. package/dist/{chunk-EKV4UBCM.js → chunk-PZBQUJ2F.js} +2 -2
  73. package/dist/{chunk-7RET77VB.js → chunk-Q2HKY32Y.js} +2 -2
  74. package/dist/{chunk-U6YTUVOX.js → chunk-Q4RMNWMZ.js} +4 -4
  75. package/dist/chunk-Q6FRR3AQ.js +48 -0
  76. package/dist/{chunk-6RJMYYIE.js → chunk-QJSIAJBY.js} +3 -3
  77. package/dist/{chunk-DOUISWEE.js → chunk-QOJ4ERMU.js} +5 -5
  78. package/dist/{chunk-IFPDAD7R.js → chunk-RWSRKXLT.js} +3 -3
  79. package/dist/{chunk-T33IYTZM.js → chunk-SAASVMIY.js} +3 -3
  80. package/dist/{chunk-2DTIWSAW.js → chunk-SEQDH6JC.js} +3 -3
  81. package/dist/{chunk-BRCOSE7O.js → chunk-SZUO5BSE.js} +2 -2
  82. package/dist/{chunk-HOEDA42N.js → chunk-T7HZ7KJF.js} +2 -2
  83. package/dist/{chunk-OJXOA6YU.js → chunk-TL6LNERH.js} +2 -2
  84. package/dist/{chunk-UCINIRHW.js → chunk-TPCZXWLT.js} +3 -3
  85. package/dist/{chunk-ZUKUCZYZ.js → chunk-TQ2KTH4A.js} +2 -2
  86. package/dist/{chunk-4WOOTKFD.js → chunk-U3FETNQB.js} +341 -20
  87. package/dist/{chunk-5B3RTYAK.js → chunk-UGDSB4AN.js} +2 -2
  88. package/dist/{chunk-VKHLUZNO.js → chunk-UZ7YBL43.js} +3 -3
  89. package/dist/{chunk-QNUQ7K7D.js → chunk-VKMSEO7Y.js} +4 -4
  90. package/dist/{chunk-IN7DGBVS.js → chunk-VLX5VZ35.js} +5 -5
  91. package/dist/{chunk-SJGS3GDI.js → chunk-WAGBMMNX.js} +5 -5
  92. package/dist/{chunk-BFOSV5EZ.js → chunk-WX2YCH7F.js} +2 -2
  93. package/dist/{chunk-3LT34CAM.js → chunk-XBIGUILU.js} +32 -17
  94. package/dist/{chunk-XDHUDE5K.js → chunk-XKYBFRWR.js} +5 -5
  95. package/dist/{chunk-U5QU5ZOD.js → chunk-XYPWFSU5.js} +3 -3
  96. package/dist/{chunk-YBUECLAF.js → chunk-YEJQDODA.js} +2 -2
  97. package/dist/{chunk-YQ6XEFVK.js → chunk-Z74OGONW.js} +39 -7
  98. package/dist/{chunk-I4ELN5BX.js → chunk-ZTNOEJRI.js} +2 -2
  99. package/dist/cli/index.js +26 -26
  100. package/dist/{clio-K2PBVCEM.js → clio-4IKFE25F.js} +2 -2
  101. package/dist/{clio-context-tools-GR4OHFSO.js → clio-context-tools-OHJXYX4R.js} +25 -23
  102. package/dist/{code-nav-4X3OI6DK.js → code-nav-XPM3MY5I.js} +7 -7
  103. package/dist/{config-Q5J7UBAB.js → config-343YS25T.js} +43 -41
  104. package/dist/{config-graph-P3555574.js → config-graph-WI263KDX.js} +43 -41
  105. package/dist/{configure-KIJ32ZO6.js → configure-GZDG4HER.js} +21 -21
  106. package/dist/{context-LFBQOSXT.js → context-4OFO7N7Y.js} +12 -12
  107. package/dist/{context-WBH3KSVR.js → context-JLK2RLOG.js} +42 -40
  108. package/dist/{context-NNLBES2Y.js → context-O6DIWCF6.js} +23 -21
  109. package/dist/{context-clear-YOHMQ6SQ.js → context-clear-MR7KOGQI.js} +42 -40
  110. package/dist/{context-working-set-MRAEVDNV.js → context-working-set-YZAD3KWO.js} +14 -13
  111. package/dist/{data-tool-JJPBGMQJ.js → data-tool-EKD7DFHG.js} +7 -7
  112. package/dist/{detail-RHJOTGN4.js → detail-YZPHFEVJ.js} +43 -41
  113. package/dist/{dispatch-runner-NC7PYNIO.js → dispatch-runner-VKVXVTWJ.js} +44 -42
  114. package/dist/{doctor-S5KZ2GTN.js → doctor-53AVQXAO.js} +17 -16
  115. package/dist/{doctor-deep-2EJQSHIZ.js → doctor-deep-BLRB2NCG.js} +5 -5
  116. package/dist/{eval-3D6M36N7.js → eval-P3BQSLQE.js} +21 -20
  117. package/dist/{evidence-XNVPPLVK.js → evidence-6LLI6SZH.js} +44 -42
  118. package/dist/{evidence-6IYN52ET.js → evidence-HTN3JLUO.js} +42 -40
  119. package/dist/{evidence-JVECPI3F.js → evidence-NJRE3R5H.js} +42 -40
  120. package/dist/{evolve-TLPUDJDM.js → evolve-WWZHJVJP.js} +42 -40
  121. package/dist/{fleet-WKVBF5SZ.js → fleet-GGZ4BVJE.js} +64 -62
  122. package/dist/{fleet-6RJWIOEO.js → fleet-QG3IIWWM.js} +44 -42
  123. package/dist/{fleet-commands-L6WFT7KZ.js → fleet-commands-J2ZIKISQ.js} +7 -7
  124. package/dist/{fleet-decisions-BTQMWU5A.js → fleet-decisions-Y2Q7375Q.js} +8 -8
  125. package/dist/{fleet-graph-553DZ67Q.js → fleet-graph-TCBCCCBP.js} +11 -11
  126. package/dist/{fleet-inspect-236FZPRB.js → fleet-inspect-KOLKBC56.js} +44 -42
  127. package/dist/{fleet-preflight-NZQHABWO.js → fleet-preflight-44UNCC4Y.js} +27 -25
  128. package/dist/{fleet-validate-7NLAE26R.js → fleet-validate-K2FXRZVA.js} +13 -13
  129. package/dist/{fleet-verify-E3RO6A5W.js → fleet-verify-AS444WDW.js} +42 -40
  130. package/dist/{fleet-view-KMZQY7YJ.js → fleet-view-X4JU4J6O.js} +43 -41
  131. package/dist/gui/ops-worker.js +9 -9
  132. package/dist/gui/reads-worker.js +9 -8
  133. package/dist/{init-DV5B6HQG.js → init-VSOWFHVL.js} +54 -52
  134. package/dist/{interactive-OXGYZYAS.js → interactive-JRCBENLZ.js} +1733 -577
  135. package/dist/{interop-J7UOBOSF.js → interop-JR6BCY7R.js} +10 -10
  136. package/dist/{inventory-OEGIL6PY.js → inventory-3JUGLVJI.js} +43 -41
  137. package/dist/{library-ROZAMUIL.js → library-7BOH3Y7N.js} +15 -15
  138. package/dist/{library-B3UPGY4V.js → library-7PFONXUI.js} +9 -9
  139. package/dist/{library-MXQA5DLT.js → library-TUYZMFIA.js} +7 -7
  140. package/dist/{library-import-2LTWOVBD.js → library-import-TJVXKLDK.js} +10 -10
  141. package/dist/{library-inventory-LJ3XHLWO.js → library-inventory-MD3AYTP4.js} +9 -9
  142. package/dist/{library-validation-DARHHUNW.js → library-validation-ZDLITEA3.js} +7 -7
  143. package/dist/{mcp-2GSPYQ54.js → mcp-EFCWUPUR.js} +3 -3
  144. package/dist/{memory-J3STUUIK.js → memory-Q2YVOKK5.js} +42 -40
  145. package/dist/{models-Y3GMFJ2V.js → models-M7BOCW7F.js} +17 -16
  146. package/dist/{monitor-LHNH577J.js → monitor-2HD3MC67.js} +47 -45
  147. package/dist/{orchestrator-DTAPXULA.js → orchestrator-OUW3ZN6Y.js} +1894 -226
  148. package/dist/{panes-A743ONZ5.js → panes-TTPPSYCL.js} +3 -3
  149. package/dist/{preload-OJBMAIXF.js → preload-A47C2NUF.js} +42 -40
  150. package/dist/{providers-CMETPZNS.js → providers-T43W7EL4.js} +4 -4
  151. package/dist/{resources-FY6XYFN2.js → resources-TP6X2V6W.js} +20 -11
  152. package/dist/{run-H64MEVTA.js → run-LDSBPMCS.js} +61 -59
  153. package/dist/{share-CAFMMQLM.js → share-URHKKMHR.js} +9 -9
  154. package/dist/{skills-ERKQUC7L.js → skills-E477MOOO.js} +13 -11
  155. package/dist/{skills-eval-CQ23MYQK.js → skills-eval-AIBR2L7H.js} +583 -93
  156. package/dist/{skills-inventory-TVUYJDVN.js → skills-inventory-HLMKY3R5.js} +13 -11
  157. package/dist/{slash-commands-ROG3KIFJ.js → slash-commands-LIIZKA5Y.js} +28 -26
  158. package/dist/{startup-background-KDWCQMWT.js → startup-background-JR2KDK6R.js} +43 -41
  159. package/dist/{steer-KKYXJSH4.js → steer-D7RTAPCM.js} +3 -3
  160. package/dist/{system-ZDW44ZGX.js → system-X2P5N4IY.js} +14 -13
  161. package/dist/{targets-AJWA6E7Z.js → targets-VFFAKUFM.js} +25 -24
  162. package/dist/{tasks-6L2JJRC5.js → tasks-ERAWZA22.js} +6 -6
  163. package/dist/{terminal-lease-YFUSDPAD.js → terminal-lease-V2N2BIKQ.js} +2 -2
  164. package/dist/{trace-LA5MVR4Y.js → trace-MA5BT7XY.js} +3 -3
  165. package/dist/{usage-XSILDHKF.js → usage-WYFY2L6M.js} +50 -48
  166. package/dist/{verifiers-ECA5GIHL.js → verifiers-TYIW2XKF.js} +7 -7
  167. package/dist/{verify-R6QQCCEB.js → verify-242UFYTP.js} +6 -6
  168. package/dist/{web-fetch-PVZNMBOB.js → web-fetch-LRAGYU6Y.js} +3 -3
  169. package/dist/{wiki-generate-RP22WP5K.js → wiki-generate-OFKOX2T6.js} +53 -51
  170. package/dist/worker/entry.js +31 -29
  171. package/docs/architecture/architecture.md +1 -0
  172. package/docs/architecture/context-engine.md +4 -2
  173. package/docs/architecture/observability.md +6 -4
  174. package/docs/architecture/prompt-envelope-and-tools.md +1 -1
  175. package/docs/architecture/session-lifecycle.md +1 -1
  176. package/docs/architecture/tui-design.md +12 -9
  177. package/docs/gui/parity/02-slash-and-surfaces.md +1 -1
  178. package/docs/guide/commands-and-modes.md +91 -5
  179. package/docs/guide/configuration-and-targets.md +2 -2
  180. package/docs/guide/context-continuity.md +49 -0
  181. package/docs/guide/proactive-memory.md +6 -3
  182. package/library/registry.yaml +292 -32
  183. package/library/skills/README.md +27 -2
  184. package/library/skills/context/context-handoff/SKILL.md +10 -4
  185. package/library/skills/meta/clio-coder-dev/SKILL.md +92 -88
  186. package/library/skills/meta/clio-coder-dev/evals.md +51 -46
  187. package/library/skills/meta/clio-coder-dev/plugin.json +2 -2
  188. package/library/skills/meta/clio-coder-dev/references/change-map.md +49 -0
  189. package/library/skills/meta/clio-coder-test/SKILL.md +89 -142
  190. package/library/skills/meta/clio-coder-test/evals.md +58 -62
  191. package/library/skills/meta/clio-coder-test/plugin.json +2 -2
  192. package/library/skills/meta/clio-coder-test/references/harness.md +3 -3
  193. package/library/skills/meta/clio-coder-test/references/lifecycle-validation.md +30 -0
  194. package/library/skills/meta/clio-coder-test/references/test-map.md +60 -89
  195. package/library/skills/registry.yaml +5 -5
  196. package/library/skills/skill-marketplace.json +12 -14
  197. package/package.json +1 -1
  198. package/src/cli/skills-eval.ts +1064 -54
  199. package/src/cli/usage.ts +2 -2
  200. package/src/core/clio-repo.ts +3 -0
  201. package/src/core/tool-names.ts +1 -0
  202. package/src/domains/context/budget/inspection.ts +9 -0
  203. package/src/domains/context/budget/live-view.ts +418 -0
  204. package/src/domains/context/budget/pressure.ts +393 -0
  205. package/src/domains/context/budget/request-fit.ts +15 -0
  206. package/src/domains/evidence/build.ts +24 -0
  207. package/src/domains/gateway/mcp/client.ts +1 -1
  208. package/src/domains/memory/commit-state.ts +160 -0
  209. package/src/domains/memory/operations.ts +18 -10
  210. package/src/domains/memory/prompt-cache.ts +95 -0
  211. package/src/domains/memory/prompt-section.ts +50 -7
  212. package/src/domains/memory/relevance.ts +93 -0
  213. package/src/domains/memory/restoration.ts +78 -0
  214. package/src/domains/memory/store.ts +39 -1
  215. package/src/domains/middleware/memory-intervention.ts +116 -24
  216. package/src/domains/observability/background-memory-usage.ts +1 -1
  217. package/src/domains/observability/cost.ts +3 -3
  218. package/src/domains/observability/extension.ts +1 -1
  219. package/src/domains/observability/metrics.ts +1 -1
  220. package/src/domains/prompts/compiler.ts +1 -1
  221. package/src/domains/prompts/extension.ts +38 -1
  222. package/src/domains/quota/anthropic-max-provider.ts +134 -0
  223. package/src/domains/quota/anthropic-usage.ts +220 -0
  224. package/src/domains/quota/antigravity-provider.ts +339 -0
  225. package/src/domains/quota/cache.ts +87 -0
  226. package/src/domains/quota/claude-code-provider.ts +169 -0
  227. package/src/domains/quota/codex-provider.ts +237 -0
  228. package/src/domains/quota/presentation.ts +240 -0
  229. package/src/domains/quota/registry.ts +23 -0
  230. package/src/domains/quota/service.ts +93 -0
  231. package/src/domains/quota/summary-feed.ts +86 -0
  232. package/src/domains/quota/types.ts +84 -0
  233. package/src/domains/resources/index.ts +11 -0
  234. package/src/domains/resources/skills/catalog-view.ts +571 -0
  235. package/src/domains/resources/skills/lexical-match.ts +136 -0
  236. package/src/domains/resources/skills/loader.ts +38 -0
  237. package/src/domains/resources/skills/promotion.ts +1 -55
  238. package/src/domains/resources/skills/provenance-pin.ts +50 -20
  239. package/src/domains/safety/action-classifier.ts +1 -0
  240. package/src/domains/session/compaction/branch-summary.ts +4 -1
  241. package/src/domains/session/compaction/compact.ts +18 -2
  242. package/src/domains/session/compaction/cut-point.ts +20 -1
  243. package/src/domains/session/compaction/tokens.ts +48 -3
  244. package/src/domains/session/context-accounting.ts +8 -1
  245. package/src/domains/session/continuity/carry.ts +59 -0
  246. package/src/domains/session/continuity/contract.ts +592 -0
  247. package/src/domains/session/continuity/evidence.ts +290 -0
  248. package/src/domains/session/continuity/fold.ts +1075 -0
  249. package/src/domains/session/continuity/note.ts +79 -0
  250. package/src/domains/session/continuity/operator-request.ts +104 -0
  251. package/src/domains/session/continuity/persistence.ts +408 -0
  252. package/src/domains/session/continuity/ports.ts +231 -0
  253. package/src/domains/session/continuity/projection.ts +538 -0
  254. package/src/domains/session/continuity/validate.ts +352 -0
  255. package/src/domains/session/entries.ts +50 -3
  256. package/src/domains/session/index.ts +48 -0
  257. package/src/domains/session/migrations/index.ts +7 -3
  258. package/src/domains/session/tree/fork.ts +15 -1
  259. package/src/domains/session/usage.ts +2 -2
  260. package/src/engine/acp/commands.ts +1 -1
  261. package/src/engine/agent.ts +133 -9
  262. package/src/engine/session.ts +13 -6
  263. package/src/entry/orchestrator.ts +116 -33
  264. package/src/interactive/chat-loop-messages.ts +2 -2
  265. package/src/interactive/chat-loop.ts +219 -63
  266. package/src/interactive/chat-renderer.ts +99 -8
  267. package/src/interactive/context-overlay.ts +24 -3
  268. package/src/interactive/continuity-controller.ts +526 -0
  269. package/src/interactive/dispatch-board.ts +21 -3
  270. package/src/interactive/footer/dashboard.ts +25 -1
  271. package/src/interactive/footer/key-hints.ts +2 -2
  272. package/src/interactive/footer/pages.ts +70 -27
  273. package/src/interactive/footer/widgets.ts +32 -7
  274. package/src/interactive/footer-panel.ts +1 -1
  275. package/src/interactive/interactive-application.ts +7 -2
  276. package/src/interactive/interactive-input-runtime.ts +2 -2
  277. package/src/interactive/interactive-presentation.ts +15 -0
  278. package/src/interactive/interactive-slash-runtime.ts +45 -8
  279. package/src/interactive/interactive-tickers.ts +7 -1
  280. package/src/interactive/model-session-replay.ts +130 -3
  281. package/src/interactive/output-reserve.ts +35 -0
  282. package/src/interactive/overlay-general-openers.ts +12 -8
  283. package/src/interactive/overlay-key-routing.ts +6 -9
  284. package/src/interactive/overlay-lifecycle.ts +10 -6
  285. package/src/interactive/overlay-session-lifecycle.ts +21 -14
  286. package/src/interactive/quota-view.ts +229 -0
  287. package/src/interactive/session-last-turn.ts +1 -1
  288. package/src/interactive/session-transcript.ts +2 -1
  289. package/src/interactive/session-usage-reseed.ts +2 -2
  290. package/src/interactive/side-question.ts +2 -2
  291. package/src/interactive/slash-commands.ts +28 -9
  292. package/src/interactive/turn-context.ts +686 -63
  293. package/src/interactive/turn-middleware.ts +39 -6
  294. package/src/interactive/turn-persistence.ts +15 -2
  295. package/src/interactive/turn-prewarm.ts +1 -1
  296. package/src/interactive/turn-runtime.ts +48 -5
  297. package/src/interactive/{cost-overlay.ts → usage-overlay.ts} +121 -33
  298. package/src/interactive/welcome-dashboard.ts +26 -3
  299. package/src/tools/agent-tools.ts +3 -1
  300. package/src/tools/bootstrap.ts +6 -0
  301. package/src/tools/builtin-tool-catalog.ts +9 -0
  302. package/src/tools/context/index.ts +148 -113
  303. package/src/tools/context/surface.ts +11 -7
  304. package/src/tools/core-bootstrap.ts +4 -0
  305. package/src/tools/observation.ts +8 -2
  306. package/src/tools/policy.ts +3 -0
  307. package/src/tools/self-compact.ts +31 -0
  308. package/src/tools/surface.ts +1 -0
  309. package/src/tools/tasks.ts +1 -1
  310. package/dist/chunk-CTFPFW3H.js +0 -44
  311. package/dist/chunk-EOJPDNUP.js +0 -1325
  312. package/dist/chunk-I4Y4WKZR.js +0 -386
@@ -0,0 +1,2551 @@
1
+ import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
+ import {
3
+ probeWorkspace
4
+ } from "./chunk-DNHVRBH4.js";
5
+ import {
6
+ entryBelongsToPath
7
+ } from "./chunk-O5F25NIE.js";
8
+ import {
9
+ collectSessionEntries,
10
+ enrichForkMeta,
11
+ listSessionsForCwd,
12
+ operatorTextOfUserPayload
13
+ } from "./chunk-CNXU7TR2.js";
14
+ import {
15
+ stripTokenizerSentinels
16
+ } from "./chunk-MCJKEGHO.js";
17
+ import {
18
+ estimateAgentMessageTokens
19
+ } from "./chunk-53P775GE.js";
20
+ import {
21
+ HANDOFF_MAX_WINDOW_MS,
22
+ HANDOFF_NOTE_MAX_BYTES,
23
+ HANDOFF_RECOVERY_REQUEST_CUSTOM_TYPE,
24
+ HANDOFF_SCHEMA_VERSION,
25
+ canonicalJson,
26
+ isContinuityCarryingSummary,
27
+ isContinuityCheckpointPayload,
28
+ isContinuityCommitEntry,
29
+ isHandoffRecoveryRequestEntry,
30
+ isHandoffTransactionEntry,
31
+ isSessionEntry,
32
+ isSessionHeader
33
+ } from "./chunk-F6KCJO3U.js";
34
+ import {
35
+ CURRENT_SESSION_FORMAT_VERSION,
36
+ appendSessionFileEntry,
37
+ atomicWrite,
38
+ createSession,
39
+ openSession,
40
+ readSessionFileEntries,
41
+ readSessionMeta,
42
+ resumeSession,
43
+ sessionPaths
44
+ } from "./chunk-PZBQUJ2F.js";
45
+ import {
46
+ protectArtifact
47
+ } from "./chunk-OYUS3UZP.js";
48
+ import {
49
+ clioStatePath,
50
+ stateRootRemoved
51
+ } from "./chunk-CTTI7G3T.js";
52
+ import {
53
+ BusChannels
54
+ } from "./chunk-PS6JAWJZ.js";
55
+ import {
56
+ init_esm_shims
57
+ } from "./chunk-ECBHH7LU.js";
58
+
59
+ // src/core/response-model-id.ts
60
+ init_esm_shims();
61
+ function emptyResponseModelIdObservationCounts() {
62
+ return { reportedCalls: 0, notReportedCalls: 0, notObservedCalls: 0, legacyDifferenceOnlyCalls: 0 };
63
+ }
64
+ function addResponseModelIdObservationCount(counts, observation, calls = 1) {
65
+ switch (observation.state) {
66
+ case "reported":
67
+ counts.reportedCalls += calls;
68
+ break;
69
+ case "not-reported":
70
+ counts.notReportedCalls += calls;
71
+ break;
72
+ case "not-observed":
73
+ counts.notObservedCalls += calls;
74
+ break;
75
+ case "legacy-difference-only":
76
+ counts.legacyDifferenceOnlyCalls += calls;
77
+ break;
78
+ }
79
+ }
80
+ function addResponseModelIdObservationCounts(counts, additional) {
81
+ counts.reportedCalls += additional.reportedCalls;
82
+ counts.notReportedCalls += additional.notReportedCalls;
83
+ counts.notObservedCalls += additional.notObservedCalls;
84
+ counts.legacyDifferenceOnlyCalls += additional.legacyDifferenceOnlyCalls;
85
+ }
86
+ function responseModelIdObservationCountsLabel(counts) {
87
+ const parts = [];
88
+ if (counts.reportedCalls > 0) parts.push(`reported ${counts.reportedCalls}`);
89
+ if (counts.notReportedCalls > 0) parts.push(`not reported ${counts.notReportedCalls}`);
90
+ if (counts.notObservedCalls > 0) parts.push(`not observed ${counts.notObservedCalls}`);
91
+ if (counts.legacyDifferenceOnlyCalls > 0) parts.push(`legacy difference-only ${counts.legacyDifferenceOnlyCalls}`);
92
+ return parts.join(", ");
93
+ }
94
+ function nonEmptyString(value) {
95
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
96
+ }
97
+ function parsedObservation(value) {
98
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
99
+ const record = value;
100
+ switch (record.state) {
101
+ case "reported": {
102
+ const reportedModelId = nonEmptyString(record.reportedModelId);
103
+ return reportedModelId === null ? null : { state: "reported", reportedModelId };
104
+ }
105
+ case "not-reported":
106
+ return { state: "not-reported" };
107
+ case "not-observed":
108
+ return { state: "not-observed" };
109
+ case "legacy-difference-only":
110
+ return { state: "legacy-difference-only", differingModelId: nonEmptyString(record.differingModelId) };
111
+ default:
112
+ return null;
113
+ }
114
+ }
115
+ function responseModelIdObservationFromRecord(record, missing) {
116
+ const current = parsedObservation(record.responseModelIdObservation);
117
+ if (current !== null) return current;
118
+ if (Object.hasOwn(record, "servedModel")) {
119
+ const reportedModelId = nonEmptyString(record.servedModel);
120
+ return reportedModelId === null ? { state: "not-reported" } : { state: "reported", reportedModelId };
121
+ }
122
+ if (missing === "not-observed") return { state: "not-observed" };
123
+ return { state: "legacy-difference-only", differingModelId: nonEmptyString(record.responseModel) };
124
+ }
125
+ function attributedModelId(observation, requestedModelId, differingResponseModelId) {
126
+ switch (observation.state) {
127
+ case "reported":
128
+ return observation.reportedModelId;
129
+ case "not-reported":
130
+ return "unknown";
131
+ case "not-observed":
132
+ return differingResponseModelId ?? requestedModelId;
133
+ case "legacy-difference-only":
134
+ return observation.differingModelId ?? requestedModelId;
135
+ }
136
+ }
137
+
138
+ // src/domains/session/archive-readers.ts
139
+ init_esm_shims();
140
+ import { readdir, readFile } from "node:fs/promises";
141
+ import { join as join3 } from "node:path";
142
+
143
+ // src/domains/session/index.ts
144
+ init_esm_shims();
145
+
146
+ // src/domains/session/extension.ts
147
+ init_esm_shims();
148
+
149
+ // src/domains/session/checkpoint.ts
150
+ init_esm_shims();
151
+ async function performCheckpoint(state, reason) {
152
+ await state.writer.persistTree();
153
+ if (stateRootRemoved()) return;
154
+ const paths = sessionPaths(state.meta);
155
+ const at = (/* @__PURE__ */ new Date()).toISOString();
156
+ const enriched = {
157
+ ...state.meta,
158
+ lastCheckpointAt: at,
159
+ lastCheckpointReason: reason ?? null
160
+ };
161
+ atomicWrite(paths.meta, JSON.stringify(enriched, null, 2));
162
+ Object.assign(state.meta, enriched);
163
+ }
164
+
165
+ // src/domains/session/manager.ts
166
+ init_esm_shims();
167
+
168
+ // node_modules/.pnpm/uuid@14.0.1/node_modules/uuid/dist-node/index.js
169
+ init_esm_shims();
170
+
171
+ // node_modules/.pnpm/uuid@14.0.1/node_modules/uuid/dist-node/stringify.js
172
+ init_esm_shims();
173
+ var byteToHex = [];
174
+ for (let i = 0; i < 256; ++i) {
175
+ byteToHex.push((i + 256).toString(16).slice(1));
176
+ }
177
+ function unsafeStringify(arr, offset = 0) {
178
+ 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();
179
+ }
180
+
181
+ // node_modules/.pnpm/uuid@14.0.1/node_modules/uuid/dist-node/rng.js
182
+ init_esm_shims();
183
+ var rnds8 = new Uint8Array(16);
184
+ function rng() {
185
+ return crypto.getRandomValues(rnds8);
186
+ }
187
+
188
+ // node_modules/.pnpm/uuid@14.0.1/node_modules/uuid/dist-node/v7.js
189
+ init_esm_shims();
190
+ var _state = {};
191
+ function v7(options, buf, offset) {
192
+ let bytes;
193
+ if (options) {
194
+ bytes = v7Bytes(options.random ?? options.rng?.() ?? rng(), options.msecs, options.seq, buf, offset);
195
+ } else {
196
+ const now = Date.now();
197
+ const rnds = rng();
198
+ updateV7State(_state, now, rnds);
199
+ bytes = v7Bytes(rnds, _state.msecs, _state.seq, buf, offset);
200
+ }
201
+ return buf ?? unsafeStringify(bytes);
202
+ }
203
+ function updateV7State(state, now, rnds) {
204
+ state.msecs ??= -Infinity;
205
+ state.seq ??= 0;
206
+ if (now > state.msecs) {
207
+ state.seq = rnds[6] << 23 | rnds[7] << 16 | rnds[8] << 8 | rnds[9];
208
+ state.msecs = now;
209
+ } else {
210
+ state.seq = state.seq + 1 | 0;
211
+ if (state.seq === 0) {
212
+ state.msecs++;
213
+ }
214
+ }
215
+ return state;
216
+ }
217
+ function v7Bytes(rnds, msecs, seq, buf, offset = 0) {
218
+ if (rnds.length < 16) {
219
+ throw new Error("Random bytes length must be >= 16");
220
+ }
221
+ if (!buf) {
222
+ buf = new Uint8Array(16);
223
+ offset = 0;
224
+ } else {
225
+ if (offset < 0 || offset + 16 > buf.length) {
226
+ throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
227
+ }
228
+ }
229
+ msecs ??= Date.now();
230
+ seq ??= rnds[6] * 127 << 24 | rnds[7] << 16 | rnds[8] << 8 | rnds[9];
231
+ buf[offset++] = msecs / 1099511627776 & 255;
232
+ buf[offset++] = msecs / 4294967296 & 255;
233
+ buf[offset++] = msecs / 16777216 & 255;
234
+ buf[offset++] = msecs / 65536 & 255;
235
+ buf[offset++] = msecs / 256 & 255;
236
+ buf[offset++] = msecs & 255;
237
+ buf[offset++] = 112 | seq >>> 28 & 15;
238
+ buf[offset++] = seq >>> 20 & 255;
239
+ buf[offset++] = 128 | seq >>> 14 & 63;
240
+ buf[offset++] = seq >>> 6 & 255;
241
+ buf[offset++] = seq << 2 & 255 | rnds[10] & 3;
242
+ buf[offset++] = rnds[11];
243
+ buf[offset++] = rnds[12];
244
+ buf[offset++] = rnds[13];
245
+ buf[offset++] = rnds[14];
246
+ buf[offset++] = rnds[15];
247
+ return buf;
248
+ }
249
+ var v7_default = v7;
250
+
251
+ // src/domains/session/migrations/index.ts
252
+ init_esm_shims();
253
+ var OLDEST_READABLE_SESSION_FORMAT_VERSION = 3;
254
+ function runMigrations(meta, sessionPath) {
255
+ const from = meta.sessionFormatVersion ?? 1;
256
+ if (from < OLDEST_READABLE_SESSION_FORMAT_VERSION) {
257
+ throw new Error(
258
+ `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.`
259
+ );
260
+ }
261
+ if (from > CURRENT_SESSION_FORMAT_VERSION) {
262
+ throw new Error(
263
+ `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.`
264
+ );
265
+ }
266
+ if (from < CURRENT_SESSION_FORMAT_VERSION) {
267
+ return { migrated: true, from, to: CURRENT_SESSION_FORMAT_VERSION };
268
+ }
269
+ return { migrated: false, from, to: from };
270
+ }
271
+
272
+ // src/domains/session/manager.ts
273
+ function newTurnId() {
274
+ return v7_default();
275
+ }
276
+ function startSession(input) {
277
+ const { meta, writer } = createSession({
278
+ cwd: input.cwd,
279
+ model: input.model ?? null,
280
+ target: input.target ?? null,
281
+ ...input.initialEntries !== void 0 ? { initialEntries: input.initialEntries } : {},
282
+ ...input.initialTree !== void 0 ? { initialTree: input.initialTree } : {},
283
+ ...input.parentSession !== void 0 ? { parentSession: input.parentSession } : {},
284
+ ...input.parentTurnId !== void 0 ? { parentTurnId: input.parentTurnId } : {}
285
+ });
286
+ return { meta, writer };
287
+ }
288
+ function persistSessionMeta(state) {
289
+ if (stateRootRemoved()) return;
290
+ atomicWrite(sessionPaths(state.meta).meta, JSON.stringify(state.meta, null, 2));
291
+ }
292
+ function resumeSessionState(sessionId) {
293
+ const persistedMeta = readSessionMeta(sessionId);
294
+ const paths = sessionPaths(persistedMeta);
295
+ const migration = runMigrations(persistedMeta, paths.meta);
296
+ const candidate = openSession(sessionId);
297
+ collectSessionEntries(candidate.turns(), paths.current);
298
+ const { meta, writer, tree } = resumeSession(
299
+ sessionId,
300
+ migration.migrated ? { sessionFormatVersion: migration.to } : {}
301
+ );
302
+ const state = { meta, writer };
303
+ return { state, nodes: tree };
304
+ }
305
+ function appendTurn(state, input) {
306
+ const record = {
307
+ id: input.id ?? newTurnId(),
308
+ parentId: input.parentId,
309
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
310
+ kind: input.kind,
311
+ payload: input.payload
312
+ };
313
+ const entry = {
314
+ kind: "message",
315
+ turnId: record.id,
316
+ parentTurnId: record.parentId,
317
+ timestamp: record.at,
318
+ role: record.kind,
319
+ payload: record.payload
320
+ };
321
+ state.writer.appendEntry(entry, {
322
+ treeNode: {
323
+ id: record.id,
324
+ parentId: record.parentId,
325
+ at: record.at,
326
+ kind: record.kind
327
+ }
328
+ });
329
+ return record;
330
+ }
331
+ function appendEntry(state, input) {
332
+ const turnId = input.turnId ?? newTurnId();
333
+ const timestamp = input.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
334
+ const entry = { ...input, turnId, timestamp };
335
+ if (!isSessionEntry(entry)) throw new Error(`session.appendEntry: invalid ${String(input.kind)} entry`);
336
+ state.writer.appendEntry(entry);
337
+ return entry;
338
+ }
339
+ function replaceEntries(state, entries) {
340
+ for (const entry of entries) {
341
+ if (!isSessionEntry(entry)) throw new Error("session.replaceEntries: invalid entry");
342
+ }
343
+ state.writer.replaceEntries(entries);
344
+ }
345
+
346
+ // src/domains/session/tree/fork.ts
347
+ init_esm_shims();
348
+ function linkedRecordFromEntry(entry) {
349
+ return {
350
+ id: entry.turnId,
351
+ parentId: entry.parentTurnId,
352
+ timestamp: entry.timestamp,
353
+ treeKind: entry.kind === "message" ? entry.role : null,
354
+ raw: entry
355
+ };
356
+ }
357
+ function traceAncestry(records, leafTurnId) {
358
+ const byId = /* @__PURE__ */ new Map();
359
+ for (const record of records) byId.set(record.id, record);
360
+ const path = [];
361
+ let current = byId.get(leafTurnId);
362
+ if (!current) throw new Error(`session.fork: parent turn not found: ${leafTurnId}`);
363
+ const seen = /* @__PURE__ */ new Set();
364
+ while (current) {
365
+ if (seen.has(current.id)) throw new Error(`session.fork: cycle in parent chain at ${current.id}`);
366
+ seen.add(current.id);
367
+ path.unshift(current);
368
+ if (current.parentId === null) break;
369
+ const next = byId.get(current.parentId);
370
+ if (!next) throw new Error(`session.fork: broken parent chain at ${current.id}`);
371
+ current = next;
372
+ }
373
+ return path;
374
+ }
375
+ function treeFromLinearPath(path) {
376
+ return path.filter((record) => record.treeKind !== null).map((record) => ({
377
+ id: record.id,
378
+ parentId: record.parentId,
379
+ at: record.timestamp,
380
+ kind: record.treeKind
381
+ }));
382
+ }
383
+ function sessionEntryBelongsToPath(entry, pathIds, atOrBeforeForkPoint) {
384
+ if (atOrBeforeForkPoint) return entryBelongsToPath(entry, pathIds, atOrBeforeForkPoint);
385
+ if (entry.kind === "compactionSummary") return false;
386
+ if (entry.kind === "handoffTransaction" || entry.kind === "continuityCommit") return false;
387
+ if (entry.kind === "custom" && entry.customType === HANDOFF_RECOVERY_REQUEST_CUSTOM_TYPE) return false;
388
+ return entryBelongsToPath(entry, pathIds, atOrBeforeForkPoint);
389
+ }
390
+ function branchEntriesFromParent(parentMeta, leafTurnId) {
391
+ const parentCurrentPath = sessionPaths(parentMeta).current;
392
+ const records = readSessionFileEntries(parentCurrentPath).filter((entry) => !isSessionHeader(entry));
393
+ const parsed = collectSessionEntries(records, parentCurrentPath);
394
+ const linked = parsed.map(linkedRecordFromEntry);
395
+ const path = traceAncestry(linked, leafTurnId);
396
+ const pathIds = new Set(path.map((record) => record.id));
397
+ const forkPointIndex = parsed.findIndex((entry) => linkedRecordFromEntry(entry).id === leafTurnId);
398
+ const entries = parsed.filter(
399
+ (entry, index) => sessionEntryBelongsToPath(entry, pathIds, forkPointIndex >= 0 && index <= forkPointIndex)
400
+ );
401
+ return {
402
+ parentCurrentPath,
403
+ entries,
404
+ tree: treeFromLinearPath(path)
405
+ };
406
+ }
407
+ function forkFromState(input) {
408
+ const parentMeta = input.from.meta;
409
+ input.from.writer.flushAppends();
410
+ const branch = branchEntriesFromParent(parentMeta, input.parentTurnId);
411
+ const cwd = input.cwd ?? parentMeta.cwd;
412
+ const next = startSession({
413
+ cwd,
414
+ model: parentMeta.model,
415
+ target: parentMeta.target,
416
+ initialEntries: branch.entries,
417
+ initialTree: branch.tree,
418
+ parentSession: branch.parentCurrentPath,
419
+ parentTurnId: input.parentTurnId
420
+ });
421
+ enrichForkMeta(next.meta, parentMeta.id, input.parentTurnId);
422
+ void input.from.writer.close();
423
+ return { next, parentMeta, nodes: branch.tree };
424
+ }
425
+
426
+ // src/domains/session/tree/manager.ts
427
+ init_esm_shims();
428
+ import { existsSync, renameSync, rmSync } from "node:fs";
429
+ import { dirname, join } from "node:path";
430
+ function readTreeBundle(sessionId) {
431
+ const reader = openSession(sessionId);
432
+ const meta = reader.meta();
433
+ const paths = sessionPaths(meta);
434
+ const nodes = [...reader.tree()];
435
+ const entries = collectSessionEntries(reader.turns(), paths.current);
436
+ const labels = resolveLabelMap(entries);
437
+ return { sessionId, meta, nodes, entries, labels };
438
+ }
439
+ function resolveLabelMap(entries) {
440
+ const out = /* @__PURE__ */ new Map();
441
+ for (const entry of entries) {
442
+ const info = labelFields(entry);
443
+ if (!info) continue;
444
+ const existing = out.get(info.targetTurnId);
445
+ if (existing && existing.timestamp > info.timestamp) continue;
446
+ if (info.label === void 0 || info.label === "") {
447
+ out.set(info.targetTurnId, { label: "", timestamp: info.timestamp });
448
+ continue;
449
+ }
450
+ out.set(info.targetTurnId, { label: info.label, timestamp: info.timestamp });
451
+ }
452
+ return out;
453
+ }
454
+ function labelFields(entry) {
455
+ if (entry.kind === "label") return entry;
456
+ if (entry.kind !== "sessionInfo") return null;
457
+ const info = entry;
458
+ if (!info.targetTurnId) return null;
459
+ const out = {
460
+ targetTurnId: info.targetTurnId,
461
+ timestamp: info.timestamp
462
+ };
463
+ if (info.label !== void 0) out.label = info.label;
464
+ return out;
465
+ }
466
+ function appendEntryToSessionFile(sessionId, entry) {
467
+ const meta = readSessionMeta(sessionId);
468
+ const paths = sessionPaths(meta);
469
+ appendSessionFileEntry(paths.current, entry);
470
+ }
471
+ function removeSessionDirectory(sessionId) {
472
+ const meta = readSessionMeta(sessionId);
473
+ const paths = sessionPaths(meta);
474
+ const dir = dirname(paths.meta);
475
+ rmSync(dir, { recursive: true, force: true });
476
+ }
477
+ function tombstoneSession(sessionId) {
478
+ const meta = readSessionMeta(sessionId);
479
+ const paths = sessionPaths(meta);
480
+ if (!existsSync(paths.meta)) return;
481
+ const tombstone = join(dirname(paths.meta), "meta.deleted.json");
482
+ renameSync(paths.meta, tombstone);
483
+ }
484
+
485
+ // src/domains/session/tree/navigator.ts
486
+ init_esm_shims();
487
+ function pickMeta(meta) {
488
+ const out = {
489
+ id: meta.id,
490
+ cwd: meta.cwd,
491
+ createdAt: meta.createdAt,
492
+ endedAt: meta.endedAt,
493
+ model: meta.model,
494
+ target: meta.target
495
+ };
496
+ if (meta.parentSessionId !== void 0) out.parentSessionId = meta.parentSessionId;
497
+ if (meta.parentTurnId !== void 0) out.parentTurnId = meta.parentTurnId;
498
+ return out;
499
+ }
500
+ function computeLeafId(nodes) {
501
+ if (nodes.length === 0) return null;
502
+ const hasChild = /* @__PURE__ */ new Set();
503
+ for (const node of nodes) {
504
+ if (node.parentId) hasChild.add(node.parentId);
505
+ }
506
+ let leaf = null;
507
+ for (const node of nodes) {
508
+ if (hasChild.has(node.id)) continue;
509
+ if (!leaf || node.at >= leaf.at) leaf = node;
510
+ }
511
+ return leaf?.id ?? null;
512
+ }
513
+ function buildTreeSnapshot(input) {
514
+ const nodesById = {};
515
+ for (const node of input.nodes) {
516
+ const entry = {
517
+ id: node.id,
518
+ parentId: node.parentId,
519
+ at: node.at,
520
+ kind: node.kind,
521
+ children: []
522
+ };
523
+ const resolved = input.labels.get(node.id);
524
+ if (resolved && resolved.label !== "") entry.label = resolved.label;
525
+ const preview = input.previews?.get(node.id);
526
+ if (preview !== void 0 && preview.length > 0) entry.preview = preview;
527
+ nodesById[node.id] = entry;
528
+ }
529
+ const rootIds = [];
530
+ for (const node of input.nodes) {
531
+ const parent = node.parentId ? nodesById[node.parentId] : void 0;
532
+ if (!parent) {
533
+ rootIds.push(node.id);
534
+ } else {
535
+ parent.children.push(node.id);
536
+ }
537
+ }
538
+ const byTime = (a, b) => {
539
+ const na = nodesById[a];
540
+ const nb = nodesById[b];
541
+ if (!na || !nb) return a < b ? -1 : a > b ? 1 : 0;
542
+ if (na.at === nb.at) return na.id < nb.id ? -1 : na.id > nb.id ? 1 : 0;
543
+ return na.at < nb.at ? -1 : 1;
544
+ };
545
+ rootIds.sort(byTime);
546
+ for (const key of Object.keys(nodesById)) {
547
+ const entry = nodesById[key];
548
+ if (entry) entry.children.sort(byTime);
549
+ }
550
+ const leafId = input.leafId !== void 0 ? input.leafId : computeLeafId(input.nodes);
551
+ return {
552
+ sessionId: input.meta.id,
553
+ meta: pickMeta(input.meta),
554
+ leafId,
555
+ nodesById,
556
+ rootIds
557
+ };
558
+ }
559
+
560
+ // src/domains/session/tree/preview.ts
561
+ init_esm_shims();
562
+ var TURN_PREVIEW_MAX_CHARS = 60;
563
+ var ESC = String.fromCharCode(27);
564
+ var ANSI_CSI_RE = new RegExp(`${ESC}\\[[0-9;?]*[ -/]*[@-~]`, "g");
565
+ var ANSI_FALLBACK_RE = new RegExp(`${ESC}[@-_]`, "g");
566
+ function stripAnsi(text) {
567
+ return text.replace(ANSI_CSI_RE, "").replace(ANSI_FALLBACK_RE, "");
568
+ }
569
+ function collapseSingleLine(text) {
570
+ return text.replace(/\s+/g, " ").trim();
571
+ }
572
+ function sanitize(text) {
573
+ return collapseSingleLine(stripTokenizerSentinels(stripAnsi(text)));
574
+ }
575
+ function clamp(text, max) {
576
+ if (text.length <= max) return text;
577
+ if (max <= 1) return text.slice(0, max);
578
+ return `${text.slice(0, max - 1)}\u2026`;
579
+ }
580
+ function extractPlainText(payload) {
581
+ if (typeof payload === "string") {
582
+ const sanitized = sanitize(payload);
583
+ return sanitized.length > 0 ? sanitized : null;
584
+ }
585
+ if (!payload || typeof payload !== "object") return null;
586
+ const obj = payload;
587
+ if (typeof obj.text === "string") {
588
+ const sanitized = sanitize(obj.text);
589
+ if (sanitized.length > 0) return sanitized;
590
+ }
591
+ if (Array.isArray(obj.content)) {
592
+ const parts = [];
593
+ for (const block of obj.content) {
594
+ if (!block || typeof block !== "object") continue;
595
+ const b = block;
596
+ if (b.type === "text" && typeof b.text === "string") parts.push(b.text);
597
+ }
598
+ if (parts.length > 0) {
599
+ const sanitized = sanitize(parts.join(" "));
600
+ if (sanitized.length > 0) return sanitized;
601
+ }
602
+ }
603
+ return null;
604
+ }
605
+ function pickToolArg(toolName, args) {
606
+ if (!args || typeof args !== "object") return "";
607
+ const a = args;
608
+ const preferred = {
609
+ read: ["path"],
610
+ write: ["path"],
611
+ edit: ["path"],
612
+ bash: ["command"],
613
+ code_nav: ["query", "mode"],
614
+ git: ["op"],
615
+ verify: ["check"],
616
+ grep: ["pattern", "query"],
617
+ search: ["query", "pattern"],
618
+ web_fetch: ["url"],
619
+ web_search: ["query"]
620
+ };
621
+ const order = preferred[toolName] ?? [];
622
+ for (const key of order) {
623
+ const value = a[key];
624
+ if (typeof value === "string" && value.length > 0) return sanitize(value);
625
+ if (typeof value === "number") return String(value);
626
+ }
627
+ for (const [, value] of Object.entries(a)) {
628
+ if (typeof value === "string" && value.length > 0) return sanitize(value);
629
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
630
+ }
631
+ return "";
632
+ }
633
+ function extractToolCall(payload) {
634
+ if (!payload || typeof payload !== "object") return null;
635
+ const p = payload;
636
+ const toolName = typeof p.name === "string" ? p.name : typeof p.toolName === "string" ? p.toolName : null;
637
+ if (!toolName) return null;
638
+ const args = p.args !== void 0 ? p.args : p.arguments ?? p.input;
639
+ return { toolName, args };
640
+ }
641
+ function extractToolResult(payload) {
642
+ if (!payload || typeof payload !== "object") return null;
643
+ const p = payload;
644
+ if (!("toolCallId" in p) && !("result" in p) && !("isError" in p)) return null;
645
+ const isError = p.isError === true;
646
+ let text = "";
647
+ const result = p.result;
648
+ if (typeof result === "string") text = result;
649
+ else if (result && typeof result === "object") {
650
+ const r = result;
651
+ if (Array.isArray(r.content)) {
652
+ const parts = [];
653
+ for (const block of r.content) {
654
+ if (!block || typeof block !== "object") continue;
655
+ const b = block;
656
+ if (b.type === "text" && typeof b.text === "string") parts.push(b.text);
657
+ }
658
+ text = parts.join(" ");
659
+ } else if (typeof r.text === "string") {
660
+ text = r.text;
661
+ }
662
+ }
663
+ if (text.length === 0 && typeof p.errorMessage === "string") text = p.errorMessage;
664
+ return { isError, text: sanitize(text) };
665
+ }
666
+ function assistantStopMarker(payload) {
667
+ if (!payload || typeof payload !== "object") return null;
668
+ const sr = payload.stopReason;
669
+ if (sr === "aborted") return "(aborted)";
670
+ if (sr === "error") return "(error)";
671
+ return null;
672
+ }
673
+ function buildTurnPreview(turn, max = TURN_PREVIEW_MAX_CHARS) {
674
+ const budget = Math.max(1, max);
675
+ switch (turn.kind) {
676
+ case "user": {
677
+ const text = operatorTextOfUserPayload(turn.payload);
678
+ return text === null ? "(empty)" : clamp(sanitize(text), budget);
679
+ }
680
+ case "assistant": {
681
+ const stop = assistantStopMarker(turn.payload);
682
+ const text = extractPlainText(turn.payload);
683
+ if (text !== null) return clamp(text, budget);
684
+ if (stop !== null) return stop;
685
+ if (turn.payload && typeof turn.payload === "object" && Array.isArray(turn.payload.content)) {
686
+ const content = turn.payload.content;
687
+ const toolCalls = content.filter(
688
+ (b) => b && typeof b === "object" && b.type === "toolCall"
689
+ );
690
+ if (toolCalls.length > 0) {
691
+ const names = toolCalls.map((b) => b.name).filter((n) => typeof n === "string");
692
+ if (names.length > 0) return clamp(`(tool calls) ${names.join(", ")}`, budget);
693
+ }
694
+ }
695
+ return "(empty)";
696
+ }
697
+ case "tool_call": {
698
+ const call = extractToolCall(turn.payload);
699
+ if (!call) return "(empty)";
700
+ const arg = pickToolArg(call.toolName, call.args);
701
+ const inner = arg.length > 0 ? `"${clamp(arg, Math.max(8, budget - call.toolName.length - 4))}"` : "";
702
+ const composed = `${call.toolName}(${inner})`;
703
+ return clamp(composed, budget);
704
+ }
705
+ case "tool_result": {
706
+ const result = extractToolResult(turn.payload);
707
+ if (!result) return "(empty)";
708
+ const status = result.isError ? "err" : "ok";
709
+ const head = `[${status}]`;
710
+ if (result.text.length === 0) return head;
711
+ const remaining = Math.max(4, budget - head.length - 1);
712
+ return `${head} ${clamp(result.text, remaining)}`;
713
+ }
714
+ case "system": {
715
+ const text = extractPlainText(turn.payload);
716
+ return text === null ? "(system)" : clamp(text, budget);
717
+ }
718
+ case "checkpoint":
719
+ return "(checkpoint)";
720
+ default:
721
+ return `(${String(turn.kind)})`;
722
+ }
723
+ }
724
+
725
+ // src/domains/session/extension.ts
726
+ function createSessionBundle(context) {
727
+ let state = null;
728
+ let currentTurnId = null;
729
+ function emitPark(sessionId, reason) {
730
+ context.bus.emit(BusChannels.SessionParked, { sessionId, reason, at: Date.now() });
731
+ }
732
+ function emitResume(sessionId, via) {
733
+ context.bus.emit(BusChannels.SessionResumed, { sessionId, via, at: Date.now() });
734
+ }
735
+ function emitTurnSwitched(sessionId, turnId) {
736
+ context.bus.emit(BusChannels.SessionTurnSwitched, { sessionId, turnId, at: Date.now() });
737
+ }
738
+ function resolveLeafOnOpen(resumed) {
739
+ const pinned = resumed.state.meta.pinnedLeafTurnId;
740
+ if (pinned && resumed.nodes.some((node) => node.id === pinned)) return pinned;
741
+ return computeLeafId(resumed.nodes);
742
+ }
743
+ async function closeCurrent(reason = "close") {
744
+ if (!state) return;
745
+ const s = state;
746
+ emitPark(s.meta.id, reason);
747
+ state = null;
748
+ currentTurnId = null;
749
+ await s.writer.close();
750
+ }
751
+ async function flushIfCurrent(sessionId) {
752
+ if (state?.meta.id === sessionId) {
753
+ await state.writer.persistTree();
754
+ }
755
+ }
756
+ function structuralTreeNodes(entries, previews) {
757
+ const nodes = [];
758
+ for (const entry of entries) {
759
+ if (entry.kind === "compactionSummary") {
760
+ nodes.push({ id: entry.turnId, parentId: entry.parentTurnId, at: entry.timestamp, kind: "compaction" });
761
+ const summarized = entry.messagesSummarized === void 0 ? "history" : `${entry.messagesSummarized} entries`;
762
+ const after = entry.tokensAfter === void 0 ? "" : ` -> ~${entry.tokensAfter}`;
763
+ previews.set(entry.turnId, `${summarized} summarized, ~${entry.tokensBefore}${after} tokens`);
764
+ continue;
765
+ }
766
+ if (entry.kind === "branchSummary") {
767
+ nodes.push({ id: entry.turnId, parentId: entry.parentTurnId, at: entry.timestamp, kind: "branch" });
768
+ const text = entry.summary.split("\n", 1)[0] ?? "";
769
+ previews.set(
770
+ entry.turnId,
771
+ text.length > 0 ? `returned from ${entry.fromTurnId}: ${text}` : `returned from ${entry.fromTurnId}`
772
+ );
773
+ }
774
+ }
775
+ return nodes;
776
+ }
777
+ function snapshotFor(sessionId) {
778
+ const bundle = readTreeBundle(sessionId);
779
+ const meta = state?.meta.id === sessionId ? state.meta : bundle.meta;
780
+ const previews = /* @__PURE__ */ new Map();
781
+ let structural = [];
782
+ try {
783
+ for (const entry of bundle.entries) {
784
+ if (entry.kind !== "message") continue;
785
+ const text = buildTurnPreview({ kind: entry.role, payload: entry.payload });
786
+ if (text.length > 0) previews.set(entry.turnId, text);
787
+ }
788
+ structural = structuralTreeNodes(bundle.entries, previews);
789
+ } catch {
790
+ }
791
+ const persistedLeaf = bundle.meta.pinnedLeafTurnId && bundle.nodes.some((node) => node.id === bundle.meta.pinnedLeafTurnId) ? bundle.meta.pinnedLeafTurnId : computeLeafId(bundle.nodes);
792
+ return buildTreeSnapshot({
793
+ meta,
794
+ nodes: [...bundle.nodes, ...structural],
795
+ labels: bundle.labels,
796
+ previews,
797
+ // The leaf is the next append point, so it is computed over turn nodes
798
+ // only: a structural node marks a moment in the timeline, never a place
799
+ // the session continues from.
800
+ leafId: state?.meta.id === sessionId ? currentTurnId : persistedLeaf
801
+ });
802
+ }
803
+ const contract = {
804
+ current: () => state?.meta ?? null,
805
+ create(input) {
806
+ const cwd = input?.cwd ?? process.cwd();
807
+ const startInput = { cwd };
808
+ if (input?.model !== void 0) startInput.model = input.model;
809
+ if (input?.target !== void 0) startInput.target = input.target;
810
+ if (state) {
811
+ const prior = state;
812
+ emitPark(prior.meta.id, "create_new");
813
+ state = null;
814
+ void prior.writer.close();
815
+ }
816
+ const next = startSession(startInput);
817
+ next.meta.workspace = probeWorkspace(cwd);
818
+ persistSessionMeta(next);
819
+ state = next;
820
+ currentTurnId = null;
821
+ return next.meta;
822
+ },
823
+ append(turn) {
824
+ if (!state) throw new Error("session.append: no current session");
825
+ if (turn.parentId !== currentTurnId) {
826
+ throw new Error(
827
+ `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`
828
+ );
829
+ }
830
+ const record = appendTurn(state, turn);
831
+ currentTurnId = record.id;
832
+ if (state.meta.pinnedLeafTurnId !== void 0 && state.meta.pinnedLeafTurnId !== null) {
833
+ state.meta.pinnedLeafTurnId = null;
834
+ persistSessionMeta(state);
835
+ }
836
+ return record;
837
+ },
838
+ appendEntry(entry) {
839
+ if (!state) throw new Error("session.appendEntry: no current session");
840
+ return appendEntry(state, entry);
841
+ },
842
+ replaceEntries(entries) {
843
+ if (!state) throw new Error("session.replaceEntries: no current session");
844
+ replaceEntries(state, entries);
845
+ },
846
+ recordSkillActivation(activation) {
847
+ if (!state) throw new Error("session.recordSkillActivation: no current session");
848
+ appendEntry(state, {
849
+ kind: "skillActivation",
850
+ parentTurnId: activation.turnId ?? null,
851
+ activation
852
+ });
853
+ const existing = state.meta.skillActivations ?? [];
854
+ state.meta.skillActivations = [...existing, activation];
855
+ persistSessionMeta(state);
856
+ return activation;
857
+ },
858
+ async checkpoint(reason) {
859
+ if (!state) throw new Error("session.checkpoint: no current session");
860
+ await performCheckpoint(state, reason);
861
+ },
862
+ flushAppends() {
863
+ if (!state) throw new Error("session.flushAppends: no current session");
864
+ state.writer.flushAppends();
865
+ },
866
+ resume(sessionId) {
867
+ if (state && state.meta.id === sessionId) return state.meta;
868
+ const resumed = resumeSessionState(sessionId);
869
+ if (state) {
870
+ const prior = state;
871
+ emitPark(prior.meta.id, "resume_other");
872
+ void prior.writer.close();
873
+ }
874
+ state = resumed.state;
875
+ currentTurnId = resolveLeafOnOpen(resumed);
876
+ emitResume(resumed.state.meta.id, "resume");
877
+ return resumed.state.meta;
878
+ },
879
+ fork(parentTurnId, input) {
880
+ if (!state) throw new Error("session.fork: no current session to fork from");
881
+ const prior = state;
882
+ const { next, nodes } = forkFromState({
883
+ from: prior,
884
+ parentTurnId,
885
+ ...input?.cwd !== void 0 ? { cwd: input.cwd } : {}
886
+ });
887
+ emitPark(prior.meta.id, "fork");
888
+ state = next;
889
+ currentTurnId = computeLeafId(nodes);
890
+ return next.meta;
891
+ },
892
+ tree(sessionId) {
893
+ const id = sessionId ?? state?.meta.id;
894
+ if (!id) throw new Error("session.tree: no sessionId provided and no current session");
895
+ if (state?.meta.id === id) void flushIfCurrent(id);
896
+ return snapshotFor(id);
897
+ },
898
+ switchBranch(sessionId) {
899
+ if (state?.meta.id === sessionId) return state.meta;
900
+ const resumed = resumeSessionState(sessionId);
901
+ if (state) {
902
+ const prior = state;
903
+ emitPark(prior.meta.id, "switch_branch");
904
+ void prior.writer.close();
905
+ }
906
+ state = resumed.state;
907
+ currentTurnId = resolveLeafOnOpen(resumed);
908
+ emitResume(resumed.state.meta.id, "switch_branch");
909
+ return resumed.state.meta;
910
+ },
911
+ switchTurn(turnId) {
912
+ if (!state) throw new Error("session.switchTurn: no current session");
913
+ void flushIfCurrent(state.meta.id);
914
+ const nodes = readTreeBundle(state.meta.id).nodes;
915
+ if (!nodes.some((node) => node.id === turnId)) {
916
+ throw new Error(`session.switchTurn: turn not found: ${turnId}`);
917
+ }
918
+ currentTurnId = turnId;
919
+ state.meta.pinnedLeafTurnId = turnId;
920
+ persistSessionMeta(state);
921
+ emitTurnSwitched(state.meta.id, turnId);
922
+ return state.meta;
923
+ },
924
+ editLabel(turnId, label, sessionId) {
925
+ const targetId = sessionId ?? state?.meta.id;
926
+ if (!targetId) throw new Error("session.editLabel: no sessionId provided and no current session");
927
+ if (state && state.meta.id === targetId) {
928
+ appendEntry(state, {
929
+ kind: "label",
930
+ parentTurnId: null,
931
+ targetTurnId: turnId,
932
+ label
933
+ });
934
+ return;
935
+ }
936
+ const entry = {
937
+ kind: "label",
938
+ turnId: newTurnId(),
939
+ parentTurnId: null,
940
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
941
+ targetTurnId: turnId,
942
+ label
943
+ };
944
+ appendEntryToSessionFile(targetId, entry);
945
+ },
946
+ setName(name, sessionId) {
947
+ const targetId = sessionId ?? state?.meta.id;
948
+ if (!targetId) throw new Error("session.setName: no sessionId provided and no current session");
949
+ if (state && state.meta.id === targetId) {
950
+ appendEntry(state, {
951
+ kind: "sessionInfo",
952
+ parentTurnId: null,
953
+ name
954
+ });
955
+ return;
956
+ }
957
+ const entry = {
958
+ kind: "sessionInfo",
959
+ turnId: newTurnId(),
960
+ parentTurnId: null,
961
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
962
+ name
963
+ };
964
+ appendEntryToSessionFile(targetId, entry);
965
+ },
966
+ deleteSession(id, opts) {
967
+ if (state?.meta.id === id) {
968
+ throw new Error("session.deleteSession: refusing to delete the currently open session; close() first");
969
+ }
970
+ const options = opts ?? {};
971
+ if (options.keepFiles) {
972
+ tombstoneSession(id);
973
+ } else {
974
+ removeSessionDirectory(id);
975
+ }
976
+ },
977
+ history() {
978
+ const cwd = state?.meta.cwd ?? process.cwd();
979
+ return listSessionsForCwd(cwd);
980
+ },
981
+ async close() {
982
+ await closeCurrent();
983
+ }
984
+ };
985
+ const extension = {
986
+ async start() {
987
+ },
988
+ async stop() {
989
+ if (!state) return;
990
+ try {
991
+ await performCheckpoint(state, "shutdown");
992
+ } catch (err) {
993
+ process.stderr.write(
994
+ `[clio-coder:session] shutdown checkpoint failed: ${err instanceof Error ? err.message : String(err)}
995
+ `
996
+ );
997
+ }
998
+ await closeCurrent("shutdown");
999
+ }
1000
+ };
1001
+ return { extension, contract };
1002
+ }
1003
+
1004
+ // src/domains/session/manifest.ts
1005
+ init_esm_shims();
1006
+ var SessionManifest = {
1007
+ name: "session",
1008
+ dependsOn: ["config"]
1009
+ };
1010
+
1011
+ // src/domains/session/continuity/ports.ts
1012
+ init_esm_shims();
1013
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
1014
+ import { join as join2 } from "node:path";
1015
+ function pinnedLedgerPath(origin) {
1016
+ return join2(clioStatePath(), "sessions", origin.cwdHash, origin.sessionId, "current.jsonl");
1017
+ }
1018
+ function readContinuityRecordExact(origin, expected) {
1019
+ const path = pinnedLedgerPath(origin);
1020
+ if (!existsSync2(path)) {
1021
+ const temp = `${path}.tmp`;
1022
+ if (existsSync2(temp)) {
1023
+ return { status: "unresolved", detail: "session ledger is missing and a temporary candidate exists beside it" };
1024
+ }
1025
+ return { status: "unresolved", detail: "session ledger does not exist" };
1026
+ }
1027
+ let raw;
1028
+ try {
1029
+ raw = readFileSync(path, "utf8");
1030
+ } catch (error) {
1031
+ return {
1032
+ status: "unresolved",
1033
+ detail: `session ledger could not be read: ${error instanceof Error ? error.message : String(error)}`
1034
+ };
1035
+ }
1036
+ const wanted = canonicalJson(expected);
1037
+ let found = 0;
1038
+ let conflicting = null;
1039
+ let headers = 0;
1040
+ for (const line of raw.split("\n")) {
1041
+ if (line.trim().length === 0) continue;
1042
+ let parsed;
1043
+ try {
1044
+ parsed = JSON.parse(line);
1045
+ } catch {
1046
+ return { status: "unresolved", detail: "session ledger contains a line that could not be parsed" };
1047
+ }
1048
+ if (isSessionHeader(parsed)) {
1049
+ headers += 1;
1050
+ if (headers > 1) return { status: "unresolved", detail: "session ledger carries more than one session header" };
1051
+ if (parsed.id !== origin.sessionId) {
1052
+ return {
1053
+ status: "unresolved",
1054
+ detail: `session ledger carries session header ${parsed.id}, not ${origin.sessionId}`
1055
+ };
1056
+ }
1057
+ if (parsed.version < OLDEST_READABLE_SESSION_FORMAT_VERSION || parsed.version > CURRENT_SESSION_FORMAT_VERSION) {
1058
+ return { status: "unresolved", detail: `session ledger header declares format version ${parsed.version}` };
1059
+ }
1060
+ continue;
1061
+ }
1062
+ if (!isSessionEntry(parsed)) {
1063
+ return { status: "unresolved", detail: "session ledger contains a structured record this build cannot validate" };
1064
+ }
1065
+ const record = parsed;
1066
+ if (record.turnId !== expected.turnId) continue;
1067
+ if (canonicalJson(record) === wanted) {
1068
+ found += 1;
1069
+ continue;
1070
+ }
1071
+ conflicting = `stored record ${String(expected.turnId)} has this id and different payload`;
1072
+ }
1073
+ if (conflicting !== null) return { status: "conflicting", detail: conflicting };
1074
+ if (headers === 0) {
1075
+ return { status: "unresolved", detail: "session ledger carries no session header to attribute it to this session" };
1076
+ }
1077
+ return found > 0 ? { status: "matching" } : { status: "absent" };
1078
+ }
1079
+ function createContinuityPersistencePorts(input) {
1080
+ const session = input.session;
1081
+ const origin = {
1082
+ sessionId: input.origin.sessionId,
1083
+ cwdHash: input.origin.cwdHash,
1084
+ ...input.origin.stillCurrent === void 0 ? {} : { stillCurrent: input.origin.stillCurrent }
1085
+ };
1086
+ const isOriginCurrent = () => {
1087
+ if (session.current()?.id !== origin.sessionId) return false;
1088
+ return origin.stillCurrent?.() ?? true;
1089
+ };
1090
+ const ports = {
1091
+ append(entry) {
1092
+ if (!isOriginCurrent()) {
1093
+ throw new Error(`continuity append refused: session ${origin.sessionId} is no longer current`);
1094
+ }
1095
+ if (typeof entry.turnId !== "string" || entry.turnId.length === 0) {
1096
+ throw new Error("continuity append refused: the record carries no reserved turnId");
1097
+ }
1098
+ if (typeof entry.timestamp !== "string" || entry.timestamp.length === 0) {
1099
+ throw new Error("continuity append refused: the record carries no explicit timestamp");
1100
+ }
1101
+ session.appendEntry(entry);
1102
+ },
1103
+ readExact: (expected) => readContinuityRecordExact(origin, expected),
1104
+ isStateRemoved: () => stateRootRemoved(),
1105
+ isOriginCurrent
1106
+ };
1107
+ if (session.flushAppends) {
1108
+ ports.flushAppends = () => {
1109
+ if (!isOriginCurrent()) {
1110
+ throw new Error(`continuity flush refused: session ${origin.sessionId} is no longer current`);
1111
+ }
1112
+ session.flushAppends?.();
1113
+ };
1114
+ }
1115
+ ports.checkpoint = async (reason) => {
1116
+ if (!isOriginCurrent()) {
1117
+ throw new Error(`continuity checkpoint refused: session ${origin.sessionId} is no longer current`);
1118
+ }
1119
+ await session.checkpoint(reason);
1120
+ };
1121
+ return ports;
1122
+ }
1123
+
1124
+ // src/domains/session/continuity/projection.ts
1125
+ init_esm_shims();
1126
+
1127
+ // src/domains/session/continuity/evidence.ts
1128
+ init_esm_shims();
1129
+ function terminalStatus(entry) {
1130
+ const payload = entry.payload;
1131
+ if (!payload || typeof payload !== "object") return "empty";
1132
+ const record = payload;
1133
+ const stop = record.stopReason;
1134
+ if (stop === "error") return "error";
1135
+ if (stop === "aborted") return "aborted";
1136
+ if (stop === "interrupted") return "interrupted";
1137
+ if (typeof record.errorMessage === "string" && record.errorMessage.length > 0) return "error";
1138
+ if (stop !== void 0 && stop !== "stop") return "empty";
1139
+ if (record.continuityDeliveryId !== void 0 && stop !== "stop") return "empty";
1140
+ const content = Array.isArray(record.content) ? record.content : [];
1141
+ for (const block of content) {
1142
+ if (!block || typeof block !== "object") continue;
1143
+ if (block.type === "toolCall") return "empty";
1144
+ }
1145
+ if (typeof record.text === "string" && record.text.trim().length > 0) return "success";
1146
+ for (const block of content) {
1147
+ if (!block || typeof block !== "object") continue;
1148
+ const typed = block;
1149
+ if (typed.type === "text" && typeof typed.text === "string" && typed.text.trim().length > 0) return "success";
1150
+ }
1151
+ return "empty";
1152
+ }
1153
+ function terminalResponses(entries) {
1154
+ const rows = [];
1155
+ const messages = new Map(
1156
+ entries.filter((entry) => entry.kind === "message").map((entry) => [entry.turnId, entry])
1157
+ );
1158
+ for (let position = 0; position < entries.length; position += 1) {
1159
+ const entry = entries[position];
1160
+ if (entry?.kind !== "message" || entry.role !== "assistant" || entry.parentTurnId === null) continue;
1161
+ let continuationTurnId = entry.parentTurnId;
1162
+ const payload = entry.payload;
1163
+ if (typeof payload?.continuityDeliveryId === "string") {
1164
+ const delivery = entries.slice(0, position).filter(
1165
+ (candidate) => candidate.kind === "handoffTransaction" && candidate.event.phase === "delivered" && candidate.event.deliveryId === payload.continuityDeliveryId
1166
+ );
1167
+ if (delivery.length !== 1) continue;
1168
+ const intent = delivery[0];
1169
+ if (intent?.kind !== "handoffTransaction" || intent.event.phase !== "delivered") continue;
1170
+ const visited = /* @__PURE__ */ new Set();
1171
+ let ancestor = entry.parentTurnId;
1172
+ while (ancestor && ancestor !== intent.event.continuationTurnId && !visited.has(ancestor)) {
1173
+ visited.add(ancestor);
1174
+ const parent = messages.get(ancestor);
1175
+ if (parent?.role === "user") break;
1176
+ ancestor = parent?.parentTurnId ?? null;
1177
+ }
1178
+ if (ancestor !== intent.event.continuationTurnId) continue;
1179
+ continuationTurnId = ancestor;
1180
+ }
1181
+ rows.push({ entryId: entry.turnId, continuationTurnId, status: terminalStatus(entry), position });
1182
+ }
1183
+ return rows;
1184
+ }
1185
+ function resumeAuthorities(entries, pathTurnIds) {
1186
+ const rows = [];
1187
+ const anchorByHandoff = /* @__PURE__ */ new Map();
1188
+ for (const entry of entries) {
1189
+ const identity = entry.kind === "handoffTransaction" ? entry.identity : entry.kind === "continuityCommit" ? entry.continuity.identity : entry.kind === "compactionSummary" ? entry.continuity?.identity : void 0;
1190
+ if (identity === void 0) continue;
1191
+ if (!anchorByHandoff.has(identity.handoffId)) anchorByHandoff.set(identity.handoffId, identity.branchAnchorTurnId);
1192
+ }
1193
+ for (let position = 0; position < entries.length; position += 1) {
1194
+ const entry = entries[position];
1195
+ if (!isHandoffRecoveryRequestEntry(entry)) continue;
1196
+ const acceptedAtMs = Date.parse(entry.timestamp);
1197
+ if (!Number.isFinite(acceptedAtMs)) continue;
1198
+ const data = entry.data;
1199
+ if (data === void 0) continue;
1200
+ if (!anchorByHandoff.has(data.handoffId)) continue;
1201
+ if (anchorByHandoff.get(data.handoffId) !== data.branchAnchorTurnId) continue;
1202
+ if (entry.parentTurnId !== data.selectedLeafTurnId) continue;
1203
+ if (data.selectedLeafTurnId !== null && !pathTurnIds.has(data.selectedLeafTurnId)) continue;
1204
+ rows.push({
1205
+ operatorRequestEntryId: entry.turnId,
1206
+ handoffId: data.handoffId,
1207
+ action: data.action,
1208
+ pausedOrFailedEntryId: data.pausedOrFailedEntryId,
1209
+ originSessionId: data.sessionId,
1210
+ position,
1211
+ acceptedAtMs
1212
+ });
1213
+ }
1214
+ return rows;
1215
+ }
1216
+ function outcomeRefs(entries) {
1217
+ const rows = [];
1218
+ const positions = /* @__PURE__ */ new Map();
1219
+ for (let position = 0; position < entries.length; position += 1) {
1220
+ const entry = entries[position];
1221
+ if (entry === void 0) continue;
1222
+ if (!positions.has(entry.turnId)) positions.set(entry.turnId, position);
1223
+ if (entry.kind === "compactionSummary" && entry.continuity !== void 0) {
1224
+ rows.push({
1225
+ entryId: entry.turnId,
1226
+ outcome: "summarized",
1227
+ handoffId: entry.continuity.identity.handoffId,
1228
+ commitId: entry.continuity.identity.commitId,
1229
+ position
1230
+ });
1231
+ }
1232
+ }
1233
+ const claims = /* @__PURE__ */ new Map();
1234
+ for (let position = 0; position < entries.length; position += 1) {
1235
+ const entry = entries[position];
1236
+ const payload = entry?.kind === "continuityCommit" ? entry.continuity : entry?.kind === "compactionSummary" ? entry.continuity : void 0;
1237
+ if (payload === void 0) continue;
1238
+ const ref = payload.commit.evictionRef;
1239
+ if (ref === void 0 || payload.commit.outcome !== "evicted") continue;
1240
+ const identity = payload.identity;
1241
+ const claimedAt = positions.get(payload.commit.entry.turnId) ?? position;
1242
+ const existing = claims.get(ref);
1243
+ if (existing === void 0) {
1244
+ claims.set(ref, { handoffId: identity.handoffId, commitId: identity.commitId, claimedAt });
1245
+ continue;
1246
+ }
1247
+ if (existing !== null && (existing.handoffId !== identity.handoffId || existing.commitId !== identity.commitId)) {
1248
+ claims.set(ref, null);
1249
+ }
1250
+ }
1251
+ for (const [ref, claim] of claims) {
1252
+ if (claim === null) continue;
1253
+ const position = positions.get(ref);
1254
+ if (position === void 0 || position >= claim.claimedAt) continue;
1255
+ if (entries[position]?.kind !== "contextEviction") continue;
1256
+ rows.push({ entryId: ref, outcome: "evicted", handoffId: claim.handoffId, commitId: claim.commitId, position });
1257
+ }
1258
+ return rows;
1259
+ }
1260
+ function resolveContinuityEvidence(input) {
1261
+ const pathTurnIds = new Set(
1262
+ input.pathTurnIds ?? input.entries.filter((entry) => entry.kind === "message").map((entry) => entry.turnId)
1263
+ );
1264
+ return {
1265
+ resumeAuthorities: resumeAuthorities(input.entries, pathTurnIds),
1266
+ terminalResponses: terminalResponses(input.entries),
1267
+ outcomeRefs: outcomeRefs(input.entries),
1268
+ unreadableRecords: input.unreadableRecords
1269
+ };
1270
+ }
1271
+
1272
+ // src/domains/session/continuity/fold.ts
1273
+ init_esm_shims();
1274
+
1275
+ // src/domains/session/continuity/note.ts
1276
+ init_esm_shims();
1277
+ import { createHash } from "node:crypto";
1278
+ function sha256Utf8(value) {
1279
+ return createHash("sha256").update(Buffer.from(value, "utf8")).digest("hex");
1280
+ }
1281
+ function validateContinuityNote(note) {
1282
+ if (typeof note !== "string") return { ok: false, reason: "not_a_string", noteBytes: null };
1283
+ if (note.trim().length === 0) return { ok: false, reason: "blank", noteBytes: null };
1284
+ if (note.includes("\0")) return { ok: false, reason: "contains_nul", noteBytes: null };
1285
+ const encoded = Buffer.from(note, "utf8");
1286
+ if (encoded.toString("utf8") !== note) {
1287
+ return { ok: false, reason: "not_utf8_round_trip", noteBytes: encoded.byteLength };
1288
+ }
1289
+ if (encoded.byteLength > HANDOFF_NOTE_MAX_BYTES) {
1290
+ return { ok: false, reason: "exceeds_max_bytes", noteBytes: encoded.byteLength };
1291
+ }
1292
+ return {
1293
+ ok: true,
1294
+ accepted: { note, noteBytes: encoded.byteLength, noteSha256: sha256Utf8(note) }
1295
+ };
1296
+ }
1297
+ function verifyAcceptedNote(accepted, expected) {
1298
+ const revalidated = validateContinuityNote(accepted.note);
1299
+ if (!revalidated.ok) return { ok: false, reason: revalidated.reason };
1300
+ if (revalidated.accepted.noteBytes !== accepted.noteBytes) return { ok: false, reason: "byte_count_mismatch" };
1301
+ if (revalidated.accepted.noteSha256 !== accepted.noteSha256) return { ok: false, reason: "hash_mismatch" };
1302
+ if (!expected) return { ok: true };
1303
+ if (expected.note !== accepted.note) return { ok: false, reason: "decoded_text_mismatch" };
1304
+ if (expected.noteBytes !== accepted.noteBytes) return { ok: false, reason: "byte_count_mismatch" };
1305
+ if (expected.noteSha256 !== accepted.noteSha256) return { ok: false, reason: "hash_mismatch" };
1306
+ return { ok: true };
1307
+ }
1308
+
1309
+ // src/domains/session/continuity/fold.ts
1310
+ var LEGAL_PREDECESSORS = Object.freeze({
1311
+ prepared: /* @__PURE__ */ new Set(["absent"]),
1312
+ reducing: /* @__PURE__ */ new Set(["prepared", "failed", "resumed"]),
1313
+ ready: /* @__PURE__ */ new Set(["reducing"]),
1314
+ delivered: /* @__PURE__ */ new Set(["ready", "resumed"]),
1315
+ acknowledged: /* @__PURE__ */ new Set(["delivered"]),
1316
+ paused: /* @__PURE__ */ new Set(["prepared", "reducing", "ready", "delivered", "resumed"]),
1317
+ failed: /* @__PURE__ */ new Set(["prepared", "reducing", "resumed"]),
1318
+ resumed: /* @__PURE__ */ new Set(["paused", "failed"])
1319
+ });
1320
+ var LIFECYCLE_RANK = Object.freeze({
1321
+ absent: 0,
1322
+ prepared: 1,
1323
+ reducing: 2,
1324
+ ready: 3,
1325
+ delivered: 4,
1326
+ acknowledged: 5,
1327
+ paused: -1,
1328
+ failed: -1,
1329
+ resumed: -1
1330
+ });
1331
+ function isUsableTimestamp(value) {
1332
+ return Number.isSafeInteger(value) && value >= 0;
1333
+ }
1334
+ function newChain(handoffId, position) {
1335
+ return {
1336
+ handoffId,
1337
+ rootPosition: position,
1338
+ identity: null,
1339
+ accepted: null,
1340
+ policy: null,
1341
+ commit: null,
1342
+ head: null,
1343
+ activeResume: null,
1344
+ resumeEvidence: null,
1345
+ delivery: null,
1346
+ deliveryPosition: null,
1347
+ carry: null,
1348
+ commitPresent: false,
1349
+ rootSeen: false,
1350
+ seen: /* @__PURE__ */ new Map(),
1351
+ anomalies: [],
1352
+ poisoned: false
1353
+ };
1354
+ }
1355
+ function fail(chain, kind, entryId, detail) {
1356
+ chain.anomalies.push({ kind, entryId, detail });
1357
+ chain.poisoned = true;
1358
+ }
1359
+ function headPhase(chain) {
1360
+ return chain.head ? chain.head.event.phase : "absent";
1361
+ }
1362
+ function duplicateVerdict(chain, entryId, payload) {
1363
+ const previous = chain.seen.get(entryId);
1364
+ if (previous === void 0) {
1365
+ chain.seen.set(entryId, payload);
1366
+ return "new";
1367
+ }
1368
+ return previous === payload ? "identical" : "conflict";
1369
+ }
1370
+ function adoptImmutable(chain, entryId, identity, accepted, policy) {
1371
+ if (chain.identity === null) {
1372
+ chain.identity = identity;
1373
+ } else if (canonicalJson(chain.identity) !== canonicalJson(identity)) {
1374
+ fail(chain, "duplicate_conflict", entryId, "identity differs across copies of one handoff");
1375
+ return false;
1376
+ }
1377
+ const verified = verifyAcceptedNote(accepted, chain.accepted ?? void 0);
1378
+ if (!verified.ok) {
1379
+ fail(chain, chain.accepted ? "conflicting_carry" : "unverified_note", entryId, `note rejected: ${verified.reason}`);
1380
+ return false;
1381
+ }
1382
+ chain.accepted ??= accepted;
1383
+ if (chain.policy === null) {
1384
+ chain.policy = policy;
1385
+ } else if (canonicalJson(chain.policy) !== canonicalJson(policy)) {
1386
+ fail(chain, "policy_conflict", entryId, "policy differs across copies of one handoff");
1387
+ return false;
1388
+ }
1389
+ return true;
1390
+ }
1391
+ function adoptCommit(chain, entryId, commit) {
1392
+ if (chain.commit === null) {
1393
+ chain.commit = commit;
1394
+ return true;
1395
+ }
1396
+ if (canonicalJson(chain.commit) !== canonicalJson(commit)) {
1397
+ fail(chain, "conflicting_carry", entryId, "commit data differs across copies of one handoff");
1398
+ return false;
1399
+ }
1400
+ return true;
1401
+ }
1402
+ function validSuccessor(chain, entryId, phase, transition) {
1403
+ const from = headPhase(chain);
1404
+ if (!LEGAL_PREDECESSORS[phase].has(from)) {
1405
+ fail(chain, "illegal_transition", entryId, `${from} -> ${phase} is not a legal transition`);
1406
+ return false;
1407
+ }
1408
+ if (phase === "prepared") return true;
1409
+ const head = chain.head;
1410
+ if (!head) {
1411
+ fail(chain, "broken_chain", entryId, `${phase} has no predecessor in this projection`);
1412
+ return false;
1413
+ }
1414
+ if (transition.prevEntryId !== head.transition.entryId) {
1415
+ fail(chain, "broken_chain", entryId, `predecessor ${transition.prevEntryId} is not the head`);
1416
+ return false;
1417
+ }
1418
+ if (transition.sequence !== head.transition.sequence + 1) {
1419
+ fail(chain, "broken_chain", entryId, `sequence ${transition.sequence} does not follow the head`);
1420
+ return false;
1421
+ }
1422
+ const expectedAttempt = phase === "reducing" ? head.transition.attempt + 1 : head.transition.attempt;
1423
+ if (transition.attempt !== expectedAttempt) {
1424
+ fail(chain, "invalid_attempt", entryId, `attempt ${transition.attempt} should be ${expectedAttempt}`);
1425
+ return false;
1426
+ }
1427
+ if (phase === "reducing" && chain.policy && transition.attempt > chain.policy.maxAttempts) {
1428
+ fail(chain, "invalid_attempt", entryId, `attempt ${transition.attempt} exceeds the policy maximum`);
1429
+ return false;
1430
+ }
1431
+ return true;
1432
+ }
1433
+ function resumeRefValid(chain, entryId, event) {
1434
+ if (event.phase !== "reducing" && event.phase !== "delivered") return true;
1435
+ const ref = event.resumeRef;
1436
+ if (headPhase(chain) !== "resumed") {
1437
+ if (ref === void 0) return true;
1438
+ fail(chain, "missing_resume_ref", entryId, "resumeRef names a resume this event does not follow");
1439
+ return false;
1440
+ }
1441
+ const resume = chain.activeResume;
1442
+ if (!resume || ref !== resume.entryId) {
1443
+ fail(chain, "missing_resume_ref", entryId, "event after a resume must name that resume");
1444
+ return false;
1445
+ }
1446
+ const wanted = event.phase === "reducing" ? "reduce" : "deliver";
1447
+ if (resume.authority.action !== wanted) {
1448
+ fail(chain, "illegal_transition", entryId, `resume granted ${resume.authority.action}, not ${wanted}`);
1449
+ return false;
1450
+ }
1451
+ return true;
1452
+ }
1453
+ function authorizeResume(chain, entry, authority, position, context) {
1454
+ if (entry.transition.prevEntryId !== authority.pausedOrFailedEntryId) {
1455
+ fail(chain, "unmatched_resume", entry.turnId, "resume authority does not name its own predecessor");
1456
+ return null;
1457
+ }
1458
+ if (authority.action === "deliver" && chain.commit === null) {
1459
+ fail(chain, "illegal_transition", entry.turnId, "resume to deliver requires a valid commit");
1460
+ return null;
1461
+ }
1462
+ if (authority.action === "reduce" && chain.commit !== null) {
1463
+ fail(chain, "illegal_transition", entry.turnId, "resume to reduce is not legal once a commit exists");
1464
+ return null;
1465
+ }
1466
+ const answered = context.positions.get(authority.pausedOrFailedEntryId);
1467
+ if (answered === void 0) {
1468
+ fail(chain, "unmatched_resume", entry.turnId, "the paused or failed head this resume answers is not in the input");
1469
+ return null;
1470
+ }
1471
+ const matched = context.authorities.find(
1472
+ (candidate) => candidate.operatorRequestEntryId === authority.operatorRequestEntryId && candidate.handoffId === chain.handoffId && candidate.action === authority.action && candidate.pausedOrFailedEntryId === authority.pausedOrFailedEntryId && candidate.originSessionId === context.sessionId && candidate.position > answered && candidate.position < position
1473
+ );
1474
+ if (!matched) {
1475
+ fail(
1476
+ chain,
1477
+ "unvalidated_resume_authority",
1478
+ entry.turnId,
1479
+ "no durable operator control request binds this handoff, action, head and position"
1480
+ );
1481
+ return null;
1482
+ }
1483
+ if (!isUsableTimestamp(matched.acceptedAtMs)) {
1484
+ fail(chain, "unvalidated_resume_authority", entry.turnId, "the operator request has no usable accepted time");
1485
+ return null;
1486
+ }
1487
+ return matched;
1488
+ }
1489
+ function carriedResumeAuthorized(chain, entryId, resume, context, carriedPrevEntryId) {
1490
+ const refuse = (detail) => {
1491
+ fail(chain, "unvalidated_resume_authority", entryId, detail);
1492
+ return null;
1493
+ };
1494
+ if (carriedPrevEntryId !== null) {
1495
+ if (carriedPrevEntryId !== resume.authority.pausedOrFailedEntryId) {
1496
+ return refuse("the carried resume does not follow the head its authority answers");
1497
+ }
1498
+ if (resume.authority.action === "reduce" && chain.commit !== null) {
1499
+ return refuse("a carried resume to reduce is not legal once a commit exists");
1500
+ }
1501
+ if (resume.authority.action === "deliver" && chain.commit === null) {
1502
+ return refuse("a carried resume to deliver requires a valid commit");
1503
+ }
1504
+ }
1505
+ const resumeRecord = context.records.get(resume.entryId);
1506
+ if (!resumeRecord || resumeRecord.event.phase !== "resumed") {
1507
+ return refuse("the carried resume names no actual resumed record in this input");
1508
+ }
1509
+ if (resumeRecord.identity.handoffId !== chain.handoffId) {
1510
+ return refuse("the referenced resumed record belongs to another handoff");
1511
+ }
1512
+ if (canonicalJson(resumeRecord.event.authority) !== canonicalJson(resume.authority) || resumeRecord.transition.prevEntryId !== resume.authority.pausedOrFailedEntryId) {
1513
+ return refuse("the carried resume disagrees with the actual resumed record it names");
1514
+ }
1515
+ const answeredRecord = context.records.get(resume.authority.pausedOrFailedEntryId);
1516
+ if (!answeredRecord || answeredRecord.event.phase !== "paused" && answeredRecord.event.phase !== "failed") {
1517
+ return refuse("the head this carried resume answers is not an actual paused or failed record");
1518
+ }
1519
+ if (answeredRecord.identity.handoffId !== chain.handoffId) {
1520
+ return refuse("the answered paused or failed record belongs to another handoff");
1521
+ }
1522
+ const answered = context.positions.get(resume.authority.pausedOrFailedEntryId);
1523
+ const upper = context.positions.get(resume.entryId);
1524
+ if (answered === void 0 || upper === void 0 || upper <= answered) {
1525
+ return refuse("the carried resume does not sit after the head it answers");
1526
+ }
1527
+ const matched = context.authorities.find(
1528
+ (candidate) => candidate.operatorRequestEntryId === resume.authority.operatorRequestEntryId && candidate.handoffId === chain.handoffId && candidate.action === resume.authority.action && candidate.pausedOrFailedEntryId === resume.authority.pausedOrFailedEntryId && candidate.originSessionId === context.sessionId && isUsableTimestamp(candidate.acceptedAtMs) && candidate.position > answered && candidate.position < upper
1529
+ );
1530
+ if (!matched) {
1531
+ return refuse("no durable operator control request binds this carried resume's handoff, action, head and order");
1532
+ }
1533
+ return matched;
1534
+ }
1535
+ function applyTransactionEvent(chain, entry, position, context) {
1536
+ const verdict = duplicateVerdict(chain, entry.turnId, canonicalJson(entry));
1537
+ if (verdict === "identical") return;
1538
+ if (verdict === "conflict") {
1539
+ fail(chain, "duplicate_conflict", entry.turnId, `two different payloads share entry id ${entry.turnId}`);
1540
+ return;
1541
+ }
1542
+ if (entry.event.phase === "prepared") {
1543
+ if (!validSuccessor(chain, entry.turnId, "prepared", entry.transition)) return;
1544
+ if (!adoptImmutable(chain, entry.turnId, entry.identity, entry.event.accepted, entry.event.policy)) return;
1545
+ chain.rootSeen = true;
1546
+ chain.rootPosition = position;
1547
+ } else {
1548
+ if (chain.identity === null) {
1549
+ chain.identity = entry.identity;
1550
+ } else if (canonicalJson(chain.identity) !== canonicalJson(entry.identity)) {
1551
+ fail(chain, "duplicate_conflict", entry.turnId, "identity differs across records of one handoff");
1552
+ return;
1553
+ }
1554
+ if (!validSuccessor(chain, entry.turnId, entry.event.phase, entry.transition)) return;
1555
+ }
1556
+ if (entry.event.phase === "reducing" && headPhase(chain) === "failed") {
1557
+ const failure = chain.head?.event;
1558
+ if (failure.reason !== "provider_failed") {
1559
+ fail(chain, "illegal_transition", entry.turnId, `failed(${failure.reason}) may not resume reduction`);
1560
+ return;
1561
+ }
1562
+ }
1563
+ if (!resumeRefValid(chain, entry.turnId, entry.event)) return;
1564
+ if (entry.event.phase === "resumed") {
1565
+ const evidence = authorizeResume(chain, entry, entry.event.authority, position, context);
1566
+ if (!evidence) return;
1567
+ chain.activeResume = { entryId: entry.turnId, authority: entry.event.authority };
1568
+ chain.resumeEvidence = evidence;
1569
+ }
1570
+ chain.head = { transition: entry.transition, event: entry.event };
1571
+ if (entry.event.phase === "delivered") {
1572
+ chain.delivery = { deliveryId: entry.event.deliveryId, continuationTurnId: entry.event.continuationTurnId };
1573
+ chain.deliveryPosition = position;
1574
+ }
1575
+ }
1576
+ function applyCommit(chain, entry) {
1577
+ const payload = entry.continuity;
1578
+ const verdict = duplicateVerdict(chain, entry.turnId, canonicalJson(entry));
1579
+ if (verdict === "identical") {
1580
+ chain.commitPresent = true;
1581
+ return;
1582
+ }
1583
+ if (verdict === "conflict") {
1584
+ fail(chain, "duplicate_conflict", entry.turnId, `two different commit payloads share entry id ${entry.turnId}`);
1585
+ return;
1586
+ }
1587
+ if (!adoptImmutable(chain, entry.turnId, payload.identity, payload.accepted, payload.policy)) return;
1588
+ const known = chain.commit;
1589
+ const restoresKnownCommit = known !== null && canonicalJson(known) === canonicalJson(payload.commit);
1590
+ if (!adoptCommit(chain, entry.turnId, payload.commit)) return;
1591
+ if (restoresKnownCommit) {
1592
+ chain.commitPresent = true;
1593
+ return;
1594
+ }
1595
+ if (!validSuccessor(chain, entry.turnId, "ready", payload.commit.transition)) return;
1596
+ chain.commitPresent = true;
1597
+ chain.head = { transition: payload.commit.transition, event: { phase: "ready" } };
1598
+ }
1599
+ function carryMayAdvance(chain, to) {
1600
+ const from = headPhase(chain);
1601
+ if (from === "absent") return true;
1602
+ if (from === "acknowledged") return false;
1603
+ if (from === "paused") return to === "resumed";
1604
+ if (from === "failed") {
1605
+ if (to === "resumed") return true;
1606
+ if (to !== "reducing") return false;
1607
+ const failure = chain.head?.event;
1608
+ return failure.reason === "provider_failed";
1609
+ }
1610
+ if (to === "paused" || to === "failed" || to === "resumed") return true;
1611
+ return LIFECYCLE_RANK[to] > LIFECYCLE_RANK[from];
1612
+ }
1613
+ function applyCarry(chain, payload, entryId, context) {
1614
+ const verdict = duplicateVerdict(chain, entryId, canonicalJson(payload));
1615
+ if (verdict === "identical") return;
1616
+ if (verdict === "conflict") {
1617
+ fail(chain, "conflicting_carry", entryId, `two different carried payloads share entry id ${entryId}`);
1618
+ return;
1619
+ }
1620
+ if (!adoptImmutable(chain, entryId, payload.identity, payload.accepted, payload.policy)) return;
1621
+ if (!adoptCommit(chain, entryId, payload.commit)) return;
1622
+ if (chain.carry && payload.state.transition.sequence === chain.carry.state.transition.sequence) {
1623
+ if (canonicalJson(chain.carry.state) !== canonicalJson(payload.state)) {
1624
+ fail(chain, "conflicting_carry", entryId, "two carries disagree about the state at the same sequence");
1625
+ return;
1626
+ }
1627
+ }
1628
+ if (chain.carry === null || payload.state.transition.sequence > chain.carry.state.transition.sequence) {
1629
+ chain.carry = payload;
1630
+ }
1631
+ const head = chain.head;
1632
+ const carriedPhase = payload.state.event.phase;
1633
+ let adjacent = false;
1634
+ if (head) {
1635
+ if (payload.state.transition.sequence === head.transition.sequence) {
1636
+ const observed = {
1637
+ transition: head.transition,
1638
+ event: head.event,
1639
+ delivery: chain.delivery,
1640
+ activeResume: chain.activeResume
1641
+ };
1642
+ const carried = {
1643
+ transition: payload.state.transition,
1644
+ event: payload.state.event,
1645
+ delivery: payload.state.delivery,
1646
+ activeResume: payload.state.activeResume
1647
+ };
1648
+ if (canonicalJson(observed) !== canonicalJson(carried)) {
1649
+ fail(chain, "conflicting_carry", entryId, "the carry contradicts the observed head at the same sequence");
1650
+ }
1651
+ return;
1652
+ }
1653
+ if (payload.state.transition.sequence < head.transition.sequence) return;
1654
+ adjacent = payload.state.transition.sequence === head.transition.sequence + 1;
1655
+ if (adjacent) {
1656
+ if (!validSuccessor(chain, entryId, carriedPhase, payload.state.transition)) return;
1657
+ } else {
1658
+ if (!carryMayAdvance(chain, carriedPhase)) {
1659
+ fail(chain, "fabricated_carry_extension", entryId, `a carry cannot move ${headPhase(chain)} to ${carriedPhase}`);
1660
+ return;
1661
+ }
1662
+ if (payload.state.transition.attempt < head.transition.attempt) {
1663
+ fail(chain, "invalid_attempt", entryId, "a carry cannot advance the head while refunding a spent attempt");
1664
+ return;
1665
+ }
1666
+ }
1667
+ }
1668
+ const carriedResume = payload.state.activeResume;
1669
+ if (carriedResume) {
1670
+ const carriedPrev = carriedPhase === "resumed" ? payload.state.transition.prevEntryId : null;
1671
+ const evidence = carriedResumeAuthorized(chain, entryId, carriedResume, context, carriedPrev);
1672
+ if (!evidence) return;
1673
+ chain.activeResume = carriedResume;
1674
+ chain.resumeEvidence = evidence;
1675
+ } else if (carriedPhase === "resumed") {
1676
+ fail(chain, "unmatched_resume", entryId, "a carried resumed head retains no resume to validate");
1677
+ return;
1678
+ }
1679
+ if (adjacent && !resumeRefValid(chain, entryId, payload.state.event)) return;
1680
+ chain.head = { transition: payload.state.transition, event: payload.state.event };
1681
+ if (payload.state.delivery) chain.delivery = payload.state.delivery;
1682
+ }
1683
+ function outcomeRefMatches(commit, identity, refs) {
1684
+ if (commit.outcome === "continuity_only") return true;
1685
+ const expectedId = commit.outcome === "summarized" ? commit.summaryRef : commit.evictionRef;
1686
+ if (expectedId === void 0) return false;
1687
+ return refs.some(
1688
+ (ref) => ref.entryId === expectedId && ref.outcome === commit.outcome && ref.handoffId === identity.handoffId && ref.commitId === identity.commitId
1689
+ );
1690
+ }
1691
+ function acknowledgementSupported(event, delivery, responses, bounds) {
1692
+ if (!delivery || delivery.deliveryId !== event.deliveryId) return false;
1693
+ const { deliveredAfter, acknowledgedBefore } = bounds;
1694
+ if (deliveredAfter === null || acknowledgedBefore === null) return false;
1695
+ return responses.some(
1696
+ (response) => response.entryId === event.terminalResponseEntryId && response.continuationTurnId === delivery.continuationTurnId && response.status === "success" && Number.isFinite(response.position) && response.position > deliveredAfter && response.position < acknowledgedBefore
1697
+ );
1698
+ }
1699
+ function effectiveDeadline(policy, resume, evidence) {
1700
+ if (resume && evidence) {
1701
+ return Math.min(resume.automaticDeadlineAtMs, evidence.acceptedAtMs + HANDOFF_MAX_WINDOW_MS);
1702
+ }
1703
+ return Math.min(policy.automaticDeadlineAtMs, policy.preparedAtMs + HANDOFF_MAX_WINDOW_MS);
1704
+ }
1705
+ function priorProjection(chain) {
1706
+ return {
1707
+ handoffId: chain.handoffId,
1708
+ commitId: chain.identity?.commitId ?? "",
1709
+ phase: headPhase(chain),
1710
+ accepted: chain.accepted,
1711
+ outcome: chain.commit?.outcome ?? null
1712
+ };
1713
+ }
1714
+ function applicable(identity, input, path) {
1715
+ if (identity.originSessionId !== input.selection.sessionId) return false;
1716
+ if (!path.has(identity.initiatingTurnId)) return false;
1717
+ return identity.branchAnchorTurnId === null || path.has(identity.branchAnchorTurnId);
1718
+ }
1719
+ function looksLikeContinuity(value) {
1720
+ if (typeof value !== "object" || value === null) return false;
1721
+ const record = value;
1722
+ if (record.kind === "handoffTransaction" || record.kind === "continuityCommit") return true;
1723
+ return record.kind === "compactionSummary" && record.continuity !== void 0;
1724
+ }
1725
+ function foldContinuity(input) {
1726
+ const path = new Set(input.selection.pathTurnIds);
1727
+ const chains = /* @__PURE__ */ new Map();
1728
+ const positions = /* @__PURE__ */ new Map();
1729
+ const ownerByEntryId = /* @__PURE__ */ new Map();
1730
+ const observed = /* @__PURE__ */ new Set();
1731
+ const malformed = [];
1732
+ let inapplicable = 0;
1733
+ const records = /* @__PURE__ */ new Map();
1734
+ const context = {
1735
+ positions,
1736
+ records,
1737
+ authorities: input.evidence.resumeAuthorities,
1738
+ sessionId: input.selection.sessionId
1739
+ };
1740
+ const chainFor = (handoffId, position) => {
1741
+ const existing = chains.get(handoffId);
1742
+ if (existing) return existing;
1743
+ const created = newChain(handoffId, position);
1744
+ chains.set(handoffId, created);
1745
+ return created;
1746
+ };
1747
+ for (let position = 0; position < input.entries.length; position += 1) {
1748
+ const raw = input.entries[position];
1749
+ if (isHandoffTransactionEntry(raw) || isContinuityCommitEntry(raw)) {
1750
+ const entryId = raw.turnId;
1751
+ if (!observed.has(entryId)) {
1752
+ observed.add(entryId);
1753
+ positions.set(entryId, position);
1754
+ if (isHandoffTransactionEntry(raw)) records.set(entryId, raw);
1755
+ }
1756
+ }
1757
+ }
1758
+ for (let position = 0; position < input.entries.length; position += 1) {
1759
+ const raw = input.entries[position];
1760
+ const transaction = isHandoffTransactionEntry(raw) ? raw : null;
1761
+ const commit = !transaction && isContinuityCommitEntry(raw) ? raw : null;
1762
+ const carrying = !transaction && !commit && isContinuityCarryingSummary(raw) ? raw : null;
1763
+ if (!transaction && !commit && !carrying) {
1764
+ if (looksLikeContinuity(raw)) {
1765
+ malformed.push({
1766
+ kind: "malformed_continuity_record",
1767
+ entryId: typeof raw.turnId === "string" ? raw.turnId : null,
1768
+ detail: "a record claiming a continuity kind failed structural validation"
1769
+ });
1770
+ }
1771
+ continue;
1772
+ }
1773
+ const identity = transaction?.identity ?? commit?.continuity.identity ?? carrying?.continuity.identity;
1774
+ if (!identity || !applicable(identity, input, path)) {
1775
+ inapplicable += 1;
1776
+ continue;
1777
+ }
1778
+ const entryId = (transaction ?? commit ?? carrying)?.turnId;
1779
+ const owner = ownerByEntryId.get(entryId);
1780
+ if (owner !== void 0 && owner !== identity.handoffId) {
1781
+ const chain2 = chainFor(identity.handoffId, position);
1782
+ fail(chain2, "entry_id_reused", entryId, `entry id is also used by handoff ${owner}`);
1783
+ const other = chains.get(owner);
1784
+ if (other) fail(other, "entry_id_reused", entryId, `entry id is also used by handoff ${identity.handoffId}`);
1785
+ continue;
1786
+ }
1787
+ ownerByEntryId.set(entryId, identity.handoffId);
1788
+ const chain = chainFor(identity.handoffId, position);
1789
+ if (transaction) applyTransactionEvent(chain, transaction, position, context);
1790
+ else if (commit) applyCommit(chain, commit);
1791
+ else if (carrying) applyCarry(chain, carrying.continuity, carrying.turnId, context);
1792
+ }
1793
+ const base = {
1794
+ skipped: { inapplicable, unreadable: input.evidence.unreadableRecords }
1795
+ };
1796
+ if (chains.size === 0) {
1797
+ return {
1798
+ phase: "absent",
1799
+ authority: "none",
1800
+ identity: null,
1801
+ accepted: null,
1802
+ policy: null,
1803
+ commit: null,
1804
+ state: null,
1805
+ attemptsSpent: 0,
1806
+ effectiveDeadlineAtMs: null,
1807
+ missingCommit: null,
1808
+ validated: malformed.length === 0,
1809
+ anomalies: malformed,
1810
+ action: { kind: "none" },
1811
+ priorHandoffs: [],
1812
+ ...base
1813
+ };
1814
+ }
1815
+ const ordered = [...chains.values()].sort((a, b) => a.rootPosition - b.rootPosition);
1816
+ const selected = ordered[ordered.length - 1];
1817
+ const priorHandoffs = ordered.slice(0, -1).map(priorProjection);
1818
+ return finalize(selected, input, positions, records, malformed, priorHandoffs, inapplicable);
1819
+ }
1820
+ function finalize(chain, input, positions, records, malformed, priorHandoffs, inapplicable) {
1821
+ const anomalies = [...malformed, ...chain.anomalies];
1822
+ const unreadable = input.evidence.unreadableRecords;
1823
+ const identity = chain.identity;
1824
+ const policy = chain.policy;
1825
+ let poisoned = chain.poisoned || malformed.length > 0;
1826
+ if (unreadable > 0 && identity !== null) {
1827
+ anomalies.push({
1828
+ kind: "malformed_record",
1829
+ entryId: null,
1830
+ detail: `${unreadable} unreadable records leave this transaction's evidence unresolved`
1831
+ });
1832
+ poisoned = true;
1833
+ }
1834
+ if (chain.commit && identity && !outcomeRefMatches(chain.commit, identity, input.evidence.outcomeRefs)) {
1835
+ anomalies.push({
1836
+ kind: unreadable > 0 ? "missing_commit_ref" : "unbound_outcome_ref",
1837
+ entryId: chain.commit.entry.turnId,
1838
+ detail: `no ${chain.commit.outcome} record is bound to commit ${identity.commitId}`
1839
+ });
1840
+ poisoned = true;
1841
+ }
1842
+ const phase = headPhase(chain);
1843
+ if (phase === "acknowledged") {
1844
+ const event = chain.head?.event;
1845
+ const ackEntryId = chain.head?.transition.entryId ?? "";
1846
+ const deliveryEntryId = chain.head?.transition.prevEntryId ?? "";
1847
+ const ackRecord = records.get(ackEntryId);
1848
+ const deliveryRecord = records.get(deliveryEntryId);
1849
+ const ackMatches = ackRecord?.event.phase === "acknowledged" && ackRecord.identity.handoffId === chain.handoffId && ackRecord.event.deliveryId === event.deliveryId && ackRecord.event.terminalResponseEntryId === event.terminalResponseEntryId;
1850
+ const deliveryMatches = deliveryRecord?.event.phase === "delivered" && deliveryRecord.identity.handoffId === chain.handoffId && deliveryRecord.event.deliveryId === (chain.delivery?.deliveryId ?? null) && deliveryRecord.event.continuationTurnId === (chain.delivery?.continuationTurnId ?? null);
1851
+ const ackBounds = {
1852
+ deliveredAfter: deliveryMatches ? chain.deliveryPosition ?? positions.get(deliveryEntryId) ?? null : null,
1853
+ acknowledgedBefore: ackMatches ? positions.get(ackEntryId) ?? null : null
1854
+ };
1855
+ if (!acknowledgementSupported(event, chain.delivery, input.evidence.terminalResponses, ackBounds)) {
1856
+ anomalies.push({
1857
+ kind: "acknowledgement_unsupported",
1858
+ entryId: chain.head?.transition.entryId ?? null,
1859
+ detail: "no terminal successful response matches this delivery"
1860
+ });
1861
+ poisoned = true;
1862
+ }
1863
+ }
1864
+ const state = chain.head ? {
1865
+ transition: chain.head.transition,
1866
+ event: chain.head.event,
1867
+ activeResume: chain.activeResume,
1868
+ delivery: chain.delivery
1869
+ } : null;
1870
+ let missingCommit = null;
1871
+ if (!chain.commitPresent && chain.carry && identity && state && !poisoned) {
1872
+ {
1873
+ const original = chain.carry.commit;
1874
+ const rebuilt = {
1875
+ kind: "continuityCommit",
1876
+ turnId: original.entry.turnId,
1877
+ parentTurnId: original.entry.parentTurnId,
1878
+ timestamp: original.entry.timestamp,
1879
+ continuity: {
1880
+ schemaVersion: HANDOFF_SCHEMA_VERSION,
1881
+ identity,
1882
+ accepted: chain.carry.accepted,
1883
+ policy: chain.carry.policy,
1884
+ commit: original,
1885
+ state: { transition: original.transition, event: { phase: "ready" }, activeResume: null, delivery: null }
1886
+ }
1887
+ };
1888
+ if (isContinuityCheckpointPayload(rebuilt.continuity)) {
1889
+ missingCommit = { entry: rebuilt, carriedState: state };
1890
+ } else {
1891
+ anomalies.push({
1892
+ kind: "conflicting_carry",
1893
+ entryId: original.entry.turnId,
1894
+ detail: "the reconstructed commit does not validate"
1895
+ });
1896
+ poisoned = true;
1897
+ }
1898
+ }
1899
+ }
1900
+ const attemptsSpent = chain.head?.transition.attempt ?? 0;
1901
+ const renewalBase = chain.resumeEvidence?.acceptedAtMs ?? null;
1902
+ const clockAmbiguous = !Number.isFinite(input.nowMs) || policy !== null && input.nowMs < policy.preparedAtMs || renewalBase !== null && input.nowMs < renewalBase;
1903
+ const deadline = policy ? effectiveDeadline(policy, chain.activeResume?.authority ?? null, chain.resumeEvidence) : null;
1904
+ const expired = deadline !== null && !clockAmbiguous && input.nowMs >= deadline;
1905
+ const action = resolveAction({
1906
+ historical: input.selection.historical,
1907
+ poisoned,
1908
+ phase,
1909
+ clockAmbiguous,
1910
+ expired,
1911
+ missingCommit,
1912
+ attemptsSpent,
1913
+ policy,
1914
+ delivery: chain.delivery,
1915
+ deliveryPosition: chain.deliveryPosition,
1916
+ responses: input.evidence.terminalResponses,
1917
+ committed: chain.commit !== null,
1918
+ resumeAction: chain.activeResume?.authority.action ?? null
1919
+ });
1920
+ const authority = identity === null ? "none" : action.kind === "none" && phase !== "absent" && phase !== "acknowledged" ? "execution" : "recall_only";
1921
+ return {
1922
+ phase,
1923
+ authority,
1924
+ identity,
1925
+ accepted: chain.accepted,
1926
+ policy,
1927
+ commit: chain.commit,
1928
+ state,
1929
+ attemptsSpent,
1930
+ effectiveDeadlineAtMs: deadline,
1931
+ missingCommit,
1932
+ validated: !poisoned,
1933
+ anomalies,
1934
+ action,
1935
+ priorHandoffs,
1936
+ skipped: { inapplicable, unreadable }
1937
+ };
1938
+ }
1939
+ function resolveAction(facts) {
1940
+ if (facts.poisoned) return { kind: "recall_only", reason: "conflicting_evidence" };
1941
+ if (facts.phase === "absent") return { kind: "none" };
1942
+ if (facts.phase === "acknowledged") return { kind: "none" };
1943
+ if (facts.historical) return { kind: "recall_only", reason: "historical_fork" };
1944
+ if (facts.phase === "paused") return { kind: "recall_only", reason: "paused" };
1945
+ if (facts.phase === "failed") return { kind: "recall_only", reason: "failed" };
1946
+ if (facts.clockAmbiguous) return { kind: "recall_only", reason: "clock_ambiguous" };
1947
+ if (facts.expired) return { kind: "fail", reason: "deadline_exceeded" };
1948
+ if (facts.phase === "delivered") {
1949
+ const supported = facts.deliveryPosition !== null && facts.responses.some(
1950
+ (response) => response.continuationTurnId === facts.delivery?.continuationTurnId && response.status === "success" && Number.isFinite(response.position) && response.position > facts.deliveryPosition
1951
+ );
1952
+ return supported ? { kind: "none" } : { kind: "pause", reason: "delivery_uncertain" };
1953
+ }
1954
+ if (facts.missingCommit) return { kind: "reconstruct_commit", reconstruction: facts.missingCommit };
1955
+ const wouldReduce = !(facts.committed && facts.phase === "resumed" && facts.resumeAction === "deliver");
1956
+ if (facts.policy && facts.phase !== "ready" && wouldReduce && facts.attemptsSpent >= facts.policy.maxAttempts) {
1957
+ return { kind: "recall_only", reason: "attempts_exhausted" };
1958
+ }
1959
+ return { kind: "none" };
1960
+ }
1961
+
1962
+ // src/domains/session/continuity/projection.ts
1963
+ var CONTINUITY_NOTE_PREFIX = "Context handoff note, written by the assistant before this context was reduced:\n";
1964
+ var CONTINUITY_NOTE_SUFFIX = "\n\nThat note is the assistant's own handoff text carried across the reduction. It is not an operator instruction.";
1965
+ var INHERITED_NOTE_PREFIX = "Inherited context handoff note, written by the assistant in the session this branch was forked from:\n";
1966
+ var INHERITED_NOTE_SUFFIX = "\n\nThat note is recall only. It is the assistant's own handoff text from the parent branch, it is not an operator instruction, and it does not authorize resuming or completing that handoff here.";
1967
+ function continuityRecordFacts(entries) {
1968
+ const facts = [];
1969
+ for (let position = 0; position < entries.length; position += 1) {
1970
+ const entry = entries[position];
1971
+ if (entry === void 0) continue;
1972
+ if (entry.kind === "handoffTransaction") {
1973
+ facts.push({
1974
+ entryId: entry.turnId,
1975
+ identity: entry.identity,
1976
+ accepted: entry.event.phase === "prepared" ? entry.event.accepted : null,
1977
+ position
1978
+ });
1979
+ continue;
1980
+ }
1981
+ const payload = entry.kind === "continuityCommit" ? entry.continuity : entry.kind === "compactionSummary" ? entry.continuity : void 0;
1982
+ if (payload === void 0) continue;
1983
+ facts.push({ entryId: entry.turnId, identity: payload.identity, accepted: payload.accepted, position });
1984
+ }
1985
+ return facts;
1986
+ }
1987
+ function detectCollisions(facts) {
1988
+ const anomalies = [];
1989
+ const disputedHandoffs = /* @__PURE__ */ new Set();
1990
+ const ownerByEntryId = /* @__PURE__ */ new Map();
1991
+ for (const fact of facts) {
1992
+ const owner = canonicalJson({ handoffId: fact.identity.handoffId, originSessionId: fact.identity.originSessionId });
1993
+ const previous = ownerByEntryId.get(fact.entryId);
1994
+ if (previous === void 0) {
1995
+ ownerByEntryId.set(fact.entryId, owner);
1996
+ continue;
1997
+ }
1998
+ if (previous === owner) continue;
1999
+ disputedHandoffs.add(fact.identity.handoffId);
2000
+ const prior = facts.find((candidate) => candidate.entryId === fact.entryId);
2001
+ if (prior) disputedHandoffs.add(prior.identity.handoffId);
2002
+ anomalies.push({
2003
+ kind: "entry_id_reused",
2004
+ entryId: fact.entryId,
2005
+ detail: "one entry id is claimed by two different continuity transactions or origins"
2006
+ });
2007
+ }
2008
+ const identityByHandoff = /* @__PURE__ */ new Map();
2009
+ const noteByHandoff = /* @__PURE__ */ new Map();
2010
+ for (const fact of facts) {
2011
+ const identity = canonicalJson(fact.identity);
2012
+ const previous = identityByHandoff.get(fact.identity.handoffId);
2013
+ if (previous === void 0) identityByHandoff.set(fact.identity.handoffId, identity);
2014
+ else if (previous !== identity) {
2015
+ disputedHandoffs.add(fact.identity.handoffId);
2016
+ anomalies.push({
2017
+ kind: "duplicate_conflict",
2018
+ entryId: fact.entryId,
2019
+ detail: `handoff ${fact.identity.handoffId} carries two different immutable identities`
2020
+ });
2021
+ }
2022
+ if (fact.accepted === null) continue;
2023
+ const note = canonicalJson(fact.accepted);
2024
+ const priorNote = noteByHandoff.get(fact.identity.handoffId);
2025
+ if (priorNote === void 0) noteByHandoff.set(fact.identity.handoffId, note);
2026
+ else if (priorNote !== note) {
2027
+ disputedHandoffs.add(fact.identity.handoffId);
2028
+ anomalies.push({
2029
+ kind: "duplicate_conflict",
2030
+ entryId: fact.entryId,
2031
+ detail: `handoff ${fact.identity.handoffId} carries two different accepted notes`
2032
+ });
2033
+ }
2034
+ }
2035
+ return { anomalies, disputedHandoffs };
2036
+ }
2037
+ function noteAnchor(facts, note) {
2038
+ if (note === null) return null;
2039
+ for (const fact of facts) {
2040
+ if (fact.identity.handoffId === note.handoffId) return fact.entryId;
2041
+ }
2042
+ return null;
2043
+ }
2044
+ function messageTurnIds(entries) {
2045
+ return entries.filter((entry) => entry.kind === "message").map((entry) => entry.turnId);
2046
+ }
2047
+ function inheritedPrefix(entries, fork) {
2048
+ if (fork.headerParentTurnId !== void 0 && fork.headerParentTurnId !== fork.parentTurnId) {
2049
+ return { problem: "the session header and metadata disagree about the fork message" };
2050
+ }
2051
+ const boundary = entries.findIndex((entry) => entry.turnId === fork.parentTurnId);
2052
+ if (boundary < 0) return { problem: `the fork message ${fork.parentTurnId} is not present in this ledger` };
2053
+ return { prefix: entries.slice(0, boundary + 1) };
2054
+ }
2055
+ function recallFromFold(fold, originSessionId, disputed) {
2056
+ const notes = [];
2057
+ if (fold.identity !== null && fold.accepted !== null && !disputed.has(fold.identity.handoffId)) {
2058
+ notes.push({
2059
+ originSessionId,
2060
+ handoffId: fold.identity.handoffId,
2061
+ commitId: fold.identity.commitId,
2062
+ note: fold.accepted.note,
2063
+ phase: fold.phase,
2064
+ provenance: "inherited",
2065
+ validated: fold.validated
2066
+ });
2067
+ }
2068
+ for (const prior of fold.priorHandoffs) {
2069
+ if (prior.accepted === null || disputed.has(prior.handoffId)) continue;
2070
+ notes.push({
2071
+ originSessionId,
2072
+ handoffId: prior.handoffId,
2073
+ commitId: prior.commitId,
2074
+ note: prior.accepted.note,
2075
+ phase: prior.phase,
2076
+ // A prior handoff is a recall projection of an earlier completed cycle,
2077
+ // never independently finalized authority.
2078
+ provenance: "inherited_prior",
2079
+ validated: fold.validated
2080
+ });
2081
+ }
2082
+ return notes;
2083
+ }
2084
+ function resolveContinuityProjection(input) {
2085
+ const facts = continuityRecordFacts(input.entries);
2086
+ if (facts.length === 0) {
2087
+ return {
2088
+ current: null,
2089
+ note: null,
2090
+ inherited: [],
2091
+ anomalies: [],
2092
+ authoritySuppressed: false,
2093
+ noteAnchorTurnId: null
2094
+ };
2095
+ }
2096
+ const collisions = detectCollisions(facts);
2097
+ const anomalies = [...collisions.anomalies];
2098
+ const pathTurnIds = input.pathTurnIds ?? messageTurnIds(input.entries);
2099
+ const nowMs = input.nowMs ?? Date.now();
2100
+ const current = input.sessionId === void 0 ? null : foldContinuity({
2101
+ entries: input.entries,
2102
+ selection: { sessionId: input.sessionId, pathTurnIds, historical: input.historical ?? false },
2103
+ // Evidence for the full ledger, resolved over the full ledger. Every
2104
+ // position in it indexes this same array.
2105
+ evidence: resolveContinuityEvidence({
2106
+ entries: input.entries,
2107
+ unreadableRecords: input.unreadableRecords ?? 0
2108
+ }),
2109
+ nowMs
2110
+ });
2111
+ const inherited = [];
2112
+ const foreignOrigins = /* @__PURE__ */ new Set();
2113
+ for (const fact of facts) {
2114
+ if (fact.identity.originSessionId !== input.sessionId) foreignOrigins.add(fact.identity.originSessionId);
2115
+ }
2116
+ let boundaryUnresolved = false;
2117
+ if (foreignOrigins.size > 0) {
2118
+ if (input.fork === void 0) {
2119
+ boundaryUnresolved = true;
2120
+ anomalies.push({
2121
+ kind: "malformed_continuity_record",
2122
+ entryId: null,
2123
+ detail: `this ledger holds continuity records from ${foreignOrigins.size} other origin session(s) but publishes no fork binding; they are not projected`
2124
+ });
2125
+ } else {
2126
+ const bounded = inheritedPrefix(input.entries, input.fork);
2127
+ if ("problem" in bounded) {
2128
+ boundaryUnresolved = true;
2129
+ anomalies.push({
2130
+ kind: "malformed_continuity_record",
2131
+ entryId: null,
2132
+ detail: `inherited boundary unresolved: ${bounded.problem}`
2133
+ });
2134
+ } else {
2135
+ const prefix = bounded.prefix;
2136
+ const prefixEvidence = resolveContinuityEvidence({
2137
+ entries: prefix,
2138
+ unreadableRecords: input.unreadableRecords ?? 0
2139
+ });
2140
+ const prefixPath = input.pathTurnIds ?? messageTurnIds(prefix);
2141
+ const eligible = [...new Set(continuityRecordFacts(prefix).map((fact) => fact.identity.originSessionId))].filter(
2142
+ (origin) => origin !== input.sessionId
2143
+ );
2144
+ for (const originSessionId of eligible) {
2145
+ const fold = foldContinuity({
2146
+ entries: prefix,
2147
+ // The origin is the record's own, unchanged. `historical: true` is
2148
+ // the honest description of the projection: another branch's
2149
+ // transaction, which the fold answers recall-only in every phase.
2150
+ selection: { sessionId: originSessionId, pathTurnIds: prefixPath, historical: true },
2151
+ evidence: prefixEvidence,
2152
+ nowMs
2153
+ });
2154
+ inherited.push(...recallFromFold(fold, originSessionId, collisions.disputedHandoffs));
2155
+ }
2156
+ }
2157
+ }
2158
+ }
2159
+ const seen = /* @__PURE__ */ new Set();
2160
+ const deduped = inherited.filter((entry) => {
2161
+ const key = canonicalJson({ h: entry.handoffId, c: entry.commitId, o: entry.originSessionId, n: entry.note });
2162
+ if (seen.has(key)) return false;
2163
+ seen.add(key);
2164
+ return true;
2165
+ });
2166
+ const authoritySuppressed = boundaryUnresolved || current !== null && current.identity !== null && collisions.disputedHandoffs.has(current.identity.handoffId);
2167
+ const published = current === null ? null : authoritySuppressed ? {
2168
+ ...current,
2169
+ authority: current.authority === "none" ? "none" : "recall_only",
2170
+ validated: false,
2171
+ action: { kind: "recall_only", reason: "conflicting_evidence" },
2172
+ missingCommit: null,
2173
+ anomalies: [...current.anomalies, ...anomalies]
2174
+ } : current;
2175
+ const note = published === null || published.identity === null || published.accepted === null ? null : {
2176
+ note: published.accepted.note,
2177
+ handoffId: published.identity.handoffId,
2178
+ commitId: published.identity.commitId,
2179
+ originSessionId: published.identity.originSessionId,
2180
+ phase: published.phase,
2181
+ authority: published.authority
2182
+ };
2183
+ return {
2184
+ current: published,
2185
+ note,
2186
+ inherited: deduped,
2187
+ anomalies,
2188
+ authoritySuppressed,
2189
+ noteAnchorTurnId: noteAnchor(facts, note)
2190
+ };
2191
+ }
2192
+ function continuityReplayText(note) {
2193
+ return `${CONTINUITY_NOTE_PREFIX}${note.note}${CONTINUITY_NOTE_SUFFIX}`;
2194
+ }
2195
+ function inheritedContinuityReplayText(note) {
2196
+ return `${INHERITED_NOTE_PREFIX}${note.note}${INHERITED_NOTE_SUFFIX}`;
2197
+ }
2198
+ function continuityReplayBlocks(projection) {
2199
+ const blocks = projection.inherited.map(inheritedContinuityReplayText);
2200
+ if (projection.note !== null && projection.note.authority !== "none") {
2201
+ blocks.push(continuityReplayText(projection.note));
2202
+ }
2203
+ return blocks;
2204
+ }
2205
+ function continuityProjectionTokens(projection) {
2206
+ let total = 0;
2207
+ for (const block of continuityReplayBlocks(projection)) {
2208
+ total += estimateAgentMessageTokens({ role: "user", payload: { text: block } });
2209
+ }
2210
+ return total;
2211
+ }
2212
+
2213
+ // src/domains/session/protected-artifacts.ts
2214
+ init_esm_shims();
2215
+ function protectedArtifactEntryFromArtifact(artifact, context) {
2216
+ const entry = {
2217
+ kind: "protectedArtifact",
2218
+ parentTurnId: context.parentTurnId,
2219
+ action: "protect",
2220
+ artifact: artifactSnapshot(artifact)
2221
+ };
2222
+ if (context.toolName !== void 0) entry.toolName = context.toolName;
2223
+ if (context.toolCallId !== void 0) entry.toolCallId = context.toolCallId;
2224
+ if (context.runId !== void 0) entry.runId = context.runId;
2225
+ if (context.correlationId !== void 0) entry.correlationId = context.correlationId;
2226
+ return entry;
2227
+ }
2228
+ function protectedArtifactFromSessionEntry(entry) {
2229
+ return protectedArtifactFromSnapshot(entry.artifact);
2230
+ }
2231
+ function protectedArtifactStateFromSessionEntries(entries) {
2232
+ let state = { artifacts: [] };
2233
+ const protectedEntries = entries.map((entry, index) => ({ entry, index })).filter((item) => item.entry.kind === "protectedArtifact").sort(compareProtectedEntryPositions);
2234
+ for (const item of protectedEntries) {
2235
+ if (item.entry.action !== "protect") continue;
2236
+ state = protectArtifact(state, protectedArtifactFromSessionEntry(item.entry));
2237
+ }
2238
+ return state;
2239
+ }
2240
+ function artifactSnapshot(artifact) {
2241
+ const snapshot = {
2242
+ path: artifact.path,
2243
+ protectedAt: artifact.protectedAt,
2244
+ reason: artifact.reason,
2245
+ source: artifact.source
2246
+ };
2247
+ if (artifact.validationCommand !== void 0) snapshot.validationCommand = artifact.validationCommand;
2248
+ if (artifact.validationExitCode !== void 0) snapshot.validationExitCode = artifact.validationExitCode;
2249
+ return snapshot;
2250
+ }
2251
+ function protectedArtifactFromSnapshot(snapshot) {
2252
+ const artifact = {
2253
+ path: snapshot.path,
2254
+ protectedAt: snapshot.protectedAt,
2255
+ reason: snapshot.reason,
2256
+ source: snapshot.source
2257
+ };
2258
+ if (snapshot.validationCommand !== void 0) artifact.validationCommand = snapshot.validationCommand;
2259
+ if (snapshot.validationExitCode !== void 0) artifact.validationExitCode = snapshot.validationExitCode;
2260
+ return artifact;
2261
+ }
2262
+ function compareProtectedEntryPositions(left, right) {
2263
+ return compareStrings(left.entry.timestamp, right.entry.timestamp) || compareStrings(left.entry.turnId, right.entry.turnId) || left.index - right.index;
2264
+ }
2265
+ function compareStrings(left, right) {
2266
+ if (left < right) return -1;
2267
+ if (left > right) return 1;
2268
+ return 0;
2269
+ }
2270
+
2271
+ // src/domains/session/usage.ts
2272
+ init_esm_shims();
2273
+ function numberAt(source, key) {
2274
+ const value = source[key];
2275
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
2276
+ }
2277
+ function reasoningTokensOf(usage) {
2278
+ const direct = numberAt(usage, "reasoning") || numberAt(usage, "reasoningTokens");
2279
+ if (direct > 0) return direct;
2280
+ const details = usage.outputTokensDetails ?? usage.completionTokensDetails;
2281
+ if (details && typeof details === "object") return numberAt(details, "reasoningTokens");
2282
+ return 0;
2283
+ }
2284
+ function stringAt(source, ...keys) {
2285
+ for (const key of keys) {
2286
+ const value = source[key];
2287
+ if (typeof value === "string" && value.trim().length > 0) return value;
2288
+ }
2289
+ return null;
2290
+ }
2291
+ function ledgerUsageCalls(entries, defaults = {}) {
2292
+ const calls = [];
2293
+ let currentTarget = defaults.target ?? null;
2294
+ let currentModel = defaults.model ?? null;
2295
+ for (const entry of entries) {
2296
+ if (entry?.kind === "compactionSummary") {
2297
+ const usage2 = entry.usage;
2298
+ if (!usage2 || usage2.totalTokens <= 0) continue;
2299
+ calls.push({
2300
+ providerId: usage2.targetId ?? currentTarget ?? "unknown",
2301
+ attributedModelId: usage2.modelId ?? currentModel ?? "unknown",
2302
+ requestedModelId: usage2.modelId ?? currentModel ?? "unknown",
2303
+ responseModelIdObservation: { state: "not-observed" },
2304
+ input: usage2.input,
2305
+ output: usage2.output,
2306
+ cacheRead: usage2.cacheRead,
2307
+ cacheWrite: usage2.cacheWrite,
2308
+ ...usage2.cacheWrite1h === void 0 ? {} : { cacheWrite1h: usage2.cacheWrite1h },
2309
+ reasoningTokens: usage2.reasoning,
2310
+ totalTokens: usage2.totalTokens,
2311
+ costUsd: usage2.cost.total,
2312
+ // A split turn runs two summarization streams under one entry; the
2313
+ // provider served that many calls even though one row records them.
2314
+ apiCalls: Math.max(1, Math.round(usage2.apiCalls))
2315
+ });
2316
+ continue;
2317
+ }
2318
+ if (entry?.kind === "modelChange") {
2319
+ const change = entry;
2320
+ if (typeof change.target === "string" && change.target.length > 0) currentTarget = change.target;
2321
+ else if (typeof change.provider === "string" && change.provider.length > 0) currentTarget = change.provider;
2322
+ if (typeof change.modelId === "string" && change.modelId.length > 0) currentModel = change.modelId;
2323
+ continue;
2324
+ }
2325
+ if (entry?.kind !== "message" || entry.role !== "assistant") continue;
2326
+ const payload = entry.payload;
2327
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) continue;
2328
+ const record = payload;
2329
+ const rawUsage = record.usage;
2330
+ if (!rawUsage || typeof rawUsage !== "object" || Array.isArray(rawUsage)) continue;
2331
+ const usage = rawUsage;
2332
+ const input = numberAt(usage, "input");
2333
+ const output = numberAt(usage, "output");
2334
+ const cacheRead = numberAt(usage, "cacheRead");
2335
+ const cacheWrite = numberAt(usage, "cacheWrite");
2336
+ const totalTokens = numberAt(usage, "totalTokens") || input + output + cacheRead + cacheWrite;
2337
+ if (totalTokens === 0) continue;
2338
+ const cost = usage.cost;
2339
+ const costUsd = cost && typeof cost === "object" ? numberAt(cost, "total") : 0;
2340
+ const requestedModelId = currentModel ?? stringAt(record, "model") ?? "unknown";
2341
+ const differingResponseModelId = stringAt(record, "responseModel");
2342
+ const responseModelIdObservation = responseModelIdObservationFromRecord(record, "legacy-difference-only");
2343
+ calls.push({
2344
+ providerId: currentTarget ?? stringAt(record, "provider", "api") ?? "unknown",
2345
+ attributedModelId: attributedModelId(responseModelIdObservation, requestedModelId, differingResponseModelId),
2346
+ requestedModelId,
2347
+ responseModelIdObservation,
2348
+ input,
2349
+ output,
2350
+ cacheRead,
2351
+ cacheWrite,
2352
+ ...typeof usage.cacheWrite1h === "number" ? { cacheWrite1h: numberAt(usage, "cacheWrite1h") } : {},
2353
+ reasoningTokens: reasoningTokensOf(usage),
2354
+ totalTokens,
2355
+ costUsd
2356
+ });
2357
+ }
2358
+ return calls;
2359
+ }
2360
+
2361
+ // src/domains/session/index.ts
2362
+ var SessionDomainModule = {
2363
+ manifest: SessionManifest,
2364
+ createExtension: createSessionBundle
2365
+ };
2366
+
2367
+ // src/domains/session/archive-readers.ts
2368
+ var ROUTING_NOTICE_CUSTOM_TYPE = "clio-coder.routing-notice";
2369
+ var LEGACY_ROUTING_NOTICE_CUSTOM_TYPE = "clio.routing-notice";
2370
+ function normalizeReleasedCustomType(entry) {
2371
+ if (entry.kind !== "custom" || entry.customType !== LEGACY_ROUTING_NOTICE_CUSTOM_TYPE) return entry;
2372
+ return { ...entry, customType: ROUTING_NOTICE_CUSTOM_TYPE };
2373
+ }
2374
+ async function readAuditRows(stateDir) {
2375
+ const root = join3(stateDir, "audit");
2376
+ let files;
2377
+ try {
2378
+ files = await readdir(root);
2379
+ } catch (error) {
2380
+ const err = error;
2381
+ if (err.code === "ENOENT") return { rows: [], errors: [] };
2382
+ return { rows: [], errors: [`audit root read error: ${err.message ?? String(err)}`] };
2383
+ }
2384
+ const rows = [];
2385
+ const errors = [];
2386
+ for (const file of files.filter((name) => name.endsWith(".jsonl")).sort(compareStrings2)) {
2387
+ const path = join3(root, file);
2388
+ let raw;
2389
+ try {
2390
+ raw = await readFile(path, "utf8");
2391
+ } catch (error) {
2392
+ const err = error;
2393
+ errors.push(`${path}: ${err.message ?? String(err)}`);
2394
+ continue;
2395
+ }
2396
+ const lines = raw.split("\n");
2397
+ for (let index = 0; index < lines.length; index += 1) {
2398
+ const line = lines[index];
2399
+ if (line === void 0 || line.length === 0) continue;
2400
+ let parsed;
2401
+ try {
2402
+ parsed = JSON.parse(line);
2403
+ } catch (error) {
2404
+ const message = error instanceof Error ? error.message : String(error);
2405
+ errors.push(`${path}:${index + 1}: invalid JSON: ${message}`);
2406
+ continue;
2407
+ }
2408
+ if (!isRecord(parsed)) {
2409
+ errors.push(`${path}:${index + 1}: expected object`);
2410
+ continue;
2411
+ }
2412
+ const auditKind = readOptionalString(parsed.kind) ?? "tool_call";
2413
+ rows.push({
2414
+ file,
2415
+ line: index + 1,
2416
+ row: parsed,
2417
+ ts: readOptionalString(parsed.ts),
2418
+ auditKind,
2419
+ correlationId: readOptionalString(parsed.correlationId) ?? ""
2420
+ });
2421
+ }
2422
+ }
2423
+ return { rows, errors };
2424
+ }
2425
+ async function readSessionEntriesForId(stateDir, sessionId) {
2426
+ const root = join3(stateDir, "sessions");
2427
+ let cwdHashes;
2428
+ try {
2429
+ cwdHashes = await readdir(root);
2430
+ } catch (error) {
2431
+ const err = error;
2432
+ if (err.code === "ENOENT") return { entries: [], missing: true, errors: [] };
2433
+ return { entries: [], missing: false, errors: [`session root read error: ${err.message ?? String(err)}`] };
2434
+ }
2435
+ for (const cwdHash of cwdHashes.sort(compareStrings2)) {
2436
+ const currentPath = join3(root, cwdHash, sessionId, "current.jsonl");
2437
+ let raw;
2438
+ let source = currentPath;
2439
+ try {
2440
+ raw = await readFile(currentPath, "utf8");
2441
+ } catch (error) {
2442
+ const err = error;
2443
+ if (err.code === "ENOENT") {
2444
+ const tmpPath = `${currentPath}.tmp`;
2445
+ try {
2446
+ raw = await readFile(tmpPath, "utf8");
2447
+ source = tmpPath;
2448
+ } catch {
2449
+ continue;
2450
+ }
2451
+ } else if (err.code === "ENOTDIR") {
2452
+ continue;
2453
+ } else {
2454
+ return { entries: [], missing: false, errors: [`${currentPath}: ${err.message ?? String(err)}`] };
2455
+ }
2456
+ }
2457
+ const result = parseSessionEntries(raw, source);
2458
+ try {
2459
+ const meta = JSON.parse(await readFile(join3(root, cwdHash, sessionId, "meta.json"), "utf8"));
2460
+ if (isRecord(meta) && typeof meta.pinnedLeafTurnId === "string") result.leafTurnId = meta.pinnedLeafTurnId;
2461
+ } catch {
2462
+ }
2463
+ return result;
2464
+ }
2465
+ return { entries: [], missing: true, errors: [] };
2466
+ }
2467
+ function parseSessionEntries(raw, source) {
2468
+ const entries = [];
2469
+ const errors = [];
2470
+ const lines = raw.split("\n");
2471
+ for (let index = 0; index < lines.length; index += 1) {
2472
+ const line = lines[index];
2473
+ if (line === void 0 || line.trim().length === 0) continue;
2474
+ let parsed;
2475
+ try {
2476
+ parsed = JSON.parse(line);
2477
+ } catch (error) {
2478
+ const message = error instanceof Error ? error.message : String(error);
2479
+ errors.push(`${source}:${index + 1}: invalid JSON: ${message}`);
2480
+ continue;
2481
+ }
2482
+ if (isSessionHeader(parsed)) continue;
2483
+ if (isSessionEntry(parsed)) {
2484
+ entries.push(normalizeReleasedCustomType(parsed));
2485
+ continue;
2486
+ }
2487
+ errors.push(
2488
+ `session ledger contains an unreadable entry (missing kind discriminant): ${source}. Remove or compact the session to reset.`
2489
+ );
2490
+ }
2491
+ return { entries, missing: false, errors };
2492
+ }
2493
+ async function listSessionLedgerRefs(stateDir, onlyCwdHash) {
2494
+ const root = join3(stateDir, "sessions");
2495
+ let cwdHashes;
2496
+ try {
2497
+ cwdHashes = await readdir(root);
2498
+ } catch {
2499
+ return [];
2500
+ }
2501
+ const refs = [];
2502
+ for (const cwdHash of cwdHashes.sort(compareStrings2)) {
2503
+ if (onlyCwdHash !== void 0 && cwdHash !== onlyCwdHash) continue;
2504
+ let sessionIds;
2505
+ try {
2506
+ sessionIds = await readdir(join3(root, cwdHash));
2507
+ } catch {
2508
+ continue;
2509
+ }
2510
+ for (const sessionId of sessionIds.sort(compareStrings2)) {
2511
+ refs.push({ cwdHash, sessionId, path: join3(root, cwdHash, sessionId, "current.jsonl") });
2512
+ }
2513
+ }
2514
+ return refs;
2515
+ }
2516
+ function readOptionalString(value) {
2517
+ return typeof value === "string" && value.length > 0 ? value : null;
2518
+ }
2519
+ function isRecord(value) {
2520
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2521
+ }
2522
+ function compareStrings2(a, b) {
2523
+ return a.localeCompare(b);
2524
+ }
2525
+
2526
+ export {
2527
+ emptyResponseModelIdObservationCounts,
2528
+ addResponseModelIdObservationCount,
2529
+ addResponseModelIdObservationCounts,
2530
+ responseModelIdObservationCountsLabel,
2531
+ responseModelIdObservationFromRecord,
2532
+ attributedModelId,
2533
+ buildTurnPreview,
2534
+ readAuditRows,
2535
+ readSessionEntriesForId,
2536
+ parseSessionEntries,
2537
+ listSessionLedgerRefs,
2538
+ createContinuityPersistencePorts,
2539
+ resolveContinuityEvidence,
2540
+ validateContinuityNote,
2541
+ resolveContinuityProjection,
2542
+ continuityReplayText,
2543
+ continuityReplayBlocks,
2544
+ continuityProjectionTokens,
2545
+ protectedArtifactEntryFromArtifact,
2546
+ protectedArtifactFromSessionEntry,
2547
+ protectedArtifactStateFromSessionEntries,
2548
+ ledgerUsageCalls,
2549
+ SessionDomainModule
2550
+ };
2551
+ //# sourceMappingURL=chunk-GPTR6OTU.js.map