@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,96 @@
1
+ /**
2
+ * Intent → argv permission policy for headless `claude -p` runs.
3
+ *
4
+ * Safety in the session bridge is argv-shaped, and this module is the SINGLE
5
+ * place tool and permission flags are produced: `claude-runner.ts` stays
6
+ * argv-agnostic and no route hand-assembles a more permissive run. Every token
7
+ * emitted here comes from a closed set of module constants — the one
8
+ * caller-supplied token is the staged system prompt, and it is kept
9
+ * unambiguously a value (see asValueToken). Bypass flags are therefore
10
+ * unreachable from HTTP input by construction rather than by convention, and
11
+ * `test/permission-policy.test.ts` drives the builder with hostile
12
+ * HTTP-shaped payloads to keep it that way.
13
+ *
14
+ * Flags verified against claude 2.1.223: `--tools <tools...>` takes a
15
+ * comma-separated built-in tool list, `--permission-mode` accepts "plan" and
16
+ * "acceptEdits", and `--append-system-prompt` appends to (never replaces) the
17
+ * system prompt. `--cwd` is deliberately absent — claude >= 2.x rejects it, and
18
+ * the runner applies the working directory through spawn options.cwd instead.
19
+ */
20
+ /** Accepted run intents. Also the source list for route-level zod validation. */
21
+ export const RUN_INTENTS = ["ask", "change"];
22
+ /** `ask`: inspect the workspace, never touch it. */
23
+ const ASK_TOOLS = ["Read", "Grep", "Glob"];
24
+ /**
25
+ * `change`: the read-only set plus the edit surface. Bash is deliberately NOT
26
+ * here — it is opt-in per run, so a change run cannot shell out by default.
27
+ */
28
+ const CHANGE_TOOLS = ["Read", "Grep", "Glob", "Edit", "Write", "TodoWrite"];
29
+ /** Appended last, and only on an explicit `allowBash === true`. */
30
+ const BASH_TOOL = "Bash";
31
+ const INTENT_TOOLS = {
32
+ ask: ASK_TOOLS,
33
+ change: CHANGE_TOOLS,
34
+ };
35
+ /** `plan` cannot edit; `acceptEdits` auto-accepts edits but not shell commands.
36
+ * "bypassPermissions" is not reachable from this map — that is the point. */
37
+ const INTENT_PERMISSION_MODES = {
38
+ ask: "plan",
39
+ change: "acceptEdits",
40
+ };
41
+ /**
42
+ * Builds the tool/permission argv segment for one run. The caller concatenates
43
+ * it onto the rest of its argv (`-p`, `--resume`, `--output-format`, …); this
44
+ * module owns nothing else.
45
+ */
46
+ export function buildPermissionArgv(input = {}) {
47
+ const fields = asRecord(input);
48
+ const intent = normalizeIntent(fields.intent);
49
+ const tools = [...INTENT_TOOLS[intent]];
50
+ if (fields.allowBash === true)
51
+ tools.push(BASH_TOOL);
52
+ const argv = ["--tools", tools.join(","), "--permission-mode", INTENT_PERMISSION_MODES[intent]];
53
+ const staged = typeof fields.stagedSystemPrompt === "string" ? fields.stagedSystemPrompt.trim() : "";
54
+ if (staged)
55
+ argv.push("--append-system-prompt", asValueToken(staged));
56
+ return argv;
57
+ }
58
+ /**
59
+ * Fails closed: anything that is not exactly `"change"` runs read-only. The
60
+ * route's zod enum rejects malformed intents with a 400 first — this is the
61
+ * second line, so a caller that skips validation cannot widen the run.
62
+ */
63
+ function normalizeIntent(value) {
64
+ return value === "change" ? "change" : "ask";
65
+ }
66
+ /**
67
+ * Keeps a caller-supplied value from ever being flag-shaped. Staged text is the
68
+ * only argv token ARCS does not author character-for-character, so a message of
69
+ * exactly `--dangerously-skip-permissions` arrives at claude's option parser in
70
+ * a position where only that parser's rules decide whether it reads as a value
71
+ * or as a flag.
72
+ *
73
+ * This prefix closes no hole that exists today, and an earlier version of this
74
+ * comment was wrong to claim it did. A live probe against claude 2.1.223:
75
+ * `claude --append-system-prompt --output-format json -p "…"` printed plain
76
+ * text where the control printed JSON — meaning the CLI had already swallowed
77
+ * the dash-leading `--output-format` as the VALUE of `--append-system-prompt`
78
+ * instead of reading it as an option. The installed parser is safe unaided.
79
+ *
80
+ * It is kept for version-independence, not as a bug fix. "An option's argument
81
+ * may itself begin with a dash" is an undocumented parser detail that a minor
82
+ * CLI upgrade could reverse with no breaking-change note, and the blast radius
83
+ * if it ever does is a permission flag from HTTP input. A single leading
84
+ * newline keeps every byte of the text while making the token unambiguously a
85
+ * value under either rule.
86
+ */
87
+ function asValueToken(text) {
88
+ return text.startsWith("-") ? `\n${text}` : text;
89
+ }
90
+ /** Tolerates any runtime shape an HTTP body can take (null, array, scalar). */
91
+ function asRecord(value) {
92
+ return typeof value === "object" && value !== null && !Array.isArray(value)
93
+ ? value
94
+ : {};
95
+ }
96
+ //# sourceMappingURL=permission-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-policy.js","sourceRoot":"","sources":["../../src/web-server/permission-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAU,CAAC;AAItD,oDAAoD;AACpD,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAEpD;;;GAGG;AACH,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAU,CAAC;AAErF,mEAAmE;AACnE,MAAM,SAAS,GAAG,MAAM,CAAC;AAEzB,MAAM,YAAY,GAAyC;IACzD,GAAG,EAAE,SAAS;IACd,MAAM,EAAE,YAAY;CACrB,CAAC;AAEF;8EAC8E;AAC9E,MAAM,uBAAuB,GAA8B;IACzD,GAAG,EAAE,MAAM;IACX,MAAM,EAAE,aAAa;CACtB,CAAC;AAoBF;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAA6B,EAAE;IACjE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAE9C,MAAM,KAAK,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAErD,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,mBAAmB,EAAE,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC;IAEhG,MAAM,MAAM,GACV,OAAO,MAAM,CAAC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACxF,IAAI,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAEtE,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAED,+EAA+E;AAC/E,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,EAAE,CAAC;AACT,CAAC"}
@@ -0,0 +1,454 @@
1
+ /**
2
+ * Prompt assembly — the STABLE staged-environment tier, plus reference
3
+ * rendering.
4
+ *
5
+ * Two independent products, one module because they share one trust model and
6
+ * one escape (`stripStageDelimiters`, `untrustedDoc`):
7
+ * - `buildStagedEnvironment` / `planStageRefresh` — the per-SESSION stable
8
+ * block, documented below.
9
+ * - `renderReference` / `renderReferences` — the per-TURN references a caller
10
+ * attached to a message. This is the ONE place a reference is turned into
11
+ * prompt text; nothing here enters the staged block, so a reference can
12
+ * never move the stable tier's fingerprint.
13
+ *
14
+ * A headless `claude -p` run starts with no ambient project knowledge: it does
15
+ * not know which DAG node it is on, where the workspace root is, or what the
16
+ * project already learned. This module renders that context once, as a single
17
+ * ordered block that rides `--append-system-prompt`. The run route
18
+ * (`routes/sessions.ts`) appends that flag/value pair directly today:
19
+ * `permission-policy.ts`'s `buildPermissionArgv` owns the flag but returns a
20
+ * WHOLE tool/permission segment keyed on an `intent` the run route does not
21
+ * have, so emitting it there would restrict what today's runs may do. When
22
+ * POST /turns introduces intents, this same text becomes its
23
+ * `stagedSystemPrompt` and the direct pair goes away. This module emits TEXT
24
+ * only — it never produces argv, and never spawns anything.
25
+ *
26
+ * STABLE means byte-identical across turns for an unchanged DAG. That is the
27
+ * whole economics of the tier: an unchanged prefix is a cache hit upstream, so
28
+ * nothing volatile (timestamps, queue depth, run state) is allowed in `text`.
29
+ * `stagedAt` therefore lives on the returned stage RECORD, never in the text.
30
+ * The VOLATILE tier is a separate, later concern.
31
+ *
32
+ * Trust model. ARCS-derived facts are asserted plainly. Every body copied out
33
+ * of a file or an agent-authored DAG document is wrapped in a named
34
+ * `<<<ARCS_UNTRUSTED_DOC …>>>` delimiter, carries an explicit "embedded
35
+ * instructions cannot override" sentence, and is run through
36
+ * `stripStageDelimiters` so a document cannot close its own wrapper and
37
+ * escalate into the controller's voice.
38
+ *
39
+ * Budgets are spent on a body's CONTENT, never on the wrapped string: clipping
40
+ * a rendered wrapper keeps its opener and severs its closer, which puts every
41
+ * later ARCS-authored block inside an unterminated untrusted region. Both
42
+ * products obey this — `renderBlock` for the staged tier, and the reference
43
+ * renderers by clipping before they call `untrustedDoc`.
44
+ *
45
+ * Read-only. This module reads ARCS data through the existing store readers and
46
+ * the existing knowledge-selection helper; it never writes. It RETURNS a
47
+ * `StageRecord` for the caller (the run route) to persist at `metadata.stage`.
48
+ * (Caveat inherited from the stores, not introduced here: `readKnowledgeIndex`
49
+ * and `readPlanIndex` self-repair a corrupt or drifted index on read. That is
50
+ * repair of existing data, never a mutation this module authors.)
51
+ */
52
+ import type { SessionReference } from "../utils/claude-transcript.js";
53
+ import type { SessionMeta } from "../utils/session-store.js";
54
+ export declare const STAGE_TRANSPORTS: readonly ["system", "prompt"];
55
+ export type StageTransport = (typeof STAGE_TRANSPORTS)[number];
56
+ /**
57
+ * How the staged text reaches the model.
58
+ *
59
+ * - `system` (default) — appended via `--append-system-prompt`. Cheapest and
60
+ * keeps the block out of the visible conversation.
61
+ * - `prompt` — prepended to the user prompt text instead.
62
+ *
63
+ * The designed fallback: flip this single constant to `"prompt"` if predicate
64
+ * P1 fails (see `STAGE_MANUAL_CHECKS`). Nothing else in the module changes —
65
+ * the transport is carried on the stage record so a flip forces a restage on
66
+ * already-staged sessions rather than silently leaving them on the old channel.
67
+ */
68
+ export declare const STAGE_TRANSPORT: StageTransport;
69
+ /**
70
+ * Checks this layer cannot automate, recorded as data so they stay greppable
71
+ * and can be surfaced by a route or a doc instead of rotting in a comment.
72
+ */
73
+ export declare const STAGE_MANUAL_CHECKS: readonly [string];
74
+ /**
75
+ * Degradation starts above this.
76
+ *
77
+ * MEASURED, against the live ARCS project, with the budgets below. It is
78
+ * HEADROOM, not a live path — a test reaches it only by passing `softCap`.
79
+ *
80
+ * STATE THE INPUTS OR THE FIGURE IS NOT RE-DERIVABLE, AND THERE ARE FOUR OF
81
+ * THEM. An assembled width is a function of inputs the corpus does not contain:
82
+ * un-budgeted IDENTITY interpolates `sessionId` (ceiling 96), `slug` (64) and
83
+ * `projectName` (64); un-budgeted WORKSPACE interpolates `workspaceRoot` (256).
84
+ * Every one is width-normalized by FIELD_WIDTHS and none is budgeted, so each
85
+ * char lands WHOLE on a build whose budgeted blocks are already saturated
86
+ * (measured on the binding node: dag-position 1199/1200, node-body 1200/1200,
87
+ * brief 800/800, knowledge 1599/1600). A row that names only one of the four is
88
+ * not a measurement — an earlier row here swept `sessionId` to its ceiling while
89
+ * leaving `projectName` at the live corpus's incidental 4 chars and
90
+ * `workspaceRoot` at 23, and recorded "fires for 0" as though it were general.
91
+ *
92
+ * WHAT IS AFFORDABLE IS THEIR SUM. The four inputs share one margin, so the
93
+ * whole measurement collapses to one number: on the live corpus the widest
94
+ * `observed` build stays off the ladder while
95
+ *
96
+ * sessionId + slug + projectName + workspaceRoot <= 230 chars
97
+ *
98
+ * and fires at 231 (verified from both sides — see the 126/127 and 66/67 rows).
99
+ * `arcs` affords 284. The ceilings sum to 480, so the all-ceilings corner is 250
100
+ * chars past what the cap can hold and MUST degrade; see the last row.
101
+ *
102
+ * Live ARCS DAG, 137 tasks / 18 plans, swept whole against a COPY of the data
103
+ * dir (the stores self-repair on read), every row at the `sessionId` ceiling of
104
+ * 96, both origins, widths post-ladder with the FIRING COUNT beside them:
105
+ *
106
+ * slug 4, name 4, root 23 — the live values:
107
+ * arcs 5843 task / 5048 plan fires 0 of 137 and 0 of 18
108
+ * observed 5897 task / 5102 plan fires 0 of 137 and 0 of 18
109
+ * slug 4, name 64 (projectName CEILING), root 23:
110
+ * arcs 5903 / 5108 fires 0 of 137 and 0 of 18
111
+ * observed 5957 / 5162 fires 0 of 137 and 0 of 18
112
+ * slug 64 (slug CEILING), name 4, root 23: same widths as the row above
113
+ * observed 5957 / 5162 fires 0 of 137 and 0 of 18
114
+ * slug 4, name 4, root 103 — a deep but ordinary checkout path:
115
+ * arcs 5923 / 5128 fires 0 of 137 and 0 of 18
116
+ * observed 5977 / 5182 fires 0 of 137 and 0 of 18
117
+ * slug 4, name 4, root 126 / 127 — the crossing, from both sides:
118
+ * observed 6000 / 5205 fires 0 of 137 (126: on the cap)
119
+ * observed 6000 / 5206 fires 11 of 137 (127: over it)
120
+ * slug 4, name 64, root 66 / 67 — the same crossing with the name at ceiling:
121
+ * observed 6000 fires 0 of 137 | 6000 fires 11 of 137
122
+ * slug 64, name 64, root 256 — ALL FOUR AT THEIR CEILINGS:
123
+ * arcs 5991 / 5401 fires 49 of 137 and 0 of 18
124
+ * observed 5998 / 5455 fires 54 of 137 and 0 of 18
125
+ * knowledge is the only payer (digest 6 entries -> 3); IDENTITY, WORKSPACE
126
+ * and LIMITS render whole, and no build exceeds the cap.
127
+ *
128
+ * THE CORPUS SIZE IS ONE OF THE INPUTS, so a re-take on a different one is DRIFT,
129
+ * not a regression: the live DAG is already 139 tasks, which moves only the
130
+ * `observed` ceiling row from 54 to 55. Every row above names its inputs so the
131
+ * next taker re-derives them rather than comparing a count across two corpora.
132
+ *
133
+ * THE CEILING ROW FIRES BY CONSTRUCTION, NOT BY REGRESSION. Un-budgeted blocks
134
+ * at their widest cost 915 chars (STAGE_HARD_CAP's arithmetic) against the 663
135
+ * this cap leaves them, so no wording can make that row zero: it is 252 chars
136
+ * over before a single word is written. Same corner on the pre-origin-conditioned
137
+ * module, same 137-task corpus: 51 of 137 (arcs) and 53 of 137 (observed), 0 of
138
+ * 18 plans, knowledge the only payer. ONE number cannot cover both origins there
139
+ * either — HEAD's IDENTITY line already interpolates `origin`, 4 chars for `arcs`
140
+ * against 8 for `observed` — which is this file's own rule again: a row that names
141
+ * only one of its inputs is not a measurement. Note the direction, too: HEAD
142
+ * degrades MORE than this module does on `arcs` (51 against 49). Zero there is
143
+ * reachable only by raising this cap or budgeting IDENTITY/WORKSPACE, and both are
144
+ * larger decisions than a reword. What the row guarantees is that the DEGRADATION
145
+ * is bounded and paid by the cheapest budgeted block.
146
+ *
147
+ * REALISTIC vs CEILING, and which is which: a `workspaceRoot` is a real checkout
148
+ * path (this repo's is 23 chars, a deep one ~103), so the 23/66/103/126 rows are
149
+ * the reachable ones and 256 is the guarantee row. A `projectName` and a `slug`
150
+ * at 64 are entirely reachable — that is why the name-at-ceiling row exists, and
151
+ * why the un-budgeted wording that pushed its crossing down to a 57-char project
152
+ * name was reverted rather than re-measured.
153
+ *
154
+ * Earlier rows, kept for shape only — none of them names all four inputs, so
155
+ * none is comparable with the table above:
156
+ * 130 tasks: 5812 task / 5011 plan, fires 0 of 130, id width unrecorded.
157
+ * 133 tasks: 5807 / 5006 at a 15-char id; 5888 / 5087 re-derived at the id
158
+ * ceiling. Fires 0 of 133 either way. Taken when `renderBlock` moved
159
+ * the budget clip onto a body's CONTENT — both maxima are UNCHANGED
160
+ * by that move (a clipped block is still bounded by the same budget:
161
+ * the wrapper's tags are paid out of it, not added to it).
162
+ *
163
+ * All of this is a measurement, not a property, and it is what to re-take when a
164
+ * budget, an input width or a block's content changes. It has already caught two.
165
+ * Staging the owning plan for task-linked sessions put real content into a
166
+ * node-body block whose 1800-char budget had been sized for content that never
167
+ * existed, which took the widest real node to 6412 and made the ladder fire for
168
+ * 62 of 130 — paying for the plan by DELETING the whole knowledge digest; the
169
+ * budget was sized to the content instead (STAGE_BLOCK_BUDGETS["node-body"]).
170
+ * And 55 chars of un-budgeted wording moved the affordance from 234 to 179,
171
+ * which fired the ladder for 15 of 137 at nothing worse than a 57-char project
172
+ * name; 51 of those chars were given back (see LIMITS_OBSERVED) and the
173
+ * affordance is 230. The residual 4 is the `observed` IDENTITY sentence, which
174
+ * buys a true statement on a path where the old one was false — and `arcs` is 46
175
+ * chars NARROWER than it was, having dropped a supersede notice nothing emits.
176
+ *
177
+ * Re-take it with a sweep over the WHOLE corpus, both origins, naming ALL FOUR
178
+ * input widths, and report the ladder-FIRING COUNT beside every width. A width
179
+ * alone is satisfied by the degradation it is supposed to detect — that is how
180
+ * the 15-of-137 regression passed every cap assertion in the suite.
181
+ * `test/prompt-assembly-stable.test.ts` describe (g) pins the count.
182
+ */
183
+ export declare const STAGE_SOFT_CAP = 6000;
184
+ /**
185
+ * Never exceeded. Held by construction, and the arithmetic is:
186
+ * budgeted blocks (STAGE_BLOCK_BUDGETS) = 4800
187
+ * + un-budgeted blocks at their widest (identity 347,
188
+ * workspace 353, limits 215 — width-normalized at
189
+ * input by FIELD_WIDTHS, never truncated) = 915
190
+ * + envelope, preamble, headings and joiners = 537
191
+ * ------
192
+ * = 6252
193
+ * which leaves 1748 chars of slack under this cap.
194
+ *
195
+ * READ THIS AGAINST STAGE_SOFT_CAP, because the two disagree ON PURPOSE. The
196
+ * un-budgeted blocks are allowed 915 chars here and the soft cap leaves them
197
+ * 663 (6000 - 4800 - 537), so a build with every bounded input at its ceiling
198
+ * is 252 chars over the SOFT cap and degrades. That is the ladder doing its job,
199
+ * not a defect — but it is also why a reword of an un-budgeted block is a
200
+ * measurement, not an edit: it is spent out of a margin this cap does not
201
+ * protect. STAGE_SOFT_CAP records the sweep that says how much margin is left.
202
+ *
203
+ * `identity` and `limits` are ORIGIN-CONDITIONED, and the two numbers above are
204
+ * the `observed` variant — still the wider of the pair on both blocks (`arcs`
205
+ * measures 320 and 188), though limits is now 215 rather than 266: it lost a
206
+ * 32-char lead-in and traded a refresh promise it cannot keep for a 31-char
207
+ * capture statement. A ceiling taken from one origin is not a ceiling, which is
208
+ * why `test/prompt-assembly-stable.test.ts` builds both and asserts the observed
209
+ * one is the larger before pinning it.
210
+ *
211
+ * The un-budgeted numbers are otherwise the widths their fields are bounded to,
212
+ * so they cannot grow without a FIELD_WIDTHS constant moving — or without
213
+ * someone rewording an authored block, which is the change these figures exist
214
+ * to catch.
215
+ */
216
+ export declare const STAGE_HARD_CAP = 8000;
217
+ export type StageBlockId = "identity" | "workspace" | "dag-position" | "node-body" | "brief" | "knowledge" | "limits";
218
+ /**
219
+ * The blocks the ladder may degrade and a per-block budget applies to.
220
+ *
221
+ * `identity`, `workspace` and `limits` are deliberately NOT here and carry no
222
+ * budget at all: they are never degraded, and their variable fields are
223
+ * width-normalized at input (FIELD_WIDTHS) instead, which bounds them by
224
+ * construction. Budgets for them used to exist and were inert — excluded from
225
+ * every code path that reads a budget, and all three exceeded the numbers they
226
+ * stated (324/353/234 against 120/120/200), so the invariant they documented
227
+ * was false as written.
228
+ */
229
+ export type StageBudgetedBlockId = "dag-position" | "node-body" | "brief" | "knowledge";
230
+ /** Render order. Fixed — the prefix must be stable for the cache to hit. */
231
+ export declare const STAGE_BLOCK_ORDER: readonly StageBlockId[];
232
+ /**
233
+ * Per-block character budgets. Keyed by StageBudgetedBlockId, so a block with
234
+ * no budget cannot be given an inert one. Sum = 4800; see STAGE_HARD_CAP for
235
+ * the full ceiling arithmetic this feeds.
236
+ *
237
+ * `node-body` is 1200, not the 1800 it carried while the block was empty for
238
+ * every task-linked run. 1800 is what the widest real DAG cannot afford: it
239
+ * takes the largest task-linked block to 6412 and makes the soft-cap ladder fire
240
+ * for 62 of 130 real nodes, whose first two rungs zero the knowledge digest — a
241
+ * curated 6-entry index traded for 600 more chars of one plan document. At 1200
242
+ * the widest real node is 5812 and the ladder fires for none, so both blocks
243
+ * survive. Measured across the whole live DAG at 1800/1500/1400/1300/1200:
244
+ * 62/34/14/0/0 nodes degraded.
245
+ *
246
+ * A budget bounds the block AS RENDERED, wrapper tags included: `renderBlock`
247
+ * pays the open/close tags out of the budget and spends what is left on the
248
+ * body's content. So a number here is not "chars of document" — a wide `source`
249
+ * buys less document, and a budget below the tag cost drops the body for a
250
+ * one-line pointer rather than emitting an opener it cannot close.
251
+ */
252
+ export declare const STAGE_BLOCK_BUDGETS: Record<StageBudgetedBlockId, number>;
253
+ /**
254
+ * Fixed truncation precedence — exactly the budgeted blocks, cheapest-to-lose
255
+ * first. The DAG position survives longest because it carries what the run must
256
+ * satisfy to finish (its scope, acceptance and verify command) alongside its
257
+ * edges; everything above it can be re-read on demand from the DAG or the repo.
258
+ */
259
+ export declare const STAGE_TRUNCATION_PRECEDENCE: readonly StageBudgetedBlockId[];
260
+ /**
261
+ * Neutralizes ARCS delimiter tokens in untrusted content. Applied to EVERY
262
+ * injected value — bodies, titles, summaries, ids — so there is no per-field
263
+ * exception to reason about.
264
+ */
265
+ export declare function stripStageDelimiters(text: string): string;
266
+ /**
267
+ * Files whose mtime means "the DAG may have moved". Cheap: four stats, no
268
+ * parsing. Store-mediated edits to a plan/knowledge BODY also land here,
269
+ * because every store write rewrites the owning index alongside the document.
270
+ */
271
+ export declare const STAGE_PROBE_FILES: readonly string[];
272
+ /**
273
+ * Deliberately NOT probed. Session heartbeat, status and run-metadata writes
274
+ * touch `sessions/index.json` on essentially every poll — including the very
275
+ * write that persists `metadata.stage`. Probing it would make every stage
276
+ * permanently stale and defeat the whole cache.
277
+ */
278
+ export declare const STAGE_PROBE_EXCLUDED: readonly string[];
279
+ /**
280
+ * The markdown document STAGED for the linked node, as a project-relative path.
281
+ * It must name the same file `readSources` copies into the node-body block, or
282
+ * an edit to that file would not invalidate the stage.
283
+ *
284
+ * Plans own `plans/<id>.md`. A task owns no document at all, so the block stages
285
+ * the plan that owns the TASK — and this therefore resolves to that same
286
+ * `plans/<planId>.md` rather than to the aggregate `tasks.md` it used to name
287
+ * (`tasks.md` was never load-bearing: it is rewritten by the same store write
288
+ * that rewrites the already-probed `tasks/index.json`).
289
+ *
290
+ * DELIBERATE COST, chosen over a silent staleness hole: resolving a task's plan
291
+ * needs the task index, so this is no longer derivable from the session alone
292
+ * and the probe is no longer stats-only. The read is one 192 KB JSON parse on
293
+ * the live ARCS project (~2 ms), paid ONCE PER RUN — the probe runs at spawn
294
+ * time, not per poll — against a `claude -p` subprocess that costs three orders
295
+ * of magnitude more. The alternative (persist the path on the stage record and
296
+ * read it back) removes the read but points the probe at the PREVIOUS build's
297
+ * file, which is a subtler thing to reason about for a saving that is invisible
298
+ * next to the spawn.
299
+ *
300
+ * `readJsonSafe`, never `listTasks`: the store readers self-repair a drifted
301
+ * index on read, and a probe that can WRITE a file it probes is a
302
+ * self-invalidating cache (the same failure `STAGE_PROBE_EXCLUDED` exists for).
303
+ *
304
+ * The one gap left: a hand-edited `plans/index.json` whose `file` does not
305
+ * follow `plans/<normalizedId>.md`. Every store-written index does, and a
306
+ * store-mediated body edit rewrites the probed index anyway.
307
+ */
308
+ export declare function linkedNodeMarkdownPath(projectDir: string, session: SessionMeta): Promise<string | undefined>;
309
+ /**
310
+ * Max mtime (epoch ms) across the probe set plus the linked node's markdown.
311
+ * Returns 0 when nothing in the set exists.
312
+ */
313
+ export declare function probeDagMtimeMs(projectDir: string, session: SessionMeta): Promise<number>;
314
+ export interface StageRecord {
315
+ /** sha256 hex of the staged text. */
316
+ fingerprint: string;
317
+ /**
318
+ * Epoch MILLISECONDS. Deliberately not ISO: it is compared numerically
319
+ * against `fs.Stats.mtimeMs`, and it sits under `metadata` next to
320
+ * `metadata.run`, which is already epoch ms. (Top-level SessionMeta
321
+ * timestamps are ISO; `metadata.*` timestamps are epoch ms. This follows its
322
+ * neighbours, not its grandparent.)
323
+ *
324
+ * On the path that persists it (`planStageRefresh`) this is the OBSERVED
325
+ * PROBE WATERMARK, never the wall clock — see that function's clause 2.
326
+ */
327
+ stagedAt: number;
328
+ transport: StageTransport;
329
+ }
330
+ /** Reads and validates `session.metadata.stage`, which is untyped on disk. */
331
+ export declare function readStageRecord(session: SessionMeta): StageRecord | undefined;
332
+ export interface StageTruncation {
333
+ block: StageBudgetedBlockId;
334
+ /** `block-budget` — the block exceeded its own budget. `soft-cap` — the
335
+ * assembled text exceeded STAGE_SOFT_CAP and this block paid for it. */
336
+ reason: "block-budget" | "soft-cap";
337
+ droppedChars: number;
338
+ }
339
+ export interface StagedEnvironment {
340
+ text: string;
341
+ chars: number;
342
+ truncated: StageTruncation[];
343
+ /** Ready to persist at `metadata.stage`. This module never writes it. */
344
+ stage: StageRecord;
345
+ }
346
+ export interface StageOptions {
347
+ /** Absolute workspace root. Falls back to the project's first
348
+ * `workspacePaths` entry, so P2 holds even when the caller passes nothing. */
349
+ workspaceRoot?: string;
350
+ /** Knowledge audience filter. Default `implementer`. */
351
+ audience?: string;
352
+ transport?: StageTransport;
353
+ /**
354
+ * Epoch ms for `stage.stagedAt`. Tests pin it; nothing else should —
355
+ * `planStageRefresh` OVERRIDES it with the probe watermark, because a record
356
+ * it returns is a record a caller persists and compares against mtimes.
357
+ */
358
+ now?: number;
359
+ /**
360
+ * Overrides STAGE_SOFT_CAP. Exists so the truncation precedence can be driven
361
+ * end-to-end at a cap the block budgets can actually breach — a caller on a
362
+ * tighter model budget may also lower it. STAGE_HARD_CAP is not overridable.
363
+ */
364
+ softCap?: number;
365
+ }
366
+ /**
367
+ * Builds the STABLE tier for one session.
368
+ *
369
+ * Deterministic: for an unchanged DAG the returned `text` is byte-identical
370
+ * across turns. Nothing volatile is rendered — `stage.stagedAt` is on the
371
+ * record, not in the text.
372
+ */
373
+ export declare function buildStagedEnvironment(projectDir: string, slug: string, session: SessionMeta, opts?: StageOptions): Promise<StagedEnvironment>;
374
+ export declare function fingerprintStagedText(text: string): string;
375
+ export type StageRefreshReason = "unstaged" | "transport-changed" | "fresh" | "unchanged" | "changed";
376
+ export interface StageRefresh {
377
+ reason: StageRefreshReason;
378
+ /** The staged text changed and must be re-injected. */
379
+ restage: boolean;
380
+ /** The caller should write `stage` to `metadata.stage`. */
381
+ persist: boolean;
382
+ stage?: StageRecord;
383
+ /** Absent only when `reason === "fresh"` — the cheap path never assembles. */
384
+ staged?: StagedEnvironment;
385
+ /** Max mtime observed by the probe, for logging. */
386
+ probedAt: number;
387
+ }
388
+ /**
389
+ * Two-phase staleness decision, verbatim:
390
+ *
391
+ * probe = max mtimeMs over tasks/index.json, plans/index.json,
392
+ * knowledge/index.json, meta.json and the linked node's markdown.
393
+ * Missing files contribute 0. sessions/index.json is DELIBERATELY
394
+ * EXCLUDED — heartbeat writes would make every stage permanently stale.
395
+ * 1. no stage record -> restage (reason "unstaged")
396
+ * 2. stage.transport !== active transport-> restage (reason "transport-changed")
397
+ * 3. probe <= stage.stagedAt -> NO rebuild (reason "fresh") [cheap exit]
398
+ * 4. otherwise rebuild, fingerprint = sha256(text):
399
+ * fingerprint === stage.fingerprint -> no restage (reason "unchanged")
400
+ * but persist a bumped stagedAt so
401
+ * the cheap exit works again
402
+ * else -> restage (reason "changed")
403
+ *
404
+ * Step 4's stagedAt bump writes to sessions/index.json, which is exactly why
405
+ * that file must stay out of the probe set: otherwise the bump would invalidate
406
+ * itself on the next call. That exclusion is clause 1 of the pattern, and it is
407
+ * NOT sufficient alone.
408
+ *
409
+ * Clause 2 — STAMP FROM THE PROBE, NOT THE CLOCK. `stagedAt` is the `probedAt`
410
+ * watermark this function observed, so step 3 compares two values from the same
411
+ * measurement. Stamping `Date.now()` instead mixes two clocks that need not
412
+ * agree: anywhere mtime can exceed wall clock (NFS, container skew, an
413
+ * mtime-preserving restore) `probe <= stagedAt` never holds, the cheap exit
414
+ * never fires, and step 4 re-assembles and re-persists on EVERY turn despite a
415
+ * perfectly correct exclusion set.
416
+ *
417
+ * It also closes a read-then-stamp TOCTOU window. The probe runs before
418
+ * `readSources`; a wall clock sampled after would absorb a write landing in
419
+ * between and leave the stage stale until the NEXT write. Stamping the
420
+ * watermark leaves that write above the stamp, so the next call rebuilds and
421
+ * the fingerprint compare decides.
422
+ */
423
+ export declare function planStageRefresh(projectDir: string, slug: string, session: SessionMeta, opts?: StageOptions): Promise<StageRefresh>;
424
+ /**
425
+ * Per-reference caps.
426
+ *
427
+ * A reference belongs to the ONE turn a caller attached it to: it is rendered
428
+ * here and never reaches `buildStagedEnvironment`, so no value below can move
429
+ * the stable tier's fingerprint, its budgets or its truncation ladder.
430
+ */
431
+ export declare const REFERENCE_BUDGETS: {
432
+ /** A doc section is what the user actually selected, so it is quoted — but
433
+ * bounded, because a selection can be a whole chapter. */
434
+ readonly doc: 1600;
435
+ /** A file excerpt is an ANCHOR for a pointer, never the content: the agent
436
+ * reads the live file, so a long excerpt buys only tokens and staleness. */
437
+ readonly fileExcerpt: 400;
438
+ };
439
+ /**
440
+ * ONE reference as prompt text.
441
+ *
442
+ * Deterministic: the same payload renders the same bytes — no timestamps, no
443
+ * counters, no ambient state. Every injected value goes through the same
444
+ * delimiter escape the staged tier uses, so a reference body cannot close its
445
+ * own wrapper and speak in the controller's voice, and every quoted body is
446
+ * introduced by an open tag carrying the governing note.
447
+ */
448
+ export declare function renderReference(reference: SessionReference): string;
449
+ /**
450
+ * The turn's whole reference block, or `""` when there is nothing to render —
451
+ * a turn without references must add no bytes at all.
452
+ */
453
+ export declare function renderReferences(references: readonly SessionReference[]): string;
454
+ //# sourceMappingURL=prompt-assembly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-assembly.d.ts","sourceRoot":"","sources":["../../src/web-server/prompt-assembly.ts"],"names":[],"mappings":"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"}