@rryando/arcs 3.11.0 → 4.0.0

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 (191) hide show
  1. package/dist/cli/commands/hooks.d.ts +46 -6
  2. package/dist/cli/commands/hooks.d.ts.map +1 -1
  3. package/dist/cli/commands/hooks.js +190 -52
  4. package/dist/cli/commands/hooks.js.map +1 -1
  5. package/dist/cli/commands/web.js +14 -4
  6. package/dist/cli/commands/web.js.map +1 -1
  7. package/dist/cli/config.d.ts +14 -3
  8. package/dist/cli/config.d.ts.map +1 -1
  9. package/dist/cli/config.js +39 -38
  10. package/dist/cli/config.js.map +1 -1
  11. package/dist/cli/instructions.d.ts +13 -1
  12. package/dist/cli/instructions.d.ts.map +1 -1
  13. package/dist/cli/instructions.js +38 -3
  14. package/dist/cli/instructions.js.map +1 -1
  15. package/dist/cli/setup.d.ts.map +1 -1
  16. package/dist/cli/setup.js +76 -11
  17. package/dist/cli/setup.js.map +1 -1
  18. package/dist/shared/session-vocabulary.d.ts +88 -0
  19. package/dist/shared/session-vocabulary.d.ts.map +1 -0
  20. package/dist/shared/session-vocabulary.js +114 -0
  21. package/dist/shared/session-vocabulary.js.map +1 -0
  22. package/dist/utils/claude-code-hook-install.d.ts +1 -1
  23. package/dist/utils/claude-code-hook-install.d.ts.map +1 -1
  24. package/dist/utils/claude-code-hook-install.js +68 -3
  25. package/dist/utils/claude-code-hook-install.js.map +1 -1
  26. package/dist/utils/claude-transcript.d.ts +81 -2
  27. package/dist/utils/claude-transcript.d.ts.map +1 -1
  28. package/dist/utils/claude-transcript.js +33 -2
  29. package/dist/utils/claude-transcript.js.map +1 -1
  30. package/dist/utils/git.d.ts +21 -0
  31. package/dist/utils/git.d.ts.map +1 -1
  32. package/dist/utils/git.js +65 -1
  33. package/dist/utils/git.js.map +1 -1
  34. package/dist/utils/hook-contract.d.ts +36 -0
  35. package/dist/utils/hook-contract.d.ts.map +1 -0
  36. package/dist/utils/hook-contract.js +35 -0
  37. package/dist/utils/hook-contract.js.map +1 -0
  38. package/dist/utils/hook-token-store.d.ts +39 -0
  39. package/dist/utils/hook-token-store.d.ts.map +1 -1
  40. package/dist/utils/hook-token-store.js +46 -3
  41. package/dist/utils/hook-token-store.js.map +1 -1
  42. package/dist/utils/session-store.d.ts +226 -0
  43. package/dist/utils/session-store.d.ts.map +1 -1
  44. package/dist/utils/session-store.js +266 -1
  45. package/dist/utils/session-store.js.map +1 -1
  46. package/dist/web-client/assets/{GraphCanvas-dNyZ458L.js → GraphCanvas-CTyf_XXQ.js} +1 -1
  47. package/dist/web-client/assets/{MarkdownEditor-BmU9mdkN.js → MarkdownEditor-af2vwQOX.js} +1 -1
  48. package/dist/web-client/assets/{abnfDiagram-VRR7QNED-D1BFBoeF.js → abnfDiagram-VRR7QNED-CdxcKX9t.js} +1 -1
  49. package/dist/web-client/assets/architecture-TIHT7OUA-Bt2-DmfV.js +1 -0
  50. package/dist/web-client/assets/{architectureDiagram-ZJ3FMSHR-CA8hTWUK.js → architectureDiagram-ZJ3FMSHR-DOISDv6o.js} +1 -1
  51. package/dist/web-client/assets/{blockDiagram-677ZJIJ3-CQpb_KwX.js → blockDiagram-677ZJIJ3-DKwtbttM.js} +1 -1
  52. package/dist/web-client/assets/{c4Diagram-LMCZKHZV-BhpHX84V.js → c4Diagram-LMCZKHZV-CilqK-Mm.js} +1 -1
  53. package/dist/web-client/assets/channel-C2mb9np_.js +1 -0
  54. package/dist/web-client/assets/{chunk-32BRIVSS-BUusQQa_.js → chunk-32BRIVSS-C79m1mkG.js} +1 -1
  55. package/dist/web-client/assets/{chunk-52WLFC77-nNYwlftl.js → chunk-52WLFC77-C6WelGWJ.js} +1 -1
  56. package/dist/web-client/assets/{chunk-C7G6YPKG-D0a-yqnK.js → chunk-C7G6YPKG-DLg7ryWI.js} +1 -1
  57. package/dist/web-client/assets/{chunk-EX3LRPZG-IDuRMN-4.js → chunk-EX3LRPZG-DjWgo4gL.js} +1 -1
  58. package/dist/web-client/assets/{chunk-FWX5IMBZ-D_spTcqi.js → chunk-FWX5IMBZ-BgS9p_zy.js} +2 -2
  59. package/dist/web-client/assets/{chunk-HOUHSVGY-C-bcxwZS.js → chunk-HOUHSVGY-DuVR7dZO.js} +1 -1
  60. package/dist/web-client/assets/{chunk-ICXQ74PX-CjmK2bAM.js → chunk-ICXQ74PX-Y8DlnIJM.js} +1 -1
  61. package/dist/web-client/assets/{chunk-MOJQB5TN-Bkc08KWY.js → chunk-MOJQB5TN-BEM3QgeD.js} +1 -1
  62. package/dist/web-client/assets/{chunk-OGEWGWER-BAAYYvG9.js → chunk-OGEWGWER-DlM8LxGr.js} +1 -1
  63. package/dist/web-client/assets/{chunk-PUDLZKDR-WXbPY7NM.js → chunk-PUDLZKDR-c6cqNVTx.js} +1 -1
  64. package/dist/web-client/assets/{chunk-Q4XR5HBZ-DcbnjxQE.js → chunk-Q4XR5HBZ-C5lNmcka.js} +1 -1
  65. package/dist/web-client/assets/{chunk-V7JOEXUC-C6t75PAp.js → chunk-V7JOEXUC-nSswxvSG.js} +1 -1
  66. package/dist/web-client/assets/{chunk-VAUOI2AC-DVrJ0Ic7.js → chunk-VAUOI2AC-CqJkCkT0.js} +1 -1
  67. package/dist/web-client/assets/{chunk-VR4S4FIN-MOOFvGS0.js → chunk-VR4S4FIN-D3pFchin.js} +1 -1
  68. package/dist/web-client/assets/{chunk-WYO6CB5R-sK7Y2NZD.js → chunk-WYO6CB5R-BI9c-NzI.js} +1 -1
  69. package/dist/web-client/assets/{chunk-ZGVPDNZ5-a13RQsku.js → chunk-ZGVPDNZ5-CKTF2kLR.js} +1 -1
  70. package/dist/web-client/assets/classDiagram-OUVF2IWQ-CPd_w3Zr.js +1 -0
  71. package/dist/web-client/assets/classDiagram-v2-EOCWNBFH-CPd_w3Zr.js +1 -0
  72. package/dist/web-client/assets/{cynefin-VYW2F7L2-D8xaH-wO.js → cynefin-VYW2F7L2-BrJrnfh1.js} +1 -1
  73. package/dist/web-client/assets/{cynefinDiagram-TSTJHNR4-Bt__EqJW.js → cynefinDiagram-TSTJHNR4-9jYzPWJZ.js} +1 -1
  74. package/dist/web-client/assets/{dagre-VKFMJZFB-Bwgjwflz.js → dagre-VKFMJZFB-B705Djpu.js} +1 -1
  75. package/dist/web-client/assets/{diagram-FQU43EPY-CaPDVUq2.js → diagram-FQU43EPY-BfQAlUlJ.js} +1 -1
  76. package/dist/web-client/assets/{diagram-G47NLZAW-BQLB9YYA.js → diagram-G47NLZAW-s1YDe06A.js} +1 -1
  77. package/dist/web-client/assets/{diagram-NH7WQ7WH-BWo84w8Y.js → diagram-NH7WQ7WH-Dy3z11Hc.js} +1 -1
  78. package/dist/web-client/assets/{diagram-OA4YK3LP-bKn6Pz5s.js → diagram-OA4YK3LP-Bius2xUN.js} +1 -1
  79. package/dist/web-client/assets/{diagram-WEI45ONY-BRqV5Oy6.js → diagram-WEI45ONY-D7x4VcHM.js} +1 -1
  80. package/dist/web-client/assets/{ebnfDiagram-CCIWWBDH-DhUXL1-7.js → ebnfDiagram-CCIWWBDH-CBg1xrmD.js} +1 -1
  81. package/dist/web-client/assets/{erDiagram-Q63AITRT-HXAQQ-_F.js → erDiagram-Q63AITRT-CJRvTFvd.js} +1 -1
  82. package/dist/web-client/assets/eventmodeling-45OFAUF4-CTKRC2Yj.js +1 -0
  83. package/dist/web-client/assets/flowDiagram-23GEKE2U-CaZjFyU1.js +1 -0
  84. package/dist/web-client/assets/{ganttDiagram-NO4QXBWP-D-Ddf_Ii.js → ganttDiagram-NO4QXBWP-C_LsypZ4.js} +1 -1
  85. package/dist/web-client/assets/{gitGraph-TEB2WS4Q-BmHzs0uF.js → gitGraph-TEB2WS4Q-Dx2XxdGk.js} +1 -1
  86. package/dist/web-client/assets/{gitGraphDiagram-IHSO6WYX-YnQWrlh_.js → gitGraphDiagram-IHSO6WYX-DiKWkGWQ.js} +1 -1
  87. package/dist/web-client/assets/{index-DCWxuIeQ.js → index-CYwhkPtc.js} +38 -36
  88. package/dist/web-client/assets/index-wSzUPvml.css +2 -0
  89. package/dist/web-client/assets/{info-DKCQHKI2-DCT_B7RN.js → info-DKCQHKI2-DORwHenK.js} +1 -1
  90. package/dist/web-client/assets/{infoDiagram-FWYZ7A6U-D-le1Zhq.js → infoDiagram-FWYZ7A6U-CQecXS1E.js} +1 -1
  91. package/dist/web-client/assets/{ishikawaDiagram-FXEZZL3T-Jr1x2VJB.js → ishikawaDiagram-FXEZZL3T-CWhj60Zp.js} +1 -1
  92. package/dist/web-client/assets/{journeyDiagram-5HDEW3XC-BF9ELxj-.js → journeyDiagram-5HDEW3XC-Cj3z2U8u.js} +1 -1
  93. package/dist/web-client/assets/{kanban-definition-HUTT4EX6-C4fJqAxu.js → kanban-definition-HUTT4EX6-DFmBRenP.js} +1 -1
  94. package/dist/web-client/assets/{line-7N7ikFxa.js → line-C_Hxz9xb.js} +1 -1
  95. package/dist/web-client/assets/{mermaid-parser.core-QbC1icPt.js → mermaid-parser.core-E3xRdzPn.js} +3 -3
  96. package/dist/web-client/assets/{mermaid.core-C26d_UJm.js → mermaid.core-C2_CcotH.js} +3 -3
  97. package/dist/web-client/assets/{mindmap-definition-LN4V7U3C-D6TV1JDf.js → mindmap-definition-LN4V7U3C-Bs7VxPid.js} +1 -1
  98. package/dist/web-client/assets/{packet-7NZHBO7P-CR1vrGj3.js → packet-7NZHBO7P-D2nqso7i.js} +1 -1
  99. package/dist/web-client/assets/{pegDiagram-2B236MQR-xOMBBtfV.js → pegDiagram-2B236MQR-Cepo1sIu.js} +1 -1
  100. package/dist/web-client/assets/{pie-RZYD4A2V-BbWuhjwy.js → pie-RZYD4A2V-Sx0IGbXt.js} +1 -1
  101. package/dist/web-client/assets/{pieDiagram-ENE6RG2P-MsfnsqgW.js → pieDiagram-ENE6RG2P-D-PMDNhk.js} +1 -1
  102. package/dist/web-client/assets/{quadrantDiagram-ABIIQ3AL-BoI7zKXF.js → quadrantDiagram-ABIIQ3AL-BNGC7Fms.js} +1 -1
  103. package/dist/web-client/assets/{radar-I7S5WNFK-CbYXKToJ.js → radar-I7S5WNFK-CFf_-2OC.js} +1 -1
  104. package/dist/web-client/assets/{railroad-3IZDKUUU-6LxHDkLe.js → railroad-3IZDKUUU-BvZeVRGh.js} +1 -1
  105. package/dist/web-client/assets/railroad-abnf-AHOZXSZD-DnGh6x2O.js +1 -0
  106. package/dist/web-client/assets/railroad-ebnf-EBAXGLYW-CGfgCoaE.js +1 -0
  107. package/dist/web-client/assets/railroad-peg-LSFZ7HO6-BzQxYdqp.js +1 -0
  108. package/dist/web-client/assets/{railroadDiagram-RFXS5EU6-D6RUoUki.js → railroadDiagram-RFXS5EU6-D0iK_k84.js} +1 -1
  109. package/dist/web-client/assets/{requirementDiagram-TGXJPOKE-B6k4BDpE.js → requirementDiagram-TGXJPOKE-BPdsw9tz.js} +1 -1
  110. package/dist/web-client/assets/{sankeyDiagram-HTMAVEWB-BUDF-UFr.js → sankeyDiagram-HTMAVEWB-maA6ukOJ.js} +1 -1
  111. package/dist/web-client/assets/{sequenceDiagram-DBY2YBRQ-D6GqcsUi.js → sequenceDiagram-DBY2YBRQ-DKmAjmgr.js} +1 -1
  112. package/dist/web-client/assets/{stateDiagram-2N3HPSRC-WfJCQAK5.js → stateDiagram-2N3HPSRC-esxmzk3w.js} +1 -1
  113. package/dist/web-client/assets/stateDiagram-v2-6OUMAXLB-hcBqlOLc.js +1 -0
  114. package/dist/web-client/assets/{swimlanes-5IMT3BWC-BtMo82mC.js → swimlanes-5IMT3BWC-DFd-wGBv.js} +1 -1
  115. package/dist/web-client/assets/swimlanesDiagram-G3AALYLV-DbSrPBYr.js +8 -0
  116. package/dist/web-client/assets/{timeline-definition-FHXFAJF6-CoAmv2Sn.js → timeline-definition-FHXFAJF6-C7SG8MQH.js} +1 -1
  117. package/dist/web-client/assets/{treeView-QDETBFTQ-BWsKzE1s.js → treeView-QDETBFTQ-M0rhxbs2.js} +1 -1
  118. package/dist/web-client/assets/{treemap-6X3UGDF4-i_qGtB3o.js → treemap-6X3UGDF4-BQfkQxSh.js} +1 -1
  119. package/dist/web-client/assets/{vennDiagram-L72KCM5P-DYkiLe-P.js → vennDiagram-L72KCM5P-CyIlb8ju.js} +1 -1
  120. package/dist/web-client/assets/{wardley-OPB4EBWU-Daaqr1Vp.js → wardley-OPB4EBWU-EE-7q8M1.js} +1 -1
  121. package/dist/web-client/assets/{wardleyDiagram-EHGQE667-Bofbsg3J.js → wardleyDiagram-EHGQE667-bY8XdYYS.js} +1 -1
  122. package/dist/web-client/assets/{xychartDiagram-FW5EYKEG-KNF4VTfL.js → xychartDiagram-FW5EYKEG-AhuXJ3de.js} +1 -1
  123. package/dist/web-client/index.html +2 -2
  124. package/dist/web-server/app.d.ts +4 -1
  125. package/dist/web-server/app.d.ts.map +1 -1
  126. package/dist/web-server/app.js +19 -0
  127. package/dist/web-server/app.js.map +1 -1
  128. package/dist/web-server/claude-runner.d.ts +109 -7
  129. package/dist/web-server/claude-runner.d.ts.map +1 -1
  130. package/dist/web-server/claude-runner.js +329 -50
  131. package/dist/web-server/claude-runner.js.map +1 -1
  132. package/dist/web-server/index.d.ts +2 -2
  133. package/dist/web-server/index.d.ts.map +1 -1
  134. package/dist/web-server/index.js +3 -2
  135. package/dist/web-server/index.js.map +1 -1
  136. package/dist/web-server/permission-policy.d.ts +46 -0
  137. package/dist/web-server/permission-policy.d.ts.map +1 -0
  138. package/dist/web-server/permission-policy.js +96 -0
  139. package/dist/web-server/permission-policy.js.map +1 -0
  140. package/dist/web-server/prompt-assembly.d.ts +454 -0
  141. package/dist/web-server/prompt-assembly.d.ts.map +1 -0
  142. package/dist/web-server/prompt-assembly.js +1122 -0
  143. package/dist/web-server/prompt-assembly.js.map +1 -0
  144. package/dist/web-server/routes/hook-events.d.ts +28 -3
  145. package/dist/web-server/routes/hook-events.d.ts.map +1 -1
  146. package/dist/web-server/routes/hook-events.js +209 -24
  147. package/dist/web-server/routes/hook-events.js.map +1 -1
  148. package/dist/web-server/routes/sessions.d.ts +7 -5
  149. package/dist/web-server/routes/sessions.d.ts.map +1 -1
  150. package/dist/web-server/routes/sessions.js +1030 -174
  151. package/dist/web-server/routes/sessions.js.map +1 -1
  152. package/dist/web-server/routes/workspace.d.ts +27 -0
  153. package/dist/web-server/routes/workspace.d.ts.map +1 -0
  154. package/dist/web-server/routes/workspace.js +280 -0
  155. package/dist/web-server/routes/workspace.js.map +1 -0
  156. package/dist/web-server/run-event-log.d.ts +167 -0
  157. package/dist/web-server/run-event-log.d.ts.map +1 -0
  158. package/dist/web-server/run-event-log.js +468 -0
  159. package/dist/web-server/run-event-log.js.map +1 -0
  160. package/dist/web-server/session-reconciler.d.ts +162 -0
  161. package/dist/web-server/session-reconciler.d.ts.map +1 -0
  162. package/dist/web-server/session-reconciler.js +363 -0
  163. package/dist/web-server/session-reconciler.js.map +1 -0
  164. package/dist/web-server/static.d.ts +7 -0
  165. package/dist/web-server/static.d.ts.map +1 -1
  166. package/dist/web-server/static.js +46 -3
  167. package/dist/web-server/static.js.map +1 -1
  168. package/dist/web-server/web-auth.d.ts +17 -0
  169. package/dist/web-server/web-auth.d.ts.map +1 -0
  170. package/dist/web-server/web-auth.js +33 -0
  171. package/dist/web-server/web-auth.js.map +1 -0
  172. package/dist/web-server/web-token.d.ts +39 -0
  173. package/dist/web-server/web-token.d.ts.map +1 -0
  174. package/dist/web-server/web-token.js +71 -0
  175. package/dist/web-server/web-token.js.map +1 -0
  176. package/opencode/arcs/manifest.json +8 -8
  177. package/package.json +1 -1
  178. package/scripts/claude-code-session-hook.mjs +49 -3
  179. package/scripts/deploy-claudecode-bundle.mjs +24 -3
  180. package/dist/web-client/assets/architecture-TIHT7OUA-CJqI5wNI.js +0 -1
  181. package/dist/web-client/assets/channel-C8DlmyVe.js +0 -1
  182. package/dist/web-client/assets/classDiagram-OUVF2IWQ-p32N1P_G.js +0 -1
  183. package/dist/web-client/assets/classDiagram-v2-EOCWNBFH-p32N1P_G.js +0 -1
  184. package/dist/web-client/assets/eventmodeling-45OFAUF4-Bj5P8mZJ.js +0 -1
  185. package/dist/web-client/assets/flowDiagram-23GEKE2U-37BztFri.js +0 -1
  186. package/dist/web-client/assets/index-3mNPVkix.css +0 -2
  187. package/dist/web-client/assets/railroad-abnf-AHOZXSZD-2Dg9wu0J.js +0 -1
  188. package/dist/web-client/assets/railroad-ebnf-EBAXGLYW-C_E2ot0R.js +0 -1
  189. package/dist/web-client/assets/railroad-peg-LSFZ7HO6-Bs9UQR1b.js +0 -1
  190. package/dist/web-client/assets/stateDiagram-v2-6OUMAXLB-BCMWGnsJ.js +0 -1
  191. package/dist/web-client/assets/swimlanesDiagram-G3AALYLV-DmudmLcK.js +0 -8
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Per-run stdout event log — the durable spine of a headless `claude -p` run.
3
+ *
4
+ * Owns `sessions/{normalizedId}.run-{runId}.events.jsonl`: every byte the child
5
+ * writes to stdout lands here VERBATIM, one file line per stdout line, BEFORE
6
+ * anything parses it. That ordering is the whole point — the log is the source
7
+ * of truth and the runner's NDJSON reader is merely its first consumer, so a
8
+ * line the reader cannot parse (drifted wire format, truncated JSON, an event
9
+ * type that did not exist when this shipped) is still on disk, and a process
10
+ * that dies mid-run still leaves behind everything that arrived.
11
+ *
12
+ * Writes are synchronous (`writeSync` against an fd opened with "a") for
13
+ * exactly that reason: an async queue would only ever have *issued* the write
14
+ * before the parse, and a hard kill would lose whatever was still in flight.
15
+ * The chunks are small and few (one per stdout chunk of a single child), so the
16
+ * event-loop cost is a rounding error against a run measured in minutes.
17
+ *
18
+ * The `runId` in the filename is the SAME id the route persists as
19
+ * `currentRunId`, so the log's name and the session record can never disagree
20
+ * about which run produced it.
21
+ *
22
+ * Total by construction, like the reader it feeds: nothing here throws. A log
23
+ * that cannot be opened or written reports itself through `RunEventLogStats`
24
+ * and the run proceeds untouched — a logging failure must never fail a run.
25
+ * Reporting is the whole point: a log that hit its size cap or lost bytes to a
26
+ * short write says so on `truncated`, which rides all the way out to
27
+ * `metadata.run.eventLogTruncated`. A silent fallback here would make an empty
28
+ * log and a capped one look identical to every consumer downstream.
29
+ *
30
+ * At settle the log folds down into the transcript sidecar (assistant text plus
31
+ * one turn per `tool_use`) through `appendSessionTurn`, and older logs for the
32
+ * same session are pruned so the sessions dir cannot grow without limit.
33
+ */
34
+ import { closeSync, mkdirSync, openSync, writeSync } from "node:fs";
35
+ import { readdir, readFile, stat, unlink } from "node:fs/promises";
36
+ import { dirname, join } from "node:path";
37
+ import { appendSessionTurn, readSessionTurns } from "../utils/claude-transcript.js";
38
+ import { normalizeIdentifier } from "../utils/slug.js";
39
+ /**
40
+ * Ceiling on one run's log — a runaway child cannot fill the disk.
41
+ *
42
+ * The finished file is never LARGER than this, terminator included: the writer
43
+ * refuses a chunk that would take it TO the cap (`>=`, not `>`), leaving the one
44
+ * byte `flush()` may still need. The off-by-one that leaves matters because
45
+ * `foldRunEventLog` refuses to read anything past this size — a writer allowed
46
+ * to land on exactly the cap and then add a terminator produces a file its own
47
+ * fold silently discards.
48
+ *
49
+ * Reaching it does not close the log: only the crossing chunk is refused, later
50
+ * chunks that still fit are still written, and `RunEventLogStats.truncated`
51
+ * reports that bytes are missing. The file always ends on a line boundary, so an
52
+ * offset-based tail never reads half a record.
53
+ */
54
+ export const RUN_EVENT_LOG_MAX_BYTES = 32 * 1024 * 1024;
55
+ /**
56
+ * How many event logs a single session keeps. Count-based rather than
57
+ * age-based: a count is a hard bound (N logs per session, at most
58
+ * RUN_EVENT_LOG_MAX_BYTES each), while an age window still admits unbounded
59
+ * growth from a session that runs in a tight loop.
60
+ */
61
+ export const RUN_EVENT_LOG_RETENTION = 5;
62
+ const LOG_SUFFIX = ".events.jsonl";
63
+ const NEWLINE = 0x0a;
64
+ /** The only byte the log ever adds of its own: a line terminator. */
65
+ const TERMINATOR = Buffer.from("\n", "utf-8");
66
+ /**
67
+ * Filename-safe form of a run id. The id reaches a path, so it is sanitized
68
+ * rather than trusted: anything outside `[A-Za-z0-9_-]` (`/`, `.`, `..`) becomes
69
+ * `-` before it can traverse out of the sessions dir.
70
+ */
71
+ export function runEventLogSegment(runId) {
72
+ const safe = runId.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 64);
73
+ return safe === "" ? "unknown" : safe;
74
+ }
75
+ /**
76
+ * Log path for one run. Keys on the session's canonical `normalizedId` exactly
77
+ * as the transcript sidecar does, so both files for a session sort together and
78
+ * neither can be derived from a raw route id.
79
+ */
80
+ export function runEventLogPath(projectDir, sessionId, runId) {
81
+ const name = `${normalizeIdentifier(sessionId)}.run-${runEventLogSegment(runId)}${LOG_SUFFIX}`;
82
+ return join(projectDir, "sessions", name);
83
+ }
84
+ /** The handle a run with no log target gets — every method is a no-op. */
85
+ const NOOP_HANDLE = {
86
+ path: "",
87
+ push() { },
88
+ flush() { },
89
+ close() {
90
+ return { lines: 0, bytes: 0, truncated: false };
91
+ },
92
+ };
93
+ /**
94
+ * Opens (creating) the run's log and returns a write-through handle.
95
+ *
96
+ * The file is created up front, so a run that produces no stdout at all — a
97
+ * child killed at its deadline before it spoke, an immediate non-zero exit —
98
+ * still leaves the log behind. `undefined` target yields the no-op handle, which
99
+ * is how the runner stays usable without a project dir (tests, and any caller
100
+ * that does not want a log).
101
+ */
102
+ export function openRunEventLog(target) {
103
+ if (target === undefined)
104
+ return NOOP_HANDLE;
105
+ const path = runEventLogPath(target.projectDir, target.sessionId, target.runId);
106
+ let fd = null;
107
+ let lines = 0;
108
+ let bytes = 0;
109
+ let truncated = false;
110
+ let closed = false;
111
+ let endsWithNewline = true;
112
+ /**
113
+ * Bytes were dropped part-way through a record, so the stream now continues
114
+ * INSIDE a line this file does not have. Appending those bytes would splice
115
+ * the tail of a record the log never took onto whatever precedes it and emit
116
+ * a line the child never wrote, so they are dropped until the next newline.
117
+ */
118
+ let resyncing = false;
119
+ let error;
120
+ const note = (err) => {
121
+ if (error === undefined)
122
+ error = String(err);
123
+ };
124
+ try {
125
+ mkdirSync(dirname(path), { recursive: true });
126
+ // "a" — the kernel appends at every write, so nothing here has to track a
127
+ // file position and a concurrent reader (the tail) can never see a hole.
128
+ fd = openSync(path, "a");
129
+ }
130
+ catch (err) {
131
+ note(err);
132
+ }
133
+ const stats = () => ({
134
+ lines,
135
+ bytes,
136
+ truncated,
137
+ ...(error !== undefined && { error }),
138
+ });
139
+ /**
140
+ * Appends `buf` whole, returning how many of its bytes actually landed.
141
+ *
142
+ * `writeSync` may return SHORT — a signal interrupting the syscall (EINTR), a
143
+ * filesystem that ran out of room mid-buffer (ENOSPC) — and its return value
144
+ * is the only place that ever shows. Discarding it emits half a record while
145
+ * counting a whole one, which breaks "always ends on a line boundary" exactly
146
+ * when the disk is full. So the loop keeps writing while the fd makes
147
+ * progress, and reports precisely how far it got. `n <= 0` ends the loop
148
+ * rather than spinning on an fd that has stopped accepting bytes.
149
+ */
150
+ const writeAll = (handle, buf) => {
151
+ let written = 0;
152
+ while (written < buf.length) {
153
+ let n;
154
+ try {
155
+ n = writeSync(handle, buf, written, buf.length - written);
156
+ }
157
+ catch (err) {
158
+ note(err);
159
+ break;
160
+ }
161
+ if (n <= 0)
162
+ break;
163
+ written += n;
164
+ }
165
+ return written;
166
+ };
167
+ /** Counts what landed: bytes, whole lines, and where the file now ends. */
168
+ const account = (buf) => {
169
+ if (buf.length === 0)
170
+ return;
171
+ bytes += buf.length;
172
+ // Native indexOf rather than a per-byte loop: a megabyte chunk must not
173
+ // cost a million iterations on the event loop.
174
+ for (let at = buf.indexOf(NEWLINE); at !== -1; at = buf.indexOf(NEWLINE, at + 1)) {
175
+ lines += 1;
176
+ }
177
+ endsWithNewline = buf[buf.length - 1] === NEWLINE;
178
+ };
179
+ /**
180
+ * Closes a line the child left open. The only byte the log adds of its own,
181
+ * and only when the file would otherwise not end on a line boundary — an
182
+ * offset-based tail would mis-frame the last record without it.
183
+ *
184
+ * Runs at flush() AND the instant a chunk is refused or lands short, not just
185
+ * at close: after a gap in the stream the next accepted chunk must start its
186
+ * own line rather than extend the fragment before the gap.
187
+ *
188
+ * The terminator write can itself fail, and failing SILENTLY is how a
189
+ * fabricated line survives every other guard here: the file is left
190
+ * un-line-closed with `resyncing` unset, so an fd that recovers splices the
191
+ * next chunk onto the open fragment. A terminator that does not land therefore
192
+ * sets BOTH flags — `truncated` because the file is no longer line-framed, and
193
+ * `resyncing` because nothing may extend that fragment ever again.
194
+ */
195
+ const terminate = (handle) => {
196
+ if (endsWithNewline || bytes === 0)
197
+ return;
198
+ if (writeAll(handle, TERMINATOR) !== TERMINATOR.length) {
199
+ truncated = true;
200
+ resyncing = true;
201
+ return;
202
+ }
203
+ account(TERMINATOR);
204
+ };
205
+ return {
206
+ path,
207
+ push(chunk) {
208
+ if (fd === null || closed)
209
+ return;
210
+ let buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf-8");
211
+ if (buf.length === 0)
212
+ return;
213
+ if (resyncing) {
214
+ const at = buf.indexOf(NEWLINE);
215
+ // Still inside the record whose bytes were dropped — all of it belongs
216
+ // to that record, none of it is a line this file may claim to hold.
217
+ if (at === -1)
218
+ return;
219
+ buf = buf.subarray(at + 1);
220
+ resyncing = false;
221
+ // The stream is back at a record start, but the FILE may not be: a
222
+ // terminator that failed AT the gap left an open fragment behind, and
223
+ // knowing where the stream resumed does nothing about it. Close it here,
224
+ // before any of these bytes can land on it — the two questions are
225
+ // different and only this one keeps the next whole record off the
226
+ // fragment. A no-op whenever the file already ends on a boundary.
227
+ terminate(fd);
228
+ // Still open: the fd will not take even one byte, so stay resynced (a
229
+ // failed terminate re-arms it) and drop this record too. A fabricated
230
+ // line is not detectable downstream; a missing one is.
231
+ if (resyncing)
232
+ return;
233
+ if (buf.length === 0)
234
+ return;
235
+ }
236
+ if (bytes + buf.length >= RUN_EVENT_LOG_MAX_BYTES) {
237
+ // The CROSSING chunk is refused whole — never half a record — but the
238
+ // log stays open. Returning early here for the rest of the run turned
239
+ // one oversized chunk into a 0-byte file; now every later chunk that
240
+ // still fits is still kept, and `truncated` is what says the file is no
241
+ // longer the whole stream. `>=` reserves the byte terminate() may add,
242
+ // so the finished file never exceeds the size the fold agrees to read.
243
+ truncated = true;
244
+ // The dropped bytes end mid-record unless the chunk's own last byte was
245
+ // a terminator; only then does the next chunk start a fresh line.
246
+ resyncing = buf[buf.length - 1] !== NEWLINE;
247
+ terminate(fd);
248
+ return;
249
+ }
250
+ const written = writeAll(fd, buf);
251
+ account(buf.subarray(0, written));
252
+ if (written === buf.length)
253
+ return;
254
+ // A short write: this record is on disk in part only and the stream
255
+ // continues from bytes that are gone. Same treatment as the cap — say the
256
+ // file is incomplete, close the half-written line, and skip to the next
257
+ // boundary instead of splicing the next chunk onto a fragment.
258
+ truncated = true;
259
+ // The lost bytes are `buf[written..]`, so the stream stands exactly where
260
+ // it does after a refusal: at `buf`'s last byte. Same rule as the cap,
261
+ // deliberately — hardcoding a resync here is safe in direction but throws
262
+ // away a whole record that begins at a record boundary, for nothing.
263
+ resyncing = buf[buf.length - 1] !== NEWLINE;
264
+ terminate(fd);
265
+ },
266
+ flush() {
267
+ if (fd === null || closed)
268
+ return;
269
+ terminate(fd);
270
+ },
271
+ close() {
272
+ if (fd !== null && !closed) {
273
+ try {
274
+ closeSync(fd);
275
+ }
276
+ catch (err) {
277
+ note(err);
278
+ }
279
+ }
280
+ closed = true;
281
+ return stats();
282
+ },
283
+ };
284
+ }
285
+ const EMPTY_FOLD = {
286
+ appended: 0,
287
+ alreadyFolded: false,
288
+ assistantTextFolded: false,
289
+ };
290
+ function isBlock(value) {
291
+ return typeof value === "object" && value !== null && !Array.isArray(value);
292
+ }
293
+ /**
294
+ * Turns one stream-json `assistant` event yields, in content-block order.
295
+ *
296
+ * Consecutive text blocks coalesce into one turn; every `tool_use` block gets
297
+ * its own turn (unlike the transcript mirror, which keeps only the first) so a
298
+ * message that fans out to three tools reads as three rows. Only completed
299
+ * `assistant` messages are folded: `stream_event` deltas repeat their text and
300
+ * the terminal `result` repeats the final message, so both would duplicate.
301
+ */
302
+ function foldAssistantEvent(event) {
303
+ const message = event.message;
304
+ if (!isBlock(message))
305
+ return [];
306
+ const content = message.content;
307
+ if (typeof content === "string")
308
+ return content === "" ? [] : [{ text: content }];
309
+ if (!Array.isArray(content))
310
+ return [];
311
+ const turns = [];
312
+ let text = "";
313
+ const flushText = () => {
314
+ if (text === "")
315
+ return;
316
+ turns.push({ text });
317
+ text = "";
318
+ };
319
+ for (const block of content) {
320
+ if (!isBlock(block))
321
+ continue;
322
+ if (block.type === "text" && typeof block.text === "string") {
323
+ text += block.text;
324
+ continue;
325
+ }
326
+ if (block.type === "tool_use" && typeof block.name === "string") {
327
+ flushText();
328
+ turns.push({ text: "", tool: { name: block.name } });
329
+ }
330
+ }
331
+ flushText();
332
+ return turns;
333
+ }
334
+ /** Every turn the log implies, in the order the child emitted them. */
335
+ function foldTurns(raw) {
336
+ const turns = [];
337
+ for (const line of raw.split("\n")) {
338
+ const text = (line.endsWith("\r") ? line.slice(0, -1) : line).trim();
339
+ if (text === "")
340
+ continue;
341
+ let value;
342
+ try {
343
+ value = JSON.parse(text);
344
+ }
345
+ catch {
346
+ continue; // unparsable lines stay in the log; they just fold to nothing
347
+ }
348
+ if (!isBlock(value) || value.type !== "assistant")
349
+ continue;
350
+ turns.push(...foldAssistantEvent(value));
351
+ }
352
+ return turns;
353
+ }
354
+ /**
355
+ * Folds a run's event log down into the session's transcript sidecar: assistant
356
+ * text plus one turn per `tool_use`, appended through `appendSessionTurn` in
357
+ * stream order, each tagged with the run id.
358
+ *
359
+ * Idempotence mechanism: the fold's OWN OUTPUT is the marker. Every appended
360
+ * turn carries `run: <runId>`, and a fold refuses as soon as the sidecar holds
361
+ * any turn tagged with that id. This was chosen over a separate marker (a
362
+ * `foldedAt` on the run record, a `.folded` sentinel file) precisely because a
363
+ * separate marker has a window: a crash between "turns appended" and "marker
364
+ * written" would re-fold and duplicate the whole run on the next settle. Here
365
+ * there is no window — the evidence and the mark are the same bytes. The cost
366
+ * is at-most-once rather than exactly-once under a crash MID-fold (the turns
367
+ * already appended are tagged, so the remainder is never completed), which is
368
+ * the safe direction: a short transcript, never a doubled one.
369
+ *
370
+ * Never throws — a fold that cannot read the log or write the sidecar reports
371
+ * an empty result and the settle continues.
372
+ */
373
+ export async function foldRunEventLog(projectDir, sessionId, runId) {
374
+ try {
375
+ const path = runEventLogPath(projectDir, sessionId, runId);
376
+ const info = await stat(path);
377
+ if (!info.isFile() || info.size > RUN_EVENT_LOG_MAX_BYTES)
378
+ return EMPTY_FOLD;
379
+ // The mark IS the data: any turn already tagged with this run id means a
380
+ // previous fold got there first.
381
+ const existing = await readSessionTurns(projectDir, sessionId);
382
+ const tagged = existing.filter((turn) => turn.run === runId);
383
+ if (tagged.length > 0) {
384
+ return {
385
+ appended: 0,
386
+ alreadyFolded: true,
387
+ assistantTextFolded: tagged.some((turn) => turn.type === "assistant" && turn.text !== ""),
388
+ };
389
+ }
390
+ const turns = foldTurns(await readFile(path, "utf-8"));
391
+ if (turns.length === 0)
392
+ return EMPTY_FOLD;
393
+ let appended = 0;
394
+ let assistantTextFolded = false;
395
+ for (const turn of turns) {
396
+ // Sequential on purpose: `appendSessionTurn` mints its negative id from
397
+ // what is already on disk, so concurrent appends would collide.
398
+ await appendSessionTurn(projectDir, sessionId, {
399
+ type: "assistant",
400
+ text: turn.text,
401
+ run: runId,
402
+ ...(turn.tool !== undefined && { tool: turn.tool }),
403
+ });
404
+ appended += 1;
405
+ if (turn.tool === undefined && turn.text !== "")
406
+ assistantTextFolded = true;
407
+ }
408
+ return { appended, alreadyFolded: false, assistantTextFolded };
409
+ }
410
+ catch {
411
+ // Missing log, unreadable sidecar, a full disk — all fold to nothing.
412
+ return EMPTY_FOLD;
413
+ }
414
+ }
415
+ // ---------------------------------------------------------------------------
416
+ // Retention
417
+ // ---------------------------------------------------------------------------
418
+ /**
419
+ * Keeps the newest `keep` event logs for one session and deletes the rest.
420
+ *
421
+ * Called at EVERY settle (after the fold), so the log that just settled is
422
+ * always among the survivors and the sessions dir is bounded at `keep` logs per
423
+ * session no matter how many runs a session accumulates. "Every settle" means
424
+ * the startup orphan sweep too (`settleOrphanedRuns`): a server that dies
425
+ * mid-run never reaches the route's write-back, so without that call site a
426
+ * session whose runs are always interrupted grows one capped log per run
427
+ * forever. `keep: 0` drops them all — what session deletion uses.
428
+ *
429
+ * Never throws; returns how many files it removed.
430
+ */
431
+ export async function pruneRunEventLogs(projectDir, sessionId, keep = RUN_EVENT_LOG_RETENTION) {
432
+ try {
433
+ const sessionsDir = join(projectDir, "sessions");
434
+ const prefix = `${normalizeIdentifier(sessionId)}.run-`;
435
+ const names = (await readdir(sessionsDir)).filter((name) => name.startsWith(prefix) &&
436
+ name.endsWith(LOG_SUFFIX) &&
437
+ name.length > prefix.length + LOG_SUFFIX.length);
438
+ if (names.length <= keep)
439
+ return 0;
440
+ const dated = await Promise.all(names.map(async (name) => {
441
+ try {
442
+ return { name, mtimeMs: (await stat(join(sessionsDir, name))).mtimeMs };
443
+ }
444
+ catch {
445
+ // Vanished under us — sort it to the front so it is dropped first.
446
+ return { name, mtimeMs: 0 };
447
+ }
448
+ }));
449
+ // Newest first; the name breaks mtime ties (same-millisecond runs) so the
450
+ // ordering is total and the prune is deterministic.
451
+ dated.sort((a, b) => b.mtimeMs - a.mtimeMs || (a.name < b.name ? 1 : -1));
452
+ let pruned = 0;
453
+ for (const entry of dated.slice(Math.max(keep, 0))) {
454
+ try {
455
+ await unlink(join(sessionsDir, entry.name));
456
+ pruned += 1;
457
+ }
458
+ catch {
459
+ // Already gone or locked — retention is best-effort.
460
+ }
461
+ }
462
+ return pruned;
463
+ }
464
+ catch {
465
+ return 0;
466
+ }
467
+ }
468
+ //# sourceMappingURL=run-event-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-event-log.js","sourceRoot":"","sources":["../../src/web-server/run-event-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Session liveness reconciliation — the world's opinion on top of the store's.
3
+ *
4
+ * `deriveSessionPhase` (session-store) answers "what does this record claim" from
5
+ * persisted evidence alone. This module answers "is that still true", using the
6
+ * two liveness sources that exist outside the DAG:
7
+ *
8
+ * - a run claim's pid, probed with signal 0. ARCS-driven runs are headless
9
+ * `claude -p` children ARCS spawned itself, so their liveness is a local
10
+ * process fact — and the persisted pid is what makes it readable from a
11
+ * server process that did not spawn them.
12
+ * - `claude agents --json`, which lists the live interactive/background
13
+ * sessions a terminal is driving. That is the only way to tell a session ARCS
14
+ * merely observes from one whose terminal has since been closed.
15
+ *
16
+ * Two shapes of work, deliberately separated:
17
+ *
18
+ * - `settleOrphanedRuns` WRITES: a claim whose process is gone settles as
19
+ * outcome `interrupted`, the claim is cleared and the session's run event
20
+ * logs are pruned. Runs once at startup (see app.ts), because a claim
21
+ * persisted by a previous process can never be live in this one unless its
22
+ * child outlived the restart.
23
+ * - `reconcileSessionPhases` READS: it returns demoted phases and never writes.
24
+ * On-demand only — there is no timer here, and no poller.
25
+ *
26
+ * Everything that touches the outside world is tolerant by construction: the
27
+ * probe is a subprocess of a binary that may not exist, running a command whose
28
+ * output shape is claude's to change. A missing binary, a non-zero exit, an
29
+ * empty stdout or unparsable JSON all degrade to the store-derived phase and
30
+ * NEVER throw — a reconciler that can fail would take the sessions UI down with
31
+ * every claude release.
32
+ */
33
+ import { type SessionMeta, type SessionPhase } from "../utils/session-store.js";
34
+ /**
35
+ * One live agent as reported by `claude agents --json` (verified on claude
36
+ * 2.1.223: live interactive and background sessions, `{pid, cwd, sessionId,
37
+ * name, state|status}`). Every field is optional because the wire shape belongs
38
+ * to claude — a release that renames one must degrade this module, not break it.
39
+ */
40
+ export interface LiveAgent {
41
+ pid?: number;
42
+ cwd?: string;
43
+ sessionId?: string;
44
+ name?: string;
45
+ /** `status`, falling back to `state` — reported, never branched on. */
46
+ status?: string;
47
+ }
48
+ /** Raw probe result: `ok:false` covers missing binary, non-zero exit, timeout. */
49
+ export type AgentsProbeResult = {
50
+ ok: true;
51
+ stdout: string;
52
+ } | {
53
+ ok: false;
54
+ };
55
+ export type AgentsProbe = () => Promise<AgentsProbeResult>;
56
+ /**
57
+ * Parses `claude agents --json` stdout into live agents.
58
+ *
59
+ * `null` means "the probe answered nothing usable" (empty output, non-JSON, a
60
+ * JSON shape carrying no array) and is NOT the same as `[]`, which means "the
61
+ * probe worked and no agent is live". Only the second may demote a session:
62
+ * conflating them would mark every observed session idle the day claude changes
63
+ * its output.
64
+ */
65
+ export declare function parseLiveAgents(stdout: string): LiveAgent[] | null;
66
+ /**
67
+ * Runs the probe and parses it. `null` on every degraded path — missing binary,
68
+ * non-zero exit, timeout, unparsable output, or a probe that threw.
69
+ */
70
+ export declare function listLiveClaudeAgents(options?: {
71
+ probe?: AgentsProbe;
72
+ }): Promise<LiveAgent[] | null>;
73
+ /**
74
+ * Whether a pid is a live process, via signal 0 (no signal delivered).
75
+ *
76
+ * SHORTCUT: pids are reused, so a recycled pid reads alive and keeps an orphan
77
+ * claim standing; upgrade to a start-time check (`/proc/<pid>/stat` field 22 or
78
+ * `ps -o lstart`) when a stuck claim is ever actually observed.
79
+ */
80
+ export declare function isProcessAlive(pid: number): boolean;
81
+ export interface ReconcileOptions {
82
+ /** Injectable `claude agents --json` probe (tests). */
83
+ probe?: AgentsProbe;
84
+ /** Injectable pid liveness (tests). */
85
+ isAlive?: (pid: number) => boolean;
86
+ /** Clock in epoch ms; defaults to `Date.now()`. */
87
+ now?: number;
88
+ }
89
+ export interface SessionPhaseView {
90
+ sessionId: string;
91
+ phase: SessionPhase;
92
+ }
93
+ /**
94
+ * The store-derived phase, demoted when the world disagrees.
95
+ *
96
+ * Demotion is one-way: this can turn `running` into `idle`, never the reverse.
97
+ * Nothing outside the record may promote a session — the DAG stays the source
98
+ * of truth and the probe is only ever allowed to take evidence away.
99
+ *
100
+ * Which evidence applies depends on what the record claims, not on its origin:
101
+ * - a run claim is checked against its pid. `claude agents` never lists a
102
+ * headless `claude -p` child, so asking it about an ARCS run would demote
103
+ * every healthy run.
104
+ * - everything else is checked against the agent list. `agents === null` (the
105
+ * probe degraded) leaves the `lastCheckpointAt`-derived phase untouched.
106
+ */
107
+ export declare function reconcilePhase(session: SessionMeta, agents: LiveAgent[] | null, options?: ReconcileOptions): SessionPhase;
108
+ /**
109
+ * Reconciled phase for every session in a project. Read-only: no writes, no
110
+ * timer, and at most ONE `claude agents --json` for the whole index — never one
111
+ * per session.
112
+ *
113
+ * The probe is LAZY: `claude agents --json` costs ~0.35s of wall clock and a
114
+ * whole subprocess, and its answer is read at a single branch of
115
+ * `reconcilePhase`. Every terminal record, every idle one and every ARCS run
116
+ * holding a claim decides without it, so a probe fired before the phases are
117
+ * derived is a subprocess whose result is thrown away — on EVERY sessions
118
+ * request, list and detail alike, at whatever rate the watcher invalidates the
119
+ * query. So the derivation runs first and the probe only follows when some
120
+ * record actually reaches that branch.
121
+ *
122
+ * Skipping it passes `null` — the same value a degraded probe yields, and the
123
+ * value no session left in this pass can read: reaching the `agents` branch is
124
+ * precisely the condition that would have run the probe.
125
+ */
126
+ export declare function reconcileSessionPhases(projectDir: string, options?: ReconcileOptions): Promise<SessionPhaseView[]>;
127
+ export interface SettledOrphan {
128
+ sessionId: string;
129
+ runId: string;
130
+ pid?: number;
131
+ }
132
+ /**
133
+ * Settles every run claim in a project whose process is gone.
134
+ *
135
+ * A claim only survives when its pid is still alive — a run spawned by a
136
+ * previous server process either outlived the restart (leave it be; it still
137
+ * holds the write-target) or died with it, and a dead claim that is never
138
+ * cleared is a session stuck on `running` for good. The settled run keeps
139
+ * whatever `metadata.run` already held and gains `outcome: "interrupted"`,
140
+ * which no live run can ever produce.
141
+ *
142
+ * A settle here also prunes the session's run event logs, exactly as the route's
143
+ * own write-back does — this is the ONLY settle an interrupted run ever gets, so
144
+ * it is the only place that can bound the logs of a session whose runs never
145
+ * settle normally.
146
+ *
147
+ * Never throws: a project whose index cannot be read is skipped, and a record
148
+ * deleted mid-sweep is simply not settled.
149
+ */
150
+ export declare function settleOrphanedRuns(projectDir: string, options?: ReconcileOptions): Promise<SettledOrphan[]>;
151
+ /**
152
+ * Startup sweep across every registered project.
153
+ *
154
+ * Composition-time work (app.ts calls it once, fire-and-forget) but the logic
155
+ * lives here. One pass, no timer: liveness questions after startup are answered
156
+ * on demand by `reconcileSessionPhases`.
157
+ *
158
+ * Never throws — a data dir without a readable meta.json settles nothing and
159
+ * reports it, rather than taking the server down at boot.
160
+ */
161
+ export declare function settleOrphanedRunsOnStartup(dataDir?: string, options?: ReconcileOptions): Promise<SettledOrphan[]>;
162
+ //# sourceMappingURL=session-reconciler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-reconciler.d.ts","sourceRoot":"","sources":["../../src/web-server/session-reconciler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,YAAY,EAGlB,MAAM,2BAA2B,CAAC;AAQnC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC;AAC7E,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAoF3D;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CAiBlE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,WAAW,CAAA;CAAO,GACpC,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CAU7B;AAMD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CASnD;AAMD,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,CAAC;CACrB;AAcD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAC1B,OAAO,GAAE,gBAAqB,GAC7B,YAAY,CAad;AAeD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAa7B;AAMD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAQD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,aAAa,EAAE,CAAC,CAqC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,GAAE,MAAqB,EAC9B,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,aAAa,EAAE,CAAC,CAa1B"}