@sema-agent/core 7.55.0 → 7.57.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 (502) hide show
  1. package/CHANGELOG.md +1054 -0
  2. package/LICENSE +1 -1
  3. package/dist/agents/cascade.js +1 -1
  4. package/dist/agents/cc-peer-directory.js +1 -0
  5. package/dist/agents/child-model-seat.d.ts +0 -11
  6. package/dist/agents/child-model-seat.js +0 -3
  7. package/dist/agents/list-agents-tool.d.ts +3 -1
  8. package/dist/agents/list-agents-tool.js +7 -7
  9. package/dist/agents/org-delivery-gate.js +3 -4
  10. package/dist/agents/peer-directory.d.ts +22 -4
  11. package/dist/agents/peer-directory.js +24 -2
  12. package/dist/agents/peer-held-queue.d.ts +69 -49
  13. package/dist/agents/peer-held-queue.js +255 -106
  14. package/dist/agents/peer-session-drain.js +65 -45
  15. package/dist/agents/repair-loop.js +2 -2
  16. package/dist/agents/send-message-tool.js +7 -6
  17. package/dist/agents/subagent.d.ts +6 -4
  18. package/dist/agents/subagent.js +72 -68
  19. package/dist/agents/tool-filter.d.ts +7 -5
  20. package/dist/agents/tool-filter.js +12 -9
  21. package/dist/agents/verify.d.ts +3 -2
  22. package/dist/agents/verify.js +3 -3
  23. package/dist/brain/anthropic.js +45 -80
  24. package/dist/brain/media-degrade.d.ts +18 -7
  25. package/dist/brain/media-degrade.js +13 -2
  26. package/dist/brain/model-capability.d.ts +37 -0
  27. package/dist/brain/model-capability.js +34 -0
  28. package/dist/brain/model-presets.d.ts +2 -8
  29. package/dist/brain/model-presets.js +1 -6
  30. package/dist/brain/open-responses.js +23 -34
  31. package/dist/brain/openai.js +25 -96
  32. package/dist/brain/provider-dialect.d.ts +3 -2
  33. package/dist/brain/provider-dialect.js +2 -2
  34. package/dist/brain/reasoning-request.d.ts +96 -0
  35. package/dist/brain/reasoning-request.js +174 -0
  36. package/dist/brain/reasoning.d.ts +11 -11
  37. package/dist/brain/reasoning.js +4 -3
  38. package/dist/brain/request-params.d.ts +13 -4
  39. package/dist/brain/request-params.js +21 -5
  40. package/dist/brain/status-sink.d.ts +20 -0
  41. package/dist/brain/status-sink.js +7 -0
  42. package/dist/brain/stream-engine.d.ts +9 -0
  43. package/dist/brain/stream-engine.js +24 -10
  44. package/dist/brain/stream-shared.d.ts +5 -4
  45. package/dist/brain/stream-shared.js +4 -4
  46. package/dist/core/a2a.d.ts +2 -1
  47. package/dist/core/a2a.js +19 -13
  48. package/dist/core/agent-definition.d.ts +10 -0
  49. package/dist/core/allow-entry.d.ts +23 -23
  50. package/dist/core/allow-entry.js +42 -46
  51. package/dist/core/allow-rule-shadow.d.ts +3 -3
  52. package/dist/core/approval-object.d.ts +21 -5
  53. package/dist/core/approval-object.js +44 -17
  54. package/dist/core/ask-origin.d.ts +66 -20
  55. package/dist/core/ask-origin.js +63 -23
  56. package/dist/core/ask-question.d.ts +51 -8
  57. package/dist/core/ask-question.js +3 -4
  58. package/dist/core/ask-unresolvable-notice.d.ts +8 -3
  59. package/dist/core/ask-unresolvable-notice.js +16 -2
  60. package/dist/core/auto-compaction.d.ts +71 -6
  61. package/dist/core/auto-compaction.js +42 -26
  62. package/dist/core/background-agent-store.d.ts +6 -2
  63. package/dist/core/background-agent-store.js +1 -1
  64. package/dist/core/checkpoint-generation.d.ts +53 -0
  65. package/dist/core/checkpoint-generation.js +35 -0
  66. package/dist/core/checkpoint-queries.d.ts +180 -0
  67. package/dist/core/checkpoint-queries.js +272 -0
  68. package/dist/core/checkpoint-requirements.d.ts +19 -1
  69. package/dist/core/checkpoint-requirements.js +6 -0
  70. package/dist/core/checkpoint-store.d.ts +105 -53
  71. package/dist/core/checkpoint-store.js +43 -20
  72. package/dist/core/cross-boundary-taint.d.ts +6 -5
  73. package/dist/core/cross-boundary-taint.js +5 -5
  74. package/dist/core/decision-receipt.d.ts +37 -3
  75. package/dist/core/decision-receipt.js +17 -17
  76. package/dist/core/delegation-not-started.d.ts +6 -0
  77. package/dist/core/delegation-not-started.js +17 -0
  78. package/dist/core/engine-attestation.d.ts +93 -0
  79. package/dist/core/engine-attestation.js +70 -0
  80. package/dist/core/engine-capabilities.d.ts +4 -1
  81. package/dist/core/engine-capabilities.js +1 -1
  82. package/dist/core/engine-notice.d.ts +32 -12
  83. package/dist/core/engine-notice.js +6 -0
  84. package/dist/core/file-history-store.d.ts +104 -22
  85. package/dist/core/file-history-store.js +37 -19
  86. package/dist/core/gate-fold.d.ts +1 -1
  87. package/dist/core/gate-fold.js +22 -10
  88. package/dist/core/gate-lanes.js +73 -65
  89. package/dist/core/gate-outcome.d.ts +26 -5
  90. package/dist/core/gate-outcome.js +26 -0
  91. package/dist/core/governance-codes.d.ts +5 -1
  92. package/dist/core/governance-codes.js +6 -0
  93. package/dist/core/hooks.d.ts +19 -18
  94. package/dist/core/hooks.js +3 -1
  95. package/dist/core/host-floor.d.ts +128 -0
  96. package/dist/core/host-floor.js +206 -0
  97. package/dist/core/host-target.d.ts +38 -3
  98. package/dist/core/host-target.js +38 -5
  99. package/dist/core/mailbox-store.d.ts +62 -1
  100. package/dist/core/mailbox-store.js +59 -0
  101. package/dist/core/mcp-name-judge.d.ts +46 -0
  102. package/dist/core/mcp-name-judge.js +61 -0
  103. package/dist/core/mcp-probe.d.ts +1 -1
  104. package/dist/core/mcp-probe.js +2 -5
  105. package/dist/core/mcp.d.ts +5 -1
  106. package/dist/core/mcp.js +70 -64
  107. package/dist/core/memory-engine/consolidation-driver.js +4 -3
  108. package/dist/core/memory-engine/delegation-settlement.js +15 -24
  109. package/dist/core/memory-engine/distiller.js +26 -17
  110. package/dist/core/memory-engine/engine.js +4 -4
  111. package/dist/core/memory-engine/layout.d.ts +60 -8
  112. package/dist/core/memory-engine/layout.js +70 -8
  113. package/dist/core/model-seat.d.ts +10 -3
  114. package/dist/core/mods/classic-events.d.ts +67 -0
  115. package/dist/core/mods/classic-events.js +1 -0
  116. package/dist/core/mods/dollar.d.ts +13 -0
  117. package/dist/core/mods/extension-call.d.ts +31 -0
  118. package/dist/core/mods/extension-call.js +43 -0
  119. package/dist/core/mods/kernel/dispatch.d.ts +20 -3
  120. package/dist/core/mods/kernel/dispatch.js +140 -69
  121. package/dist/core/mods/kernel/events.d.ts +47 -11
  122. package/dist/core/mods/kernel/events.js +66 -42
  123. package/dist/core/mods/kernel/index.d.ts +4 -4
  124. package/dist/core/mods/kernel/index.js +2 -2
  125. package/dist/core/mods/kernel/receive.d.ts +39 -5
  126. package/dist/core/mods/kernel/receive.js +290 -17
  127. package/dist/core/mods/kernel/registration.d.ts +33 -2
  128. package/dist/core/mods/kernel/registration.js +6 -7
  129. package/dist/core/mods/kernel/site-rules.d.ts +44 -16
  130. package/dist/core/mods/kernel/site-rules.js +24 -42
  131. package/dist/core/mods/spec-seats.d.ts +69 -0
  132. package/dist/core/mods/spec-seats.js +114 -0
  133. package/dist/core/mods/stations.d.ts +28 -8
  134. package/dist/core/mods/stations.js +29 -13
  135. package/dist/core/mods/tool-call.d.ts +79 -0
  136. package/dist/core/mods/tool-call.js +152 -0
  137. package/dist/core/namespaced-rule-name.d.ts +32 -38
  138. package/dist/core/namespaced-rule-name.js +29 -36
  139. package/dist/core/parent-spec-seat.d.ts +6 -1
  140. package/dist/core/parent-spec-seat.js +2 -0
  141. package/dist/core/park-selfcheck.js +3 -0
  142. package/dist/core/permission-rule-consent.d.ts +24 -3
  143. package/dist/core/permission-rule-consent.js +136 -41
  144. package/dist/core/permission-rule-model.d.ts +57 -5
  145. package/dist/core/permission-rule-model.js +90 -31
  146. package/dist/core/permission-rule-org.d.ts +6 -2
  147. package/dist/core/permission-rule-org.js +8 -4
  148. package/dist/core/permission-rule-provider.d.ts +5 -2
  149. package/dist/core/permission-rule-provider.js +41 -11
  150. package/dist/core/permission-rule-store.d.ts +3 -2
  151. package/dist/core/permission-rule-store.js +11 -2
  152. package/dist/core/permission-rule-sync.js +3 -2
  153. package/dist/core/permission-rule-syntax.d.ts +5 -4
  154. package/dist/core/permission-rule-syntax.js +2 -2
  155. package/dist/core/permission-rules.d.ts +4 -4
  156. package/dist/core/permission-rules.js +11 -20
  157. package/dist/core/persisted-rule-arms.d.ts +2 -2
  158. package/dist/core/persisted-rule-arms.js +10 -12
  159. package/dist/core/principal-name.d.ts +20 -0
  160. package/dist/core/principal-name.js +22 -0
  161. package/dist/core/prompt-section-seat.js +4 -6
  162. package/dist/core/protocol-http-auth.d.ts +40 -5
  163. package/dist/core/protocol-http-auth.js +168 -4
  164. package/dist/core/protocol-naming.d.ts +20 -0
  165. package/dist/core/protocol-naming.js +20 -3
  166. package/dist/core/read-only-shell.d.ts +20 -5
  167. package/dist/core/read-only-shell.js +29 -14
  168. package/dist/core/remote-env.d.ts +37 -1
  169. package/dist/core/remote-env.js +11 -0
  170. package/dist/core/resume-startup-cause.d.ts +24 -2
  171. package/dist/core/resume-startup-cause.js +20 -1
  172. package/dist/core/retired-keys.d.ts +16 -0
  173. package/dist/core/retired-keys.js +16 -4
  174. package/dist/core/rewrite-rejudge.d.ts +60 -0
  175. package/dist/core/rewrite-rejudge.js +36 -0
  176. package/dist/core/roles.d.ts +28 -1
  177. package/dist/core/roles.js +81 -26
  178. package/dist/core/rule-lane-reads.d.ts +20 -15
  179. package/dist/core/rule-lane-reads.js +45 -17
  180. package/dist/core/rule-owner.d.ts +3 -3
  181. package/dist/core/rule-owner.js +3 -2
  182. package/dist/core/runner/announce-once-ledger.d.ts +14 -0
  183. package/dist/core/runner/announce-once-ledger.js +5 -0
  184. package/dist/core/runner/assemble-result.d.ts +1 -0
  185. package/dist/core/runner/assemble-result.js +1 -1
  186. package/dist/core/runner/call-hook-stations.d.ts +3 -2
  187. package/dist/core/runner/caller-roster.d.ts +16 -4
  188. package/dist/core/runner/caller-roster.js +33 -6
  189. package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
  190. package/dist/core/runner/capability-withheld-seat.js +24 -0
  191. package/dist/core/runner/classifier-ladder.d.ts +7 -9
  192. package/dist/core/runner/classifier-ladder.js +3 -3
  193. package/dist/core/runner/compact-session.d.ts +10 -0
  194. package/dist/core/runner/compact-session.js +202 -0
  195. package/dist/core/runner/compaction-call-options.d.ts +2 -2
  196. package/dist/core/runner/compaction-seams.d.ts +21 -1
  197. package/dist/core/runner/compaction-seams.js +11 -8
  198. package/dist/core/runner/construction-doors.js +3 -0
  199. package/dist/core/runner/contracts.d.ts +50 -40
  200. package/dist/core/runner/cross-boundary-lane.d.ts +17 -13
  201. package/dist/core/runner/cross-boundary-lane.js +40 -27
  202. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  203. package/dist/core/runner/denial-limit-arms.js +1 -1
  204. package/dist/core/runner/derived-route-fallback.d.ts +13 -0
  205. package/dist/core/runner/derived-route-fallback.js +6 -0
  206. package/dist/core/runner/env-capability-census.d.ts +3 -2
  207. package/dist/core/runner/env-capability-census.js +9 -3
  208. package/dist/core/runner/file-history-request.d.ts +18 -0
  209. package/dist/core/runner/file-history-request.js +21 -13
  210. package/dist/core/runner/file-history-restore.d.ts +45 -0
  211. package/dist/core/runner/file-history-restore.js +46 -0
  212. package/dist/core/runner/gate-exit.d.ts +3 -3
  213. package/dist/core/runner/gate-exit.js +4 -3
  214. package/dist/core/runner/image.d.ts +7 -17
  215. package/dist/core/runner/image.js +9 -60
  216. package/dist/core/runner/permission-rule-lanes.d.ts +11 -11
  217. package/dist/core/runner/permission-rule-lanes.js +39 -17
  218. package/dist/core/runner/person-path-rows.d.ts +12 -9
  219. package/dist/core/runner/person-path-rows.js +18 -7
  220. package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -0
  221. package/dist/core/runner/prepare-acquire-reconcile.js +2 -1
  222. package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
  223. package/dist/core/runner/prepare-ask-lane.js +5 -2
  224. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
  225. package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
  226. package/dist/core/runner/prepare-config-doors.d.ts +35 -38
  227. package/dist/core/runner/prepare-config-doors.js +46 -28
  228. package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
  229. package/dist/core/runner/prepare-defer-classify.js +7 -8
  230. package/dist/core/runner/prepare-file-history.d.ts +48 -8
  231. package/dist/core/runner/prepare-file-history.js +7 -39
  232. package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
  233. package/dist/core/runner/prepare-gate-stations.js +10 -1
  234. package/dist/core/runner/prepare-hands-readface.d.ts +5 -9
  235. package/dist/core/runner/prepare-hands-readface.js +17 -14
  236. package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
  237. package/dist/core/runner/prepare-inherited-gate.js +5 -3
  238. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  239. package/dist/core/runner/prepare-park-ask.d.ts +2 -0
  240. package/dist/core/runner/prepare-park-ask.js +7 -7
  241. package/dist/core/runner/prepare-policy-chain.d.ts +6 -3
  242. package/dist/core/runner/prepare-policy-chain.js +67 -76
  243. package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
  244. package/dist/core/runner/prepare-prompt-assembly.js +10 -8
  245. package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
  246. package/dist/core/runner/prepare-prompt-inputs.js +3 -5
  247. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -0
  248. package/dist/core/runner/prepare-protocol-tools.js +3 -3
  249. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  250. package/dist/core/runner/prepare-safety-scan.js +0 -17
  251. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  252. package/dist/core/runner/prepare-suspend-saga.js +5 -6
  253. package/dist/core/runner/prepare-task.d.ts +5 -2
  254. package/dist/core/runner/prepare-task.js +48 -43
  255. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +20 -4
  256. package/dist/core/runner/prepare-tool-disclosure-mount.js +33 -23
  257. package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
  258. package/dist/core/runner/prepare-turn-wiring.js +2 -7
  259. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -2
  260. package/dist/core/runner/prepare-wiring-manifest.js +3 -3
  261. package/dist/core/runner/resume-admission.js +1 -1
  262. package/dist/core/runner/resume-apply.js +11 -12
  263. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  264. package/dist/core/runner/resume-claim.d.ts +4 -2
  265. package/dist/core/runner/resume-claim.js +15 -8
  266. package/dist/core/runner/retired-runner-deps.js +1 -0
  267. package/dist/core/runner/rewind-files.d.ts +12 -0
  268. package/dist/core/runner/rewind-files.js +90 -0
  269. package/dist/core/runner/run-attachment-seats.js +2 -1
  270. package/dist/core/runner/run-brain-sinks.d.ts +4 -1
  271. package/dist/core/runner/run-brain-sinks.js +4 -2
  272. package/dist/core/runner/run-harness-handlers.js +12 -4
  273. package/dist/core/runner/run-leg.d.ts +1 -1
  274. package/dist/core/runner/run-leg.js +40 -39
  275. package/dist/core/runner/run-notification-lane.d.ts +2 -0
  276. package/dist/core/runner/run-notification-lane.js +2 -2
  277. package/dist/core/runner/run-recovery-lanes.js +3 -4
  278. package/dist/core/runner/run-settle-and-teardown.js +7 -12
  279. package/dist/core/runner/run-terminal-adoption.js +3 -4
  280. package/dist/core/runner/run-turn-boundary.js +4 -15
  281. package/dist/core/runner/runtask.d.ts +10 -0
  282. package/dist/core/runner/runtask.js +35 -42
  283. package/dist/core/runner/send-message-continuation.js +1 -1
  284. package/dist/core/runner/session-read-file-states.d.ts +15 -0
  285. package/dist/core/runner/session-read-file-states.js +28 -0
  286. package/dist/core/runner/session-rule-policy.js +4 -3
  287. package/dist/core/runner/session-verb-doors.d.ts +28 -0
  288. package/dist/core/runner/session-verb-doors.js +48 -0
  289. package/dist/core/runner/session-write-ledger.d.ts +30 -0
  290. package/dist/core/runner/session-write-ledger.js +101 -0
  291. package/dist/core/runner/shell-env-withheld.d.ts +33 -0
  292. package/dist/core/runner/shell-env-withheld.js +51 -0
  293. package/dist/core/runner/skills-seat.js +6 -2
  294. package/dist/core/runner/stream-settle-backstop.js +12 -15
  295. package/dist/core/runner/sub-agent-deny-lists.d.ts +25 -3
  296. package/dist/core/runner/sub-agent-deny-lists.js +25 -17
  297. package/dist/core/runner/synthetic-tools.d.ts +6 -0
  298. package/dist/core/runner/synthetic-tools.js +4 -1
  299. package/dist/core/runner/teardown-bounded.d.ts +1 -0
  300. package/dist/core/runner/teardown-bounded.js +1 -0
  301. package/dist/core/runner/tool-disclosure.d.ts +10 -12
  302. package/dist/core/runner/tool-disclosure.js +1 -3
  303. package/dist/core/runner/tool-end-body.d.ts +1 -1
  304. package/dist/core/runner/tool-end-body.js +10 -9
  305. package/dist/core/runner/tool-not-executed.d.ts +5 -0
  306. package/dist/core/runner/tool-not-executed.js +11 -0
  307. package/dist/core/runner/tool-occurrence.d.ts +8 -0
  308. package/dist/core/runner/tool-occurrence.js +7 -0
  309. package/dist/core/runner/turn-attachments.d.ts +33 -15
  310. package/dist/core/runner/turn-attachments.js +62 -37
  311. package/dist/core/runner-deps.d.ts +7 -7
  312. package/dist/core/safety-axis-vocab.d.ts +6 -4
  313. package/dist/core/safety-axis-vocab.js +6 -5
  314. package/dist/core/safety-merge-corpus.js +11 -3
  315. package/dist/core/secret-env.d.ts +30 -3
  316. package/dist/core/secret-env.js +47 -8
  317. package/dist/core/select-model.js +3 -3
  318. package/dist/core/session-policy-store.d.ts +32 -15
  319. package/dist/core/session-policy-store.js +156 -36
  320. package/dist/core/session-store.d.ts +29 -10
  321. package/dist/core/session-store.js +51 -19
  322. package/dist/core/session.d.ts +82 -16
  323. package/dist/core/session.js +53 -0
  324. package/dist/core/shell-read-silence.d.ts +11 -12
  325. package/dist/core/shell-read-silence.js +9 -5
  326. package/dist/core/shell-run-candidates.js +3 -1
  327. package/dist/core/shell-wrapper-projection.d.ts +1 -1
  328. package/dist/core/shell-wrapper-projection.js +1 -1
  329. package/dist/core/shell-wrapper-table.d.ts +14 -7
  330. package/dist/core/shell-wrapper-table.js +5 -4
  331. package/dist/core/side-query.js +10 -1
  332. package/dist/core/skill-grant.d.ts +7 -2
  333. package/dist/core/skill-grant.js +5 -0
  334. package/dist/core/skills-directory.js +6 -2
  335. package/dist/core/store-contracts/background-agent-store-contract.js +1 -0
  336. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
  337. package/dist/core/store-contracts/checkpoint-store-contract.js +141 -2
  338. package/dist/core/store-contracts/file-history-store-contract.js +40 -1
  339. package/dist/core/store-contracts/mailbox-store-contract.js +49 -1
  340. package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
  341. package/dist/core/store-contracts/peer-directory-contract.js +14 -2
  342. package/dist/core/store-contracts/session-repo-contract.js +51 -0
  343. package/dist/core/store-contracts/session-store-contract.d.ts +2 -1
  344. package/dist/core/store-contracts/session-store-contract.js +150 -14
  345. package/dist/core/stub-env.d.ts +2 -0
  346. package/dist/core/stub-env.js +1 -0
  347. package/dist/core/synthetic-read-verdict.js +3 -3
  348. package/dist/core/task-event.d.ts +32 -4
  349. package/dist/core/task-notification.d.ts +16 -12
  350. package/dist/core/task-registry-agent.d.ts +8 -4
  351. package/dist/core/task-registry-agent.js +14 -8
  352. package/dist/core/task-registry-shared.d.ts +35 -6
  353. package/dist/core/task-registry-shared.js +11 -0
  354. package/dist/core/task-registry.d.ts +10 -1
  355. package/dist/core/task-registry.js +12 -2
  356. package/dist/core/task-result.d.ts +18 -0
  357. package/dist/core/task-spec.d.ts +18 -13
  358. package/dist/core/tighten-task-spec.js +2 -1
  359. package/dist/core/tool-catalog-entries.d.ts +23 -0
  360. package/dist/core/tool-catalog-entries.js +14 -2
  361. package/dist/core/tool-check-reading.d.ts +11 -0
  362. package/dist/core/tool-check-reading.js +11 -6
  363. package/dist/core/tool-model-gate.d.ts +2 -2
  364. package/dist/core/tool-name-aliases.d.ts +48 -18
  365. package/dist/core/tool-name-aliases.js +36 -19
  366. package/dist/core/tool-name-readings.d.ts +6 -32
  367. package/dist/core/tool-name-readings.js +2 -9
  368. package/dist/core/tool-policy.d.ts +78 -204
  369. package/dist/core/tool-policy.js +152 -204
  370. package/dist/core/tool-result-store.js +3 -0
  371. package/dist/core/tool-roster.d.ts +14 -19
  372. package/dist/core/tool-roster.js +11 -44
  373. package/dist/core/tool-spec-members.js +7 -2
  374. package/dist/core/tool-spec.d.ts +19 -11
  375. package/dist/core/tools.js +49 -9
  376. package/dist/core/trace.d.ts +6 -3
  377. package/dist/core/trace.js +2 -1
  378. package/dist/core/types.d.ts +1 -1
  379. package/dist/core/untrusted-egress.d.ts +20 -0
  380. package/dist/core/untrusted-egress.js +77 -3
  381. package/dist/core/user-attachment.d.ts +8 -1
  382. package/dist/core/user-attachment.js +64 -4
  383. package/dist/core/wiring-manifest.d.ts +9 -0
  384. package/dist/core/wiring-manifest.js +1 -0
  385. package/dist/core/workflow-journal-store.js +1 -1
  386. package/dist/engine/compaction/compaction.js +3 -2
  387. package/dist/engine/execution-env/node-execution-env.d.ts +7 -3
  388. package/dist/engine/execution-env/node-execution-env.js +11 -2
  389. package/dist/engine/harness/agent-harness.d.ts +32 -6
  390. package/dist/engine/harness/agent-harness.js +65 -9
  391. package/dist/engine/harness/types.d.ts +29 -2
  392. package/dist/engine/llm/types.d.ts +28 -11
  393. package/dist/engine/loop/agent-loop.d.ts +23 -4
  394. package/dist/engine/loop/agent-loop.js +42 -30
  395. package/dist/engine/loop/types.d.ts +16 -6
  396. package/dist/engine/session/memory-repo.d.ts +9 -2
  397. package/dist/engine/session/memory-repo.js +11 -1
  398. package/dist/engine/session/repo-utils.d.ts +6 -5
  399. package/dist/engine/session/repo-utils.js +14 -6
  400. package/dist/index.d.ts +34 -17
  401. package/dist/index.js +22 -11
  402. package/dist/internal/harness-types.d.ts +1 -1
  403. package/dist/internal/harness.d.ts +2 -1
  404. package/dist/internal/harness.js +1 -0
  405. package/dist/internal/llm.d.ts +1 -1
  406. package/dist/orchestration/run-workflow-tool.d.ts +6 -4
  407. package/dist/orchestration/run-workflow-tool.js +5 -3
  408. package/dist/orchestration/workflow.d.ts +3 -0
  409. package/dist/orchestration/workflow.js +36 -18
  410. package/dist/primitives/credential-ref.d.ts +89 -0
  411. package/dist/primitives/credential-ref.js +125 -0
  412. package/dist/primitives/index.d.ts +2 -0
  413. package/dist/primitives/index.js +1 -0
  414. package/dist/prompt-assembly/artifact.d.ts +2 -2
  415. package/dist/prompt-assembly/artifact.js +5 -3
  416. package/dist/prompt-assembly/assemble.d.ts +3 -2
  417. package/dist/prompt-assembly/assemble.js +1 -1
  418. package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
  419. package/dist/prompt-assembly/catalog/schema.js +34 -2
  420. package/dist/prompt-assembly/catalog/sections.js +0 -1
  421. package/dist/prompt-assembly/composer.d.ts +3 -1
  422. package/dist/prompt-assembly/composer.js +1 -1
  423. package/dist/prompt-assembly/event-registry.js +1 -0
  424. package/dist/prompt-assembly/overlay.js +3 -3
  425. package/dist/prompt-assembly/renderers.d.ts +1 -1
  426. package/dist/prompt-assembly/renderers.js +0 -3
  427. package/dist/prompt-assembly/role-base.d.ts +15 -0
  428. package/dist/prompt-assembly/role-base.js +9 -0
  429. package/dist/prompt-assembly/select.d.ts +39 -14
  430. package/dist/prompt-assembly/select.js +47 -16
  431. package/dist/prompt-assembly/sugar.d.ts +17 -11
  432. package/dist/prompt-assembly/sugar.js +8 -3
  433. package/dist/prompt-assembly/tool-catalog.d.ts +37 -3
  434. package/dist/prompt-assembly/tool-catalog.js +114 -4
  435. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
  436. package/dist/prompt-assembly/turn-snapshot.js +0 -3
  437. package/dist/prompt-assembly/types.d.ts +0 -7
  438. package/dist/scenarios/full-body.d.ts +11 -11
  439. package/dist/scenarios/full-body.js +4 -3
  440. package/dist/shell/lex.js +10 -1
  441. package/dist/stores/file/adoption/adopt.js +4 -3
  442. package/dist/stores/file/adoption/marker.js +3 -2
  443. package/dist/stores/file/checkpoint-store.d.ts +21 -4
  444. package/dist/stores/file/checkpoint-store.js +38 -7
  445. package/dist/stores/file/file-history-store.js +18 -11
  446. package/dist/stores/file/index.js +2 -1
  447. package/dist/stores/file/mailbox-store.d.ts +2 -1
  448. package/dist/stores/file/mailbox-store.js +26 -1
  449. package/dist/stores/file/permission-rule-adopt.js +4 -3
  450. package/dist/stores/file/permission-rule-store.js +11 -5
  451. package/dist/stores/file/session-store.d.ts +29 -6
  452. package/dist/stores/file/session-store.js +42 -50
  453. package/dist/tools/artifact/artifact-tool.js +3 -2
  454. package/dist/tools/fs/bash-program-position.d.ts +19 -2
  455. package/dist/tools/fs/bash-program-position.js +56 -9
  456. package/dist/tools/fs/bash-readonly-classifier.d.ts +15 -19
  457. package/dist/tools/fs/bash-readonly-classifier.js +5 -10
  458. package/dist/tools/fs/fs-bash.js +15 -8
  459. package/dist/tools/fs/fs-glob.d.ts +86 -0
  460. package/dist/tools/fs/fs-glob.js +226 -0
  461. package/dist/tools/fs/fs-read.js +14 -14
  462. package/dist/tools/fs/fs-search-tools.js +7 -7
  463. package/dist/tools/fs/pattern-kill-guard.d.ts +23 -0
  464. package/dist/tools/fs/pattern-kill-guard.js +429 -0
  465. package/dist/tools/fs/pdf.d.ts +9 -6
  466. package/dist/tools/fs/pdf.js +3 -3
  467. package/dist/tools/fs/program-read-model.d.ts +57 -7
  468. package/dist/tools/fs/program-read-model.js +11 -1
  469. package/dist/tools/fs/program-read-model.rows.js +78 -21
  470. package/dist/tools/fs/read-deny-home.js +3 -2
  471. package/dist/tools/fs/removal-targets.d.ts +17 -7
  472. package/dist/tools/fs/removal-targets.js +47 -20
  473. package/dist/tools/fs/search.d.ts +10 -0
  474. package/dist/tools/fs/search.js +20 -26
  475. package/dist/tools/fs/shell-read-argv.js +89 -12
  476. package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
  477. package/dist/tools/fs/shell-read-bindings.js +41 -0
  478. package/dist/tools/fs/shell-read-check.d.ts +10 -0
  479. package/dist/tools/fs/shell-read-check.js +13 -20
  480. package/dist/tools/fs/shell-read-environment.d.ts +5 -1
  481. package/dist/tools/fs/shell-read-environment.js +3 -2
  482. package/dist/tools/fs/shell-read-establish.d.ts +15 -0
  483. package/dist/tools/fs/shell-read-establish.js +19 -20
  484. package/dist/tools/fs/shell-read-identity.d.ts +12 -9
  485. package/dist/tools/fs/shell-read-identity.js +9 -4
  486. package/dist/tools/fs/shell-read-plan.js +24 -16
  487. package/dist/tools/fs/shell-read-start.d.ts +6 -1
  488. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
  489. package/dist/tools/fs/shell-read-write-targets.js +2 -3
  490. package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
  491. package/dist/tools/fs/shell-reentry-text.js +40 -2
  492. package/dist/tools/todo.js +63 -63
  493. package/dist/tools/web.d.ts +19 -14
  494. package/dist/tools/web.js +46 -61
  495. package/package.json +1 -1
  496. package/test/export-surface.snapshot.json +221 -7
  497. package/wire-changes/core-stores-sql@7.57.0.json +29 -0
  498. package/wire-changes/core@7.56.0.json +3891 -0
  499. package/wire-changes/core@7.57.0.json +1487 -0
  500. package/wire-changes/mods-kernel@7.56.0.json +303 -0
  501. package/wire-changes/mods-kernel@7.57.0.json +29 -0
  502. package/wire-changes/primitives@7.57.0.json +245 -0
@@ -1,13 +1,6 @@
1
1
  export function resolveFileHistoryRequest(spec, deps) {
2
- const onTrackFailure = deps.onTrackFailure === undefined ? "refuse" : deps.onTrackFailure;
3
- if (onTrackFailure !== "refuse" && onTrackFailure !== "proceed-unprotected") {
4
- const e = new Error(`RunnerDeps.onTrackFailure must be "refuse" or "proceed-unprotected"; got ${JSON.stringify(deps.onTrackFailure)} — refusing prepare rather than silently defaulting a first-touch-failure policy.`);
5
- e.code = "config.invalid_on_track_failure";
6
- throw e;
7
- }
8
- const wiredSeat = resolveWiredFileHistoryStore(deps.fileHistoryStore);
2
+ const { onTrackFailure, wiredSeat, captureOff } = resolveDeploymentFileHistory(deps);
9
3
  const runDeclaredOff = resolveRunFileHistoryWord(spec.fileHistory);
10
- const captureOff = resolveFileHistoryCaptureWord(deps.fileHistoryCapture, wiredSeat);
11
4
  const declaredOff = wiredSeat === "disabled" || runDeclaredOff;
12
5
  const fileHistoryStore = declaredOff ? undefined : wiredSeat;
13
6
  const captureStore = captureOff ? undefined : fileHistoryStore;
@@ -34,14 +27,29 @@ export function resolveFileHistoryRequest(spec, deps) {
34
27
  if (rewindTarget !== undefined && fileHistoryStore === undefined) {
35
28
  if (runDeclaredOff)
36
29
  throw captureDisabledRefusal(rewindTarget);
37
- const e = new Error(wiredSeat === "disabled"
38
- ? `rewind-files: restoring files to entry "${rewindTarget}" requires a history backend, but this deployment declared file history disabled (RunnerDeps.fileHistoryStore: "disabled"), so there is no history to restore — the files were NOT rewound`
39
- : `rewind-files: restoring files to entry "${rewindTarget}" requires a history backend, but this deployment wired no RunnerDeps.fileHistoryStore — no file history was ever recorded, so the files were NOT rewound`);
40
- e.code = "rewind.store_unconfigured";
41
- throw e;
30
+ throw storeUnconfiguredRefusal(wiredSeat, rewindTarget);
42
31
  }
43
32
  return { onTrackFailure, wiredSeat, runDeclaredOff, captureOff, declaredOff, fileHistoryStore, captureStore, rewindTarget, rewindBefore };
44
33
  }
34
+ export function resolveDeploymentFileHistory(deps) {
35
+ const onTrackFailure = deps.onTrackFailure === undefined ? "refuse" : deps.onTrackFailure;
36
+ if (onTrackFailure !== "refuse" && onTrackFailure !== "proceed-unprotected") {
37
+ const e = new Error(`RunnerDeps.onTrackFailure must be "refuse" or "proceed-unprotected"; got ${JSON.stringify(deps.onTrackFailure)} — refusing prepare rather than silently defaulting a first-touch-failure policy.`);
38
+ e.code = "config.invalid_on_track_failure";
39
+ throw e;
40
+ }
41
+ const wiredSeat = resolveWiredFileHistoryStore(deps.fileHistoryStore);
42
+ const captureOff = resolveFileHistoryCaptureWord(deps.fileHistoryCapture, wiredSeat);
43
+ const fileHistoryStore = wiredSeat === "disabled" ? undefined : wiredSeat;
44
+ return { onTrackFailure, wiredSeat, captureOff, fileHistoryStore, captureStore: captureOff ? undefined : fileHistoryStore };
45
+ }
46
+ export function storeUnconfiguredRefusal(wiredSeat, target) {
47
+ const e = new Error(wiredSeat === "disabled"
48
+ ? `rewind-files: restoring files to entry "${target}" requires a history backend, but this deployment declared file history disabled (RunnerDeps.fileHistoryStore: "disabled"), so there is no history to restore — the files were NOT rewound`
49
+ : `rewind-files: restoring files to entry "${target}" requires a history backend, but this deployment wired no RunnerDeps.fileHistoryStore — no file history was ever recorded, so the files were NOT rewound`);
50
+ e.code = "rewind.store_unconfigured";
51
+ return e;
52
+ }
45
53
  function resolveWiredFileHistoryStore(candidate) {
46
54
  if (candidate === undefined || candidate === "disabled")
47
55
  return candidate;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The ONE restore of a session's tracked set and the ONE reading of its answer, reached DOWN for by the two seats that
3
+ * restore: the file-history phase's restore seat (`prepare-file-history.ts`, a `restoreFiles` / `rewindFilesTo` run)
4
+ * and the out-of-run rewind (`rewind-files.ts`, `Runner.rewindFiles`). A rewind outside a run is therefore the same
5
+ * history operation as the one a run performs, refused with the same codes and sentences, and handing the paths it
6
+ * moved to the same read-state channel.
7
+ */
8
+ import type { ExecutionEnv } from "../../internal/harness.js";
9
+ import type { FileHistoryRestoreResult, FileHistoryStore, RewindFilesResult } from "../file-history-store.js";
10
+ import type { SessionReadFileStates } from "./session-read-file-states.js";
11
+ /** A restore outcome a seat may accept: the ledger, with `failed` (the operation could not run) already refused —
12
+ * the out-of-run rewind's answer carries exactly this. */
13
+ export type AcceptedFileHistoryRestore = Extract<RewindFilesResult, {
14
+ dryRun: false;
15
+ }>["restore"];
16
+ /**
17
+ * The ONE restore of a session's tracked set and the ONE reading of its answer — the run's restore seat and the
18
+ * out-of-run `Runner.rewindFiles` both call it, so the two cannot disagree about what a restore means:
19
+ * · every path the attempt may have moved (`filesChanged` ∪ `failed[].path`, whatever the outcome) goes to the
20
+ * session's read-state channel (`noteRestored`), because a read record of the old bytes must stop vouching
21
+ * for them on the session's next leg;
22
+ * · `not_found` is "no boundary for that entry" — `rewind_snapshot.unresolvable` (in a "before" restore, the
23
+ * before-specific sentence);
24
+ * · any other `failed` outcome is `rewind.restore_failed` carrying the ledger and the store's own judgment on
25
+ * the coded `detail` channel (a host reads it, never the prose).
26
+ * A `complete` or `partial` answer is returned for the caller to accept: only a run goes on to work on the tree,
27
+ * so only the run seat has a partial-restore policy.
28
+ */
29
+ export declare function settleFileHistoryRestore(input: {
30
+ store: FileHistoryStore;
31
+ sessionId: string;
32
+ target: string;
33
+ before: boolean;
34
+ env: ExecutionEnv;
35
+ root: string;
36
+ signal: AbortSignal | undefined;
37
+ readStates: Pick<SessionReadFileStates, "noteRestored"> | undefined;
38
+ }): Promise<AcceptedFileHistoryRestore>;
39
+ /** The per-file ledger as one sentence (counts, then each refused / failed path with its reason). */
40
+ export declare function fileHistoryRestoreLedgerLine(r: FileHistoryRestoreResult): string;
41
+ /** A restore whose entry has no boundary on the session; a PREVIEW's form says "usable" — its probe
42
+ * (`canRestore`) cannot tell a boundary never made from one that is there and unreadable, a real restore can. */
43
+ export declare function noBoundaryRefusal(target: string, sessionId: string, preview?: boolean): Error & {
44
+ code?: string;
45
+ };
@@ -0,0 +1,46 @@
1
+ import { resolveTrackKey } from "../file-history-store.js";
2
+ import { isGovernanceRetryClass } from "../governance-codes.js";
3
+ export async function settleFileHistoryRestore(input) {
4
+ const { store, sessionId, target, env, root } = input;
5
+ const restored = await store.restore(sessionId, target, env, root, input.signal);
6
+ input.readStates?.noteRestored(sessionId, restoredAbsPathsOf(restored, root));
7
+ if (restored.outcome !== "failed")
8
+ return restored;
9
+ if (restored.error?.code === "not_found") {
10
+ if (input.before)
11
+ throw beforeWithoutBoundaryRefusal(target, sessionId);
12
+ throw noBoundaryRefusal(target, sessionId);
13
+ }
14
+ const e = new Error(`rewind-files restore failed (${restored.error?.code ?? "restore_failed"}): ${restored.error?.message ?? "unknown"} — ledger: ${fileHistoryRestoreLedgerLine(restored)}`);
15
+ e.code = "rewind.restore_failed";
16
+ if (restored.error !== undefined)
17
+ e.detail = { code: restored.error.code, ...(isGovernanceRetryClass(restored.error.permanence) ? { permanence: restored.error.permanence } : {}) };
18
+ throw e;
19
+ }
20
+ export function fileHistoryRestoreLedgerLine(r) {
21
+ return `applied ${r.filesChanged.length}, identical ${r.identicalSkipped.length}, refused ${r.refused.length}${r.refused.length > 0 ? ` (${r.refused.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}, failed ${r.failed.length}${r.failed.length > 0 ? ` (${r.failed.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}`;
22
+ }
23
+ export function noBoundaryRefusal(target, sessionId, preview = false) {
24
+ const e = new Error(preview
25
+ ? `rewind-files: no usable file-history boundary exists for entry "${target}" on session "${sessionId}" (never made, reaped, or there but unreadable — a real rewind names which), so there is nothing to preview. ${rewindRefusalTail()}`
26
+ : `rewind-files: no file-history boundary exists for entry "${target}" on session "${sessionId}" — the working tree was NOT rewound. ${rewindRefusalTail()}`);
27
+ e.code = "rewind_snapshot.unresolvable";
28
+ return e;
29
+ }
30
+ function restoredAbsPathsOf(restored, root) {
31
+ const out = [];
32
+ for (const key of [...restored.filesChanged, ...restored.failed.map((f) => f.path)]) {
33
+ const abs = resolveTrackKey(root, key);
34
+ if (abs.ok)
35
+ out.push(abs.abs);
36
+ }
37
+ return out;
38
+ }
39
+ function rewindRefusalTail() {
40
+ return `Boundaries are keyed by a turn's INITIAL entry and exist only for turns that recorded history (a RunnerDeps.fileHistoryStore wired, capture on, and the run's file history not disabled); a boundary from the retired whole-tree snapshot era is not restorable by this seat, and reaped boundaries have none.`;
41
+ }
42
+ function beforeWithoutBoundaryRefusal(target, sessionId) {
43
+ const e = new Error(`rewind-files: entry "${target}" has no file-history boundary on session "${sessionId}" — a before-restore needs the target's own boundary and never crosses into an earlier turn, so the working tree was NOT rewound. ${rewindRefusalTail()}`);
44
+ e.code = "rewind_snapshot.unresolvable";
45
+ return e;
46
+ }
@@ -238,9 +238,9 @@ export interface GatePass {
238
238
  hookAsk: Extract<PermissionResult, {
239
239
  action: "ask";
240
240
  }> | undefined;
241
- /** borrowed-mutable — the phase-1 screen answered its own `allow` (the exact word), naming the registration that answered
242
- * it: the allow layer's hook member (its `entries`), which speaks only while no policy rewrote the arguments after the
243
- * screen saw them. Writer: the dispatch (phase 1). Reader: the lanes' allow layer (its one reader). */
241
+ /** borrowed-mutable — the screen answered its own `allow` (the exact word) for the bytes that run, naming the registration
242
+ * that answered it: the allow layer's hook member (its `entries`). Writers: the dispatch (phase 1) and the fold's second
243
+ * look after a policy rewrite (rewrite-rejudge.ts), whose answer replaces the first. Reader: the lanes' allow layer. */
244
244
  hookAllowedBy?: readonly string[];
245
245
  /** borrowed-mutable — `tool.check` was raised in this pass's first adjudication, which raises it once: set by the
246
246
  * raise in front of the ask branch, read by the raise behind the read-only arm (it then raises nothing). Writer and
@@ -244,7 +244,7 @@ export function hookDenyExit(pass, deniedReason) {
244
244
  preToolContext: pass.preToolContext,
245
245
  };
246
246
  }
247
- const ANCESTOR_UNRESOLVED_CLASSIFICATION = classifyAsk({ action: "ask", message: "" }, { contentQuestion: false, markedUnresolvable: true, org: undefined, tightened: undefined, mandateMarks: {} });
247
+ const ancestorUnresolvedClassification = (call) => classifyAsk({ action: "ask", message: "" }, { call, contentQuestion: false, markedUnresolvable: true, org: undefined, tightened: undefined, mandateMarks: {} });
248
248
  function unresolvableCauseOfSettlement(gate) {
249
249
  if (gate.disposition.kind !== "denied" || gate.disposition.deniedBy !== "ask_resolution" || gate.settlement === undefined)
250
250
  return undefined;
@@ -274,12 +274,13 @@ export async function exitGate(pass) {
274
274
  const tableSeat = unresolvable !== undefined ? askUnresolvableRemedySeat(unresolvable) : undefined;
275
275
  const remedySeat = tableSeat === "absent" && input.askSeatForm === "declared_none" && pass.askClassification?.classes.has("content_question") !== true ? "declared_none" : tableSeat;
276
276
  const bindingRefusal = attestationOf(pass.decision, { toolCallId, toolName })?.bindingRefusal;
277
- const remedy = bindingRefusal !== undefined ? undefined : remedySeat !== undefined ? headlessApprovalRemedy({ mode: input.permissionMode?.(), classification: foldCause !== undefined ? ANCESTOR_UNRESOLVED_CLASSIFICATION : pass.askClassification, approverSeat: remedySeat, coveredWrite: isProtectedWrite(pass.req), ...(pass.shellUnplacedOptions !== undefined ? { unplacedOptions: pass.shellUnplacedOptions } : {}) }) : undefined;
277
+ const headless = bindingRefusal !== undefined ? undefined : remedySeat !== undefined ? headlessApprovalRemedy({ mode: input.permissionMode?.(), classification: foldCause !== undefined ? ancestorUnresolvedClassification(pass.req) : pass.askClassification, approverSeat: remedySeat, coveredWrite: isProtectedWrite(pass.req), ...(pass.shellUnplacedOptions !== undefined ? { unplacedOptions: pass.shellUnplacedOptions } : {}) }) : undefined;
278
+ const remedy = headless?.sentence;
278
279
  const denyReason = withParkFailureCause(`${decisionText(pass.decision) ?? `tool "${toolName}" denied by policy`}${remedy !== undefined ? ` — ${remedy}` : ""}`, pass.parkFailed, unresolvable !== undefined);
279
280
  if (unresolvable !== undefined) {
280
281
  const cause = unresolvable;
281
282
  const noticeRemedy = bindingRefusal ?? remedy;
282
- pass.notifier.notify(() => input.onAskUnresolvable?.({ toolName, toolCallId, cause, parkLaneExisted: pass.suspendAsk !== undefined, ...(noticeRemedy !== undefined ? { remedy: noticeRemedy } : {}) }), "toolGate.onAskUnresolvable");
283
+ pass.notifier.notify(() => input.onAskUnresolvable?.({ toolName, toolCallId, cause, parkLaneExisted: pass.suspendAsk !== undefined, ...(noticeRemedy !== undefined ? { remedy: noticeRemedy } : {}), ...(headless !== undefined ? { remedies: headless.remedies } : {}) }), "toolGate.onAskUnresolvable");
283
284
  }
284
285
  const finalInput = frozenInputSnapshot(pass.currentInput);
285
286
  await notifyPermissionDenied({
@@ -1,22 +1,12 @@
1
1
  import type { ImageContent } from "../../internal/llm.js";
2
+ import { type HostFloor } from "../host-floor.js";
2
3
  import type { ImageInput } from "../types.js";
3
4
  /**
4
- * Block obviously-internal hosts (loopback / private ranges / link-local incl. cloud metadata / CGNAT / IPv6
5
- * ULA, site-local, unspecified, IPv4-mapped, NAT64). Classifies with `net.isIP` rather than a string-prefix
6
- * regex — the old regex both UNDER-matched (IPv4-mapped `::ffff:`, NAT64, CGNAT 100.64/10, `::`) and
7
- * OVER-matched (any DNS name starting `fc`/`fd`, e.g. `fdpartners.com`). Numeric IPv4 forms (decimal/octal/hex)
8
- * are normalized to dotted-decimal by `URL` before this is called, so they're already covered.
9
- * Note: this checks the LITERAL host; it does NOT resolve DNS, so a hostname resolving to a private IP is not
10
- * caught here (documented limitation — a deployment needing that supplies its own resolve-and-pin guard).
5
+ * The guard of a remote image URL: https only, then the one egress hop judge with the private-network floor (`hopRefusal`,
6
+ * host-floor.ts) — the deployment's host floor (`RunnerDeps.hostFloor`, `floor`) must admit the host, and a private host is
7
+ * reachable only when the floor's `only` list names it — so an end-user-supplied `images[].url` can't make the server fetch
8
+ * `169.254.169.254` or a private box. The host is read literally (no DNS resolution). Throws on a refused URL.
11
9
  */
12
- export declare function isPrivateHost(host: string): boolean;
13
- /**
14
- * Default SSRF guard for a remote image URL: https only, and no internal/reserved hosts (so an
15
- * end-user-supplied `images[].url` can't make the server fetch `169.254.169.254` or a private box).
16
- * Note: this checks the URL host literally — it does not resolve DNS, so a hostname that resolves to
17
- * a private IP is not caught here; a deployment needing that should supply `RunnerDeps.allowImageUrl`
18
- * (e.g. a CDN allowlist or a resolve-and-pin check). Throws on a disallowed URL.
19
- */
20
- export declare function defaultImageUrlGuard(raw: string): void;
10
+ export declare function defaultImageUrlGuard(raw: string, floor?: HostFloor): void;
21
11
  /** Resolve an {@link ImageInput} (inline base64 or a URL to fetch) into a vendored `ImageContent` block. */
22
- export declare function toImageContent(img: ImageInput, allowUrl?: (url: string) => boolean): Promise<ImageContent>;
12
+ export declare function toImageContent(img: ImageInput, floor: HostFloor | undefined): Promise<ImageContent>;
@@ -1,55 +1,10 @@
1
- import { isIP } from "node:net";
1
+ import { hopRefusal } from "../host-floor.js";
2
2
  import { ADMITTED_IMAGE_MIME_TYPES, imageMagicMatches } from "../image-admission.js";
3
3
  import { admitInlineImage } from "./inline-image.js";
4
4
  function sniffImageMime(bytes) {
5
5
  return ADMITTED_IMAGE_MIME_TYPES.find((mime) => imageMagicMatches(bytes, mime));
6
6
  }
7
- function isPrivateV4(ip) {
8
- const o = ip.split(".").map(Number);
9
- if (o.length !== 4 || o.some((n) => !Number.isInteger(n) || n < 0 || n > 255))
10
- return true;
11
- const [a, b] = o;
12
- return (a === 127 ||
13
- a === 10 ||
14
- a === 0 ||
15
- (a === 172 && b >= 16 && b <= 31) ||
16
- (a === 192 && b === 168) ||
17
- (a === 169 && b === 254) ||
18
- (a === 100 && b >= 64 && b <= 127));
19
- }
20
- function embeddedV4(h) {
21
- const fromHex = (hi, lo) => {
22
- const a = parseInt(hi, 16), c = parseInt(lo, 16);
23
- return `${(a >> 8) & 255}.${a & 255}.${(c >> 8) & 255}.${c & 255}`;
24
- };
25
- let m = /^(?:::ffff:|64:ff9b::)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(h);
26
- if (m)
27
- return m[1];
28
- m = /^(?:::ffff:|64:ff9b::)([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(h);
29
- if (m)
30
- return fromHex(m[1], m[2]);
31
- return undefined;
32
- }
33
- export function isPrivateHost(host) {
34
- const h = host
35
- .replace(/^\[|\]$/g, "")
36
- .replace(/\.+$/, "")
37
- .toLowerCase();
38
- if (h === "localhost")
39
- return true;
40
- const fam = isIP(h);
41
- if (fam === 0)
42
- return false;
43
- if (fam === 4)
44
- return isPrivateV4(h);
45
- const v4 = embeddedV4(h);
46
- if (v4)
47
- return isPrivateV4(v4);
48
- if (h === "::1" || h === "::")
49
- return true;
50
- return /^fe[89ab]/.test(h) || /^f[cd]/.test(h) || /^fe[c-f]/.test(h);
51
- }
52
- export function defaultImageUrlGuard(raw) {
7
+ export function defaultImageUrlGuard(raw, floor) {
53
8
  let u;
54
9
  try {
55
10
  u = new URL(raw);
@@ -60,9 +15,9 @@ export function defaultImageUrlGuard(raw) {
60
15
  if (u.protocol !== "https:") {
61
16
  throw new Error(`image url must be https (got "${u.protocol}")`);
62
17
  }
63
- if (isPrivateHost(u.hostname)) {
64
- throw new Error(`image url host not allowed (private/reserved): ${u.hostname}`);
65
- }
18
+ const refused = hopRefusal(u, floor, true);
19
+ if (refused !== undefined)
20
+ throw new Error(`image url not fetched — ${refused}`);
66
21
  }
67
22
  const IMAGE_FETCH_TIMEOUT_MS = 30_000;
68
23
  const IMAGE_MAX_BYTES = 20 * 1024 * 1024;
@@ -112,17 +67,11 @@ async function fetchImageCapped(url) {
112
67
  }
113
68
  return { buf, mimeType: sniffed };
114
69
  }
115
- export async function toImageContent(img, allowUrl) {
70
+ export async function toImageContent(img, floor) {
116
71
  if ("url" in img) {
117
- if (allowUrl) {
118
- if (!allowUrl(img.url)) {
119
- throw new Error(`image url rejected by policy: ${img.url}`);
120
- }
121
- }
122
- else {
123
- defaultImageUrlGuard(img.url);
124
- }
125
- const { buf, mimeType } = await fetchImageCapped(img.url);
72
+ const url = img.url;
73
+ defaultImageUrlGuard(url, floor);
74
+ const { buf, mimeType } = await fetchImageCapped(url);
126
75
  return { type: "image", data: buf.toString("base64"), mimeType };
127
76
  }
128
77
  return admitInlineImage(img);
@@ -29,11 +29,8 @@ import { type OrgGateVerdict, type PersistedRuleAnswer, type PersistedRuleHit, t
29
29
  import type { PersistedRule, PersistedRuleVerdict, RuleOffer, ScopeRootIdentity, SegmentCoverage } from "../permission-rule-model.js";
30
30
  import { type PathRuleBases } from "../permission-rule-model.js";
31
31
  import { type HeadLeads } from "../path-rule-binding.js";
32
- import type { ToolCallLike } from "../tool-registry.js";
33
32
  import { type OrgRuleResolution } from "../permission-rule-org.js";
34
33
  import { type EffectivePermissionRules, type PermissionRuleStoreProvider, type PersonRowsRead } from "../permission-rule-provider.js";
35
- /** The one SHELL tool the persisted-rule lane's COMMAND grammar speaks for (the rule-offer face mints for it, the name tools and the host tools); the other grammars' tools are derived (`ruleToolGrammarOf`). */
36
- export declare const PERSISTED_RULE_TOOL = "Bash";
37
34
  /**
38
35
  * The evidence an INHERITED-lane ask carries.
39
36
  *
@@ -121,7 +118,7 @@ export interface PermissionRuleLanes {
121
118
  * `onDegraded` receives the snapshot's degraded line ({@link describeUnreadableOrgRows}) on every adjudication over a
122
119
  * snapshot carrying a row this layer cannot read. */
123
120
  export declare function orgRuleLane(cfg: {
124
- read: (toolCallId: string) => Promise<Pick<EffectivePermissionRules, "org">>;
121
+ read: (toolCallId: string, judged: unknown) => Promise<Pick<EffectivePermissionRules, "org">>;
125
122
  questionToolMounted: boolean;
126
123
  questionToolName: string;
127
124
  onRevisionDefect: (message: string) => void;
@@ -177,10 +174,10 @@ export declare function createPermissionRuleLanes(cfg: {
177
174
  onRevisionDefect: (message: string) => void;
178
175
  /** Sink for a judgment against the rows that threw, and for the org lane's degraded-snapshot line. */
179
176
  onDisclosure: (message: string) => void;
180
- /** The run's one reader of the person's rules: the call's ONE read, pinned, that the read station, the org lane and the
181
- * personal lane all take (the lanes consume a reader; they do not make one). */
177
+ /** The run's one reader of the person's rules: the call's ONE read of the bytes judged, pinned, that the read station, the
178
+ * org lane and the personal lane all take (the lanes consume a reader; they do not make one). */
182
179
  rows: {
183
- pin(toolCallId: string): Promise<PersonRowsRead>;
180
+ pin(toolCallId: string, judged?: unknown): Promise<PersonRowsRead>;
184
181
  };
185
182
  /** The allow layer's auto strip over a stored ALLOW row (true ⇒ out of this call's table BEFORE the coverage is chosen). */
186
183
  stripsAllow?: (rule: PersistedRule, req: ToolCallRequest) => boolean;
@@ -203,9 +200,12 @@ interface RuleOffersAskFacts extends RuleClearanceFacts {
203
200
  * approval-preview field is a standing example of what happens otherwise: it reached two of the
204
201
  * five, so a delegated child's card was silently poorer than the top-level one).
205
202
  *
206
- * Empty unless a rule lane is armed — offering an option that redemption would refuse is worse than
207
- * offering none — and empty for any call the lane cannot speak for (a compound, a redirection, a
208
- * substitution, or a tool that is not the shell tool).
203
+ * Silent (no offer and no reason) only where no rule speaks for the call: no lane armed, the anonymous floor,
204
+ * a call the lane never judges — offering an option that redemption would refuse is worse than offering none.
205
+ * Every other card offers the one rule its call's form mints ({@link cardRuleFormOf}: the shell card's
206
+ * command rules, a name or host tool's exact value rule, the whole-tool rule of a call no rule word speaks
207
+ * for) or names why it offers none (`lane_cannot_speak`: a compound, a redirection, a substitution, a path
208
+ * tool, a shell-face sibling, a tool answering to one of those under an alias).
209
209
  *
210
210
  * Empty, too, on any ask a persisted rule could never CLEAR — "allow rules silence the classifier's
211
211
  * questions, never a mandated one". Two doors, matching the two halves of that boundary:
@@ -270,7 +270,7 @@ export declare function createRuleOffersOf(cfg: {
270
270
  current: string;
271
271
  } | undefined;
272
272
  deniesDirectoryRead: ((directory: string) => boolean) | undefined;
273
- }): (call: Pick<ToolCallLike, "toolName" | "face">, args: unknown, ask: RuleOffersAskFacts | undefined) => {
273
+ }): (call: Pick<ToolCallRequest, "toolName" | "face">, args: unknown, ask: RuleOffersAskFacts | undefined) => {
274
274
  ruleOffers?: readonly RuleOffer[];
275
275
  ruleOffersAbsence?: RuleOffersAbsence;
276
276
  execCwd?: string;
@@ -1,17 +1,18 @@
1
1
  import { persistedRuleMandateOf, ruleClearanceOf } from "../ask-origin.js";
2
2
  import {} from "../hooks.js";
3
- import { adjudicatePersistedDomainRules, adjudicatePersistedNameRules, adjudicatePersistedPathRules, adjudicatePersistedRules, callPathFaceOf, escapeForDisclosure, faceRowsSpeakFor, laneToolNamesOf, nameRuleSubjectOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForHost, suggestRuleForName, suggestRulesForCommand } from "../permission-rule-model.js";
3
+ import { adjudicatePersistedDomainRules, adjudicatePersistedNameRules, adjudicatePersistedPathRules, adjudicatePersistedRules, adjudicatePersistedToolRules, callPathFaceOf, cardRuleFormOf, eligiblePersisted, escapeForDisclosure, faceRowsSpeakFor, parseRuleText, laneToolNamesOf, nameRuleSubjectOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForHost, suggestRuleForName, suggestRuleForTool, suggestRulesForCommand, wholeToolRuleReaches } from "../permission-rule-model.js";
4
4
  import { callHostTargetOf } from "../host-target.js";
5
5
  import { callTargetViewsOf, validatePath } from "../effective-path-target.js";
6
6
  import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
7
7
  import { heldBy, seatOf, wireOf } from "../tool-name-readings.js";
8
8
  import { describeUnreadableOrgRows, readOrgRules, settleOrgVerdictWithin } from "../permission-rule-org.js";
9
9
  import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
10
+ import { routesContent } from "../tool-catalog-entries.js";
10
11
  import { ownerStoreOf } from "../permission-rule-provider.js";
12
+ import { readPrincipal } from "../principal-name.js";
11
13
  import { thrownText } from "../thrown-value.js";
12
14
  import { strongestAnswer } from "../persisted-rule-arms.js";
13
15
  import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
14
- export const PERSISTED_RULE_TOOL = "Bash";
15
16
  const NO_LEADS = new Map();
16
17
  export function inheritedAskRuleEvidence(provider) {
17
18
  const org = provider?.partitions.org === true ? "not_adjudicated" : "not_wired";
@@ -59,7 +60,7 @@ export function orgRuleLane(cfg) {
59
60
  return { status: "available" };
60
61
  let resolution;
61
62
  try {
62
- resolution = (await cfg.read(req.toolCallId)).org;
63
+ resolution = (await cfg.read(req.toolCallId, req.args)).org;
63
64
  }
64
65
  catch (err) {
65
66
  return { status: "unavailable", disclosures: [`the permission-rule store threw while resolving the org partition: ${thrownText(err)}`] };
@@ -106,10 +107,10 @@ export function createPermissionRuleLanes(cfg) {
106
107
  }
107
108
  if (provider === undefined)
108
109
  return { personal: undefined, org: undefined };
109
- const anonymous = cfg.principal === undefined || cfg.principal === "";
110
+ const anonymous = readPrincipal(cfg.principal).kind === "absent";
110
111
  const reader = cfg.rows;
111
- const orgViewOf = async (toolCallId) => {
112
- const read = await reader.pin(toolCallId);
112
+ const orgViewOf = async (toolCallId, judged) => {
113
+ const read = await reader.pin(toolCallId, judged);
113
114
  if (read.org === undefined)
114
115
  throw new Error(read.unreadable ?? "the store did not answer");
115
116
  return { org: read.org };
@@ -121,14 +122,26 @@ export function createPermissionRuleLanes(cfg) {
121
122
  const judged = laneToolNamesOf(req).map((n) => [n, ruleToolGrammarOf(n)]);
122
123
  if (!judged.some(([, g]) => g === "path") && faceRowsSpeakFor(callPathFaceOf(req)))
123
124
  judged.push([req.toolName, "path"]);
124
- const view = await reader.pin(req.toolCallId);
125
- if (judged.length === 0)
125
+ const view = await reader.pin(req.toolCallId, req.args);
126
+ const ownRow = (r) => judged.some(([n]) => n === r.tool) || wholeToolRuleReaches(r.tool, req);
127
+ if (judged.length === 0 && !view.rules.some(ownRow))
126
128
  return view.unreadable !== undefined ? { unreadable: true } : undefined;
127
129
  try {
128
- const table = view.rules.filter((r) => r.behavior !== "allow" || cfg.stripsAllow?.(r, req) !== true);
129
- const leads = judged.some(([, g]) => g === "path") ? await cfg.leads.resolve(bindPathRules(table.filter((r) => r.match === "path" || r.match === "subpath").map((r) => ({ parsed: r, ruleText: r.rule, rule: r })), (spec) => anchorsOf(spec.rule))) : NO_LEADS;
130
+ const stripped = view.rules.filter((r) => r.behavior !== "allow" || cfg.stripsAllow?.(r, req) !== true);
131
+ const judgesPath = judged.some(([, g]) => g === "path");
132
+ const noRule = [];
133
+ const table = stripped.filter((r) => {
134
+ const read = ownRow(r) || (judgesPath && (r.match === "path" || r.match === "subpath")) ? parseRuleText(r.rule, r.behavior) : undefined;
135
+ if (read !== undefined && "reject" in read)
136
+ noRule.push({ row: r, why: read.reject.message });
137
+ return read === undefined || !("reject" in read) || r.behavior !== "allow";
138
+ });
139
+ const leads = judgesPath ? await cfg.leads.resolve(bindPathRules(table.filter((r) => r.match === "path" || r.match === "subpath").map((r) => ({ parsed: r, ruleText: r.rule, rule: r })), (spec) => anchorsOf(spec.rule))) : NO_LEADS;
130
140
  const scopeIdentity = await projectScopeIdentity(table, cfg.rootCanonical, cfg.leads);
131
- const answer = strongestAnswer(judged.map(([n, grammar]) => judgeAgainst(table, { ...req, toolName: n }, grammar, leads, scopeIdentity)));
141
+ const wholeTool = persistedRuleAnswerOf(adjudicatePersistedToolRules(table, { request: req, cwd: cfg.root, sessionId: cfg.sessionId, ...(scopeIdentity !== undefined ? { scopeIdentity } : {}) }));
142
+ const judgedAnswer = strongestAnswer([...judged.map(([n, grammar]) => judgeAgainst(table, { ...req, toolName: n }, grammar, leads, scopeIdentity)), wholeTool]);
143
+ const unjudged = noRule.find(({ row }) => (row.behavior === "deny" || row.behavior === "ask") && eligiblePersisted(row, { tool: row.tool, cwd: cfg.root, sessionId: cfg.sessionId, ...(scopeIdentity !== undefined ? { scopeIdentity } : {}) }, row.behavior));
144
+ const answer = unjudged !== undefined && !(judgedAnswer !== undefined && "behavior" in judgedAnswer && judgedAnswer.behavior === "deny") ? unjudgedRowAnswer(unjudged.row, unjudged.why, req.toolName) : judgedAnswer;
132
145
  const stands = answer !== undefined && (("behavior" in answer && answer.behavior === "deny") || "unreadable" in answer);
133
146
  return view.unreadable !== undefined && !stands ? { unreadable: true } : answer;
134
147
  }
@@ -138,6 +151,11 @@ export function createPermissionRuleLanes(cfg) {
138
151
  return { unreadable: true, reason: why };
139
152
  }
140
153
  };
154
+ const unjudgedRowAnswer = (row, refusal, tool) => {
155
+ const why = `the stored ${row.behavior} rule ${escapeForDisclosure(row.rule)} is no rule this engine reads (${refusal})`;
156
+ cfg.onDisclosure(`the persisted rule lane could not judge a ${tool} call against the rows it read — ${why}`);
157
+ return { unreadable: true, reason: why };
158
+ };
141
159
  const anchorsOf = (rule) => persistedRowAnchors(rule.scope, cfg.chainTop);
142
160
  const judgeAgainst = (table, req, grammar, leads, scopeIdentity) => {
143
161
  const liveCwd = cfg.liveCwd();
@@ -164,11 +182,9 @@ export function createPermissionRuleLanes(cfg) {
164
182
  export function createRuleOffersOf(cfg) {
165
183
  return (call, args, ask) => {
166
184
  const toolName = wireOf("offer.laneTool", call);
167
- const grammar = ruleToolGrammarOf(toolName);
168
- if (!cfg.laneArmed || (toolName !== PERSISTED_RULE_TOOL && grammar !== "name" && grammar !== "domain"))
169
- return {};
170
- if ((cfg.principal === undefined || cfg.principal === "") && !cfg.localOwnerDeclared)
185
+ if (!cfg.laneArmed || (readPrincipal(cfg.principal).kind === "absent" && !cfg.localOwnerDeclared) || routesContent(call.face))
171
186
  return {};
187
+ const form = cardRuleFormOf(toolName, call);
172
188
  const tierSeat = seatOf("offer.marks", cfg.irreversibilityTier, call);
173
189
  const closedDoor = ruleClearanceOf(ask, {
174
190
  egress: heldBy("offer.marks.egress", cfg.egressTools, call),
@@ -177,11 +193,17 @@ export function createRuleOffersOf(cfg) {
177
193
  }).word;
178
194
  if (closedDoor !== undefined)
179
195
  return { ruleOffersAbsence: closedDoor };
180
- if (grammar === "name") {
196
+ if (form === "tool") {
197
+ const offer = suggestRuleForTool(toolName);
198
+ return offer !== undefined ? { ruleOffers: [offer] } : { ruleOffersAbsence: "lane_cannot_speak" };
199
+ }
200
+ if (form === undefined)
201
+ return { ruleOffersAbsence: "lane_cannot_speak" };
202
+ if (form === "name") {
181
203
  const offer = suggestRuleForName(toolName, nameRuleSubjectOf(toolName, args) ?? "");
182
204
  return offer !== undefined ? { ruleOffers: [offer] } : { ruleOffersAbsence: "lane_cannot_speak" };
183
205
  }
184
- if (grammar === "domain") {
206
+ if (form === "domain") {
185
207
  const host = callHostTargetOf({ toolName: call.toolName, args, face: call.face }, "grant");
186
208
  const offer = host !== undefined && "host" in host ? suggestRuleForHost(toolName, host.host) : undefined;
187
209
  return offer !== undefined ? { ruleOffers: [offer] } : { ruleOffersAbsence: "lane_cannot_speak" };
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * The person's persisted rules as ONE run reads them.
3
3
  *
4
- * ## One read per adjudicated call
4
+ * ## One read per judgment of a call's bytes
5
5
  *
6
- * A call is judged against ONE read of the store, pinned to its `toolCallId` ({@link PersonPathRows.pin}): whichever of the
7
- * call's consumers asks first — the read station's judgment, the org layer, the personal rule lane — takes the read, the
8
- * others are handed the same answer, and the tables the call's tool judges with when it executes are compiled from it
9
- * ({@link PathTablesFacade.forCall}). A call is an attempt the leg's occurrence table holds; the pin is released when the
6
+ * A call's bytes are judged against ONE read of the store, pinned to its `toolCallId` ({@link PersonPathRows.pin}): whichever
7
+ * of the consumers judging those bytes asks first — the read station's judgment, the org layer, the personal rule lane —
8
+ * takes the read, the others are handed the same answer, and the tables the call's tool judges with when it executes are
9
+ * compiled from it ({@link PathTablesFacade.forCall}). Judging other bytes of the same call (an approved edit) is another
10
+ * judgment: it takes a new read, which the call then holds — a rule written while a person decided binds their edit. A call is an attempt the leg's occurrence table holds; the pin is released when the
10
11
  * table no longer holds that attempt. A judgment that is no call of the leg (a visibility probe's fixed id, a gate with no
11
12
  * leg) keeps nothing: its rule lanes take a read of their own, its read station the run's latest tables.
12
13
  *
@@ -106,10 +107,12 @@ export interface PersonPathRows {
106
107
  readonly holdsPersonRows: boolean;
107
108
  /** The prepare read — the unit's first write. `undefined`, synchronously, when there is no person's rows to read. */
108
109
  prime(): Promise<void> | undefined;
109
- /** The rows of a call's one read — a call the leg's occurrence table holds: taken by its first consumer, handed to every
110
- * other until the table no longer holds that attempt; any other judgment (no leg, or an id the table does not hold — a
111
- * visibility probe) takes a read of its own, kept by nobody. Bounded ({@link PERSON_READ_BOUND_MS}); never rejects. */
112
- pin(toolCallId: string): Promise<PersonRowsRead>;
110
+ /** The rows of a call's one read of the bytes it judges (`judged`) — a call the leg's occurrence table holds: taken by its
111
+ * first consumer, handed to every other that judges the same bytes (or names none) until the table no longer holds that
112
+ * attempt; a judgment of OTHER bytes of the call (an approved edit, its outlet form) takes a new read, which the call then
113
+ * holds — a judgment reads the store, as the session rules' does. Any other judgment (no leg, or an id the table does not
114
+ * hold — a visibility probe) takes a read of its own, kept by nobody. Bounded ({@link PERSON_READ_BOUND_MS}); never rejects. */
115
+ pin(toolCallId: string, judged?: unknown): Promise<PersonRowsRead>;
113
116
  /** Settles once the tables a reader takes are compiled: a pinned call's own; any other reader's (no id, or a call holding
114
117
  * no pin), the unit's newest — through every change that lands meanwhile, within {@link PATH_TABLES_READY_BOUND_MS} —
115
118
  * or at `signal` or the run's cut ({@link PersonPathRowsConfig.signal}). Never rejects: a reader whose wait was cut
@@ -1,5 +1,7 @@
1
1
  import { ownerStoreOf } from "../permission-rule-provider.js";
2
+ import { readPrincipal } from "../principal-name.js";
2
3
  import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
4
+ import { canonicalize } from "../canonical-json.js";
3
5
  import { PATH_RULE_FACES, READ_RULE_TOOL, eligiblePersisted, noticeRuleText } from "../permission-rule-model.js";
4
6
  import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
5
7
  import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
@@ -21,7 +23,7 @@ const COMPILED_TABLES_KEPT = 8;
21
23
  export const EMPTY_READ_ROOTS = Object.assign(() => [], { forCall: () => () => [], ready: () => Promise.resolve() });
22
24
  export function createPersonPathRows(cfg) {
23
25
  const provider = cfg.provider;
24
- const anonymous = cfg.principal === undefined || cfg.principal === "";
26
+ const anonymous = readPrincipal(cfg.principal).kind === "absent";
25
27
  const holdsPersonRows = provider !== undefined && provider.partitions.durable && (!anonymous || cfg.localOwnerDeclared);
26
28
  const partitions = PARTITIONS.filter((p) => (p === "durable" ? provider?.partitions.durable === true : provider?.partitions.session === true));
27
29
  const unit = { durable: { failedSeq: 0 }, session: { failedSeq: 0 } };
@@ -186,20 +188,29 @@ export function createPersonPathRows(cfg) {
186
188
  pins.delete(toolCallId);
187
189
  return undefined;
188
190
  };
189
- const enter = (toolCallId, attempt) => {
191
+ const enter = (toolCallId, attempt, judged) => {
190
192
  const rows = readOnce("call").then((read) => (entry.read = read));
191
- const entry = { attempt, rows, tables: holdsPersonRows ? rows.then((read) => settleTables(entry, read)) : Promise.resolve(), ...(holdsPersonRows ? {} : { settled: {} }) };
193
+ const entry = { attempt, rows, tables: holdsPersonRows ? rows.then((read) => settleTables(entry, read)) : Promise.resolve(), ...(holdsPersonRows ? {} : { settled: {} }), ...(judged !== undefined ? { judged: { value: judged } } : {}) };
192
194
  pins.set(toolCallId, entry);
193
195
  return entry;
194
196
  };
195
- const pin = (toolCallId) => {
197
+ const serves = (p, judged) => {
198
+ if (judged === undefined || p.judged === undefined || p.judged.value === judged)
199
+ return true;
200
+ p.judged.canonical ??= canonicalize(p.judged.value);
201
+ return p.judged.canonical === canonicalize(judged);
202
+ };
203
+ const pin = (toolCallId, judged) => {
196
204
  for (const id of [...pins.keys()])
197
205
  pinOf(id);
198
206
  const known = pinOf(toolCallId);
199
- if (known !== undefined)
207
+ if (known !== undefined && serves(known, judged)) {
208
+ if (judged !== undefined)
209
+ known.judged ??= { value: judged };
200
210
  return known.rows;
211
+ }
201
212
  const attempt = cfg.occurrences?.get(toolCallId);
202
- return attempt === undefined ? readOnce("call") : enter(toolCallId, attempt).rows;
213
+ return attempt === undefined ? readOnce("call") : enter(toolCallId, attempt, judged).rows;
203
214
  };
204
215
  const settleTables = async (entry, read) => {
205
216
  try {
@@ -317,7 +328,7 @@ export function onPinnedRead(rows, stations) {
317
328
  const id = callOpts.toolCallId;
318
329
  if (readStationMemberOf(call, members) === undefined)
319
330
  return check.judge(call, args, cwd, signal, callOpts);
320
- const ready = id !== undefined && rows.holds(id) ? rows.pin(id).then(() => rows.tablesReady(id)) : rows.tablesReady(undefined, signal);
331
+ const ready = id !== undefined && rows.holds(id) ? rows.pin(id, args).then(() => rows.tablesReady(id)) : rows.tablesReady(undefined, signal);
321
332
  return ready.then(() => check.judge(call, args, cwd, signal, callOpts) ?? VOUCHED);
322
333
  },
323
334
  unanswered: (call, cause) => check.unanswered(call, cause),
@@ -24,6 +24,7 @@ import { type RecoveredOrphan } from "../session-reconcile.js";
24
24
  import type { TaskSpec, ToolEffect } from "../types.js";
25
25
  import type { PrepareResume } from "./contracts.js";
26
26
  import { type ResumeAtBranch } from "./branch-writeback.js";
27
+ import { type LegWrites } from "./session-write-ledger.js";
27
28
  export interface PrepareAcquireReconcileInput {
28
29
  /** borrowed-mutable (service port) — the deployment's session store. This phase's verbs on it are
29
30
  * the acquire/forget half of the session resource protocol: `acquire` per attempt, `forget` to
@@ -54,6 +55,8 @@ export interface PrepareAcquireReconcileInput {
54
55
  durableParkGap?: string;
55
56
  /** borrowed-readonly — the incident channel for this phase's own cleanup legs: a `resumeAt` branch it could not write back, a forget that did not settle. */
56
57
  report?: (err: Error, sessionId: string) => void;
58
+ /** borrowed-mutable (the Runner's) — this leg's view of the session write ledger, every storage write issued through it ({@link writesThroughLeg}); absent for a standalone prepare. */
59
+ legWrites?: LegWrites;
57
60
  }
58
61
  /** The phase's outputs (four-class form). All six are fresh bindings —
59
62
  * the driver destructures them into consts, so a consumer moved ahead of this call is a lexical