@sema-agent/core 7.52.0 → 7.54.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 (357) hide show
  1. package/CHANGELOG.md +470 -0
  2. package/LICENSE +3 -1
  3. package/README.md +3 -1
  4. package/dist/agents/cross-session-envelope.d.ts +8 -0
  5. package/dist/agents/cross-session-envelope.js +6 -0
  6. package/dist/agents/cross-session-judge.d.ts +21 -1
  7. package/dist/agents/cross-session-judge.js +8 -0
  8. package/dist/agents/cross-session-ref.d.ts +5 -0
  9. package/dist/agents/cross-session-ref.js +1 -0
  10. package/dist/agents/org-delivery-gate.d.ts +122 -0
  11. package/dist/agents/org-delivery-gate.js +69 -0
  12. package/dist/agents/peer-held-queue.d.ts +9 -4
  13. package/dist/agents/peer-held-queue.js +6 -5
  14. package/dist/agents/peer-session-drain.d.ts +35 -6
  15. package/dist/agents/peer-session-drain.js +271 -182
  16. package/dist/agents/read-inbox-tool.d.ts +7 -0
  17. package/dist/agents/read-inbox-tool.js +5 -1
  18. package/dist/agents/send-message-tool.js +3 -4
  19. package/dist/agents/subagent.d.ts +6 -5
  20. package/dist/agents/subagent.js +117 -83
  21. package/dist/agents/tool-filter.d.ts +25 -4
  22. package/dist/agents/tool-filter.js +13 -3
  23. package/dist/brain/anthropic.d.ts +6 -1
  24. package/dist/brain/anthropic.js +4 -4
  25. package/dist/brain/model-presets.js +2 -1
  26. package/dist/brain/route-adjudicator.d.ts +6 -0
  27. package/dist/brain/routing.d.ts +24 -1
  28. package/dist/brain/routing.js +54 -19
  29. package/dist/brain/timeout.js +5 -4
  30. package/dist/core/a2a.d.ts +5 -2
  31. package/dist/core/a2a.js +33 -14
  32. package/dist/core/allow-entry.d.ts +3 -1
  33. package/dist/core/allow-entry.js +3 -2
  34. package/dist/core/approval-object.d.ts +87 -0
  35. package/dist/core/approval-object.js +85 -0
  36. package/dist/core/arg-summary.d.ts +3 -2
  37. package/dist/core/arg-summary.js +17 -11
  38. package/dist/core/ask-origin.d.ts +43 -27
  39. package/dist/core/ask-origin.js +20 -11
  40. package/dist/core/ask-question.d.ts +0 -1
  41. package/dist/core/ask-question.js +4 -67
  42. package/dist/core/ask-unresolvable-notice.d.ts +3 -2
  43. package/dist/core/auto-compaction.d.ts +106 -50
  44. package/dist/core/auto-compaction.js +69 -53
  45. package/dist/core/auto-mode-prompt.d.ts +4 -2
  46. package/dist/core/auto-mode-prompt.js +4 -3
  47. package/dist/core/canonical-json.d.ts +69 -0
  48. package/dist/core/canonical-json.js +130 -8
  49. package/dist/core/checkpoint-requirements.d.ts +13 -7
  50. package/dist/core/checkpoint-requirements.js +4 -1
  51. package/dist/core/checkpoint-store.d.ts +60 -36
  52. package/dist/core/checkpoint-store.js +7 -6
  53. package/dist/core/child-clamp-seats.d.ts +45 -6
  54. package/dist/core/child-clamp-seats.js +56 -4
  55. package/dist/core/compliance.js +3 -1
  56. package/dist/core/consolidate-scope.d.ts +1 -1
  57. package/dist/core/cross-boundary-taint.d.ts +213 -0
  58. package/dist/core/cross-boundary-taint.js +196 -0
  59. package/dist/core/decision-receipt.d.ts +44 -1
  60. package/dist/core/decision-receipt.js +16 -0
  61. package/dist/core/effective-path-target.d.ts +28 -6
  62. package/dist/core/effective-path-target.js +28 -2
  63. package/dist/core/engine-notice.d.ts +47 -4
  64. package/dist/core/engine-notice.js +17 -1
  65. package/dist/core/engine-storage-backend.d.ts +73 -0
  66. package/dist/core/engine-storage-backend.js +1 -0
  67. package/dist/core/file-history-store.d.ts +6 -2
  68. package/dist/core/file-history-store.js +1 -0
  69. package/dist/core/gate-fold.js +13 -4
  70. package/dist/core/gate-lanes.js +41 -34
  71. package/dist/core/gate-outcome.d.ts +18 -9
  72. package/dist/core/governance-codes.d.ts +9 -2
  73. package/dist/core/governance-codes.js +9 -0
  74. package/dist/core/hands-band-screen.d.ts +7 -0
  75. package/dist/core/hands-band-screen.js +32 -0
  76. package/dist/core/hooks.d.ts +21 -19
  77. package/dist/core/hooks.js +6 -5
  78. package/dist/core/locked-config.d.ts +110 -31
  79. package/dist/core/locked-config.js +155 -42
  80. package/dist/core/lsp.d.ts +1 -1
  81. package/dist/core/mcp-probe.js +1 -1
  82. package/dist/core/mcp-server-spec.d.ts +152 -17
  83. package/dist/core/mcp-server-spec.js +223 -40
  84. package/dist/core/mcp.d.ts +21 -14
  85. package/dist/core/mcp.js +59 -54
  86. package/dist/core/memory-engine/engine.d.ts +14 -19
  87. package/dist/core/memory-engine/engine.js +9 -6
  88. package/dist/core/memory-engine/export-bundle.d.ts +1 -1
  89. package/dist/core/memory-engine/export-bundle.js +1 -1
  90. package/dist/core/memory-engine/file-backend.d.ts +2 -6
  91. package/dist/core/memory-engine/file-backend.js +1 -12
  92. package/dist/core/memory.d.ts +3 -3
  93. package/dist/core/mode-policy.d.ts +16 -26
  94. package/dist/core/mode-policy.js +5 -18
  95. package/dist/core/mods/kernel/dispatch.js +13 -15
  96. package/dist/core/mods/kernel/events.d.ts +9 -6
  97. package/dist/core/mods/kernel/events.js +2 -2
  98. package/dist/core/mods/kernel/receive.d.ts +41 -11
  99. package/dist/core/mods/kernel/receive.js +124 -54
  100. package/dist/core/mods/kernel/site-rules.d.ts +80 -15
  101. package/dist/core/mods/kernel/site-rules.js +232 -40
  102. package/dist/core/mods/stations.d.ts +0 -6
  103. package/dist/core/mods/stations.js +0 -7
  104. package/dist/core/mounted-mcp.d.ts +57 -0
  105. package/dist/core/mounted-mcp.js +71 -2
  106. package/dist/core/own-property-receipt.d.ts +13 -0
  107. package/dist/core/own-property-receipt.js +3 -1
  108. package/dist/core/parent-spec-seat.d.ts +12 -15
  109. package/dist/core/parent-spec-seat.js +14 -6
  110. package/dist/core/pause-registry.d.ts +35 -1
  111. package/dist/core/pause-registry.js +10 -0
  112. package/dist/core/permission-rule-consent.d.ts +18 -5
  113. package/dist/core/permission-rule-consent.js +13 -8
  114. package/dist/core/permission-rule-model.d.ts +87 -15
  115. package/dist/core/permission-rule-model.js +87 -8
  116. package/dist/core/permission-rule-org.d.ts +200 -33
  117. package/dist/core/permission-rule-org.js +217 -91
  118. package/dist/core/permission-rule-session.js +2 -2
  119. package/dist/core/permission-rule-store.d.ts +8 -0
  120. package/dist/core/permission-rule-store.js +1 -0
  121. package/dist/core/permission-rule-sync.js +3 -2
  122. package/dist/core/permission-rules.d.ts +7 -1
  123. package/dist/core/permission-rules.js +37 -8
  124. package/dist/core/read-only-shell.d.ts +1 -1
  125. package/dist/core/read-only-shell.js +30 -9
  126. package/dist/core/remote-env.d.ts +41 -1
  127. package/dist/core/remote-env.js +18 -0
  128. package/dist/core/resume-startup-cause.d.ts +18 -0
  129. package/dist/core/resume-startup-cause.js +12 -0
  130. package/dist/core/retention.js +7 -2
  131. package/dist/core/retired-keys.d.ts +8 -2
  132. package/dist/core/retired-keys.js +12 -1
  133. package/dist/core/roles.js +60 -10
  134. package/dist/core/rule-lane-reads.js +1 -1
  135. package/dist/core/runner/approval-feedback.d.ts +46 -0
  136. package/dist/core/runner/approval-feedback.js +41 -0
  137. package/dist/core/runner/compaction-call-options.d.ts +3 -3
  138. package/dist/core/runner/compaction-call-options.js +2 -2
  139. package/dist/core/runner/contracts.d.ts +34 -41
  140. package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
  141. package/dist/core/runner/cross-boundary-lane.js +190 -0
  142. package/dist/core/runner/decide-continuation.d.ts +4 -4
  143. package/dist/core/runner/decide-continuation.js +3 -1
  144. package/dist/core/runner/gate-exit.d.ts +32 -12
  145. package/dist/core/runner/gate-exit.js +77 -34
  146. package/dist/core/runner/inherited-ask-grants.js +2 -2
  147. package/dist/core/runner/mcp-redial.d.ts +52 -10
  148. package/dist/core/runner/mcp-redial.js +38 -9
  149. package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
  150. package/dist/core/runner/permission-rule-lanes.js +22 -10
  151. package/dist/core/runner/person-path-rows.d.ts +5 -3
  152. package/dist/core/runner/person-path-rows.js +2 -2
  153. package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -4
  154. package/dist/core/runner/prepare-acquire-reconcile.js +3 -3
  155. package/dist/core/runner/prepare-ask-lane.js +3 -3
  156. package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
  157. package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
  158. package/dist/core/runner/prepare-config-doors.d.ts +58 -15
  159. package/dist/core/runner/prepare-config-doors.js +167 -146
  160. package/dist/core/runner/prepare-context-lane.js +6 -3
  161. package/dist/core/runner/prepare-defer-classify.js +4 -7
  162. package/dist/core/runner/prepare-delegation-surface.d.ts +5 -4
  163. package/dist/core/runner/prepare-delegation-surface.js +8 -7
  164. package/dist/core/runner/prepare-execution-env.d.ts +1 -2
  165. package/dist/core/runner/prepare-execution-env.js +2 -1
  166. package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
  167. package/dist/core/runner/prepare-gate-stations.js +4 -3
  168. package/dist/core/runner/prepare-hands-readface.d.ts +3 -3
  169. package/dist/core/runner/prepare-hands-readface.js +3 -2
  170. package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
  171. package/dist/core/runner/prepare-inherited-gate.js +3 -2
  172. package/dist/core/runner/prepare-listings.d.ts +1 -1
  173. package/dist/core/runner/prepare-listings.js +4 -2
  174. package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
  175. package/dist/core/runner/prepare-memory-engine-session.js +19 -6
  176. package/dist/core/runner/prepare-memory.d.ts +3 -2
  177. package/dist/core/runner/prepare-memory.js +6 -2
  178. package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
  179. package/dist/core/runner/prepare-offload-wrappers.js +2 -1
  180. package/dist/core/runner/prepare-park-ask.d.ts +1 -1
  181. package/dist/core/runner/prepare-park-ask.js +9 -3
  182. package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
  183. package/dist/core/runner/prepare-policy-chain.js +14 -19
  184. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  185. package/dist/core/runner/prepare-project-context.js +6 -4
  186. package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
  187. package/dist/core/runner/prepare-prompt-assembly.js +20 -16
  188. package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
  189. package/dist/core/runner/prepare-prompt-inputs.js +6 -3
  190. package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
  191. package/dist/core/runner/prepare-protocol-tools.js +19 -12
  192. package/dist/core/runner/prepare-question-face.d.ts +4 -2
  193. package/dist/core/runner/prepare-question-face.js +4 -3
  194. package/dist/core/runner/prepare-safety-scan.d.ts +7 -2
  195. package/dist/core/runner/prepare-safety-scan.js +34 -27
  196. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
  197. package/dist/core/runner/prepare-suspend-saga.js +6 -2
  198. package/dist/core/runner/prepare-task.js +22 -19
  199. package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
  200. package/dist/core/runner/prepare-turn-wiring.js +14 -49
  201. package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -5
  202. package/dist/core/runner/prepare-wiring-manifest.js +9 -6
  203. package/dist/core/runner/prepare-workspace-restore.js +2 -2
  204. package/dist/core/runner/resume-admission.d.ts +4 -1
  205. package/dist/core/runner/resume-admission.js +16 -14
  206. package/dist/core/runner/resume-apply.js +4 -4
  207. package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
  208. package/dist/core/runner/resume-internals-and-config.js +3 -8
  209. package/dist/core/runner/resume-policy-outcome.d.ts +2 -2
  210. package/dist/core/runner/resume-policy-outcome.js +18 -15
  211. package/dist/core/runner/resume-preflight.js +2 -2
  212. package/dist/core/runner/resume-review-outcome.js +3 -3
  213. package/dist/core/runner/retired-runner-deps.d.ts +5 -6
  214. package/dist/core/runner/retired-runner-deps.js +1 -4
  215. package/dist/core/runner/run-leg.js +97 -43
  216. package/dist/core/runner/run-recovery-lanes.js +5 -3
  217. package/dist/core/runner/run-settle-and-teardown.js +2 -0
  218. package/dist/core/runner/run-terminal-adoption.js +21 -9
  219. package/dist/core/runner/run-turn-boundary.js +9 -3
  220. package/dist/core/runner/runtask.js +1 -1
  221. package/dist/core/runner/session-file-state-replay.d.ts +5 -2
  222. package/dist/core/runner/session-file-state-replay.js +3 -2
  223. package/dist/core/runner/skills-seat.d.ts +15 -0
  224. package/dist/core/runner/skills-seat.js +57 -0
  225. package/dist/core/runner/stream-steer-verb.js +2 -2
  226. package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
  227. package/dist/core/runner/sub-agent-deny-lists.js +7 -5
  228. package/dist/core/runner/synthetic-tools.d.ts +5 -2
  229. package/dist/core/runner/synthetic-tools.js +2 -12
  230. package/dist/core/runner/tool-face-overlay.d.ts +24 -6
  231. package/dist/core/runner/tool-face-overlay.js +24 -15
  232. package/dist/core/runner-deps.d.ts +37 -22
  233. package/dist/core/session-policy-store.d.ts +19 -0
  234. package/dist/core/session-policy-store.js +22 -2
  235. package/dist/core/session-store.d.ts +35 -16
  236. package/dist/core/session-store.js +20 -12
  237. package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
  238. package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
  239. package/dist/core/shell-wrapper-table.d.ts +5 -3
  240. package/dist/core/shell-wrapper-table.js +1 -1
  241. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
  242. package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
  243. package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
  244. package/dist/core/store-contracts/session-store-contract.js +639 -0
  245. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
  246. package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
  247. package/dist/core/task-event.d.ts +44 -6
  248. package/dist/core/task-notification.js +2 -2
  249. package/dist/core/task-registry-agent.js +1 -0
  250. package/dist/core/task-registry-shared.d.ts +5 -0
  251. package/dist/core/task-registry.d.ts +7 -0
  252. package/dist/core/task-registry.js +17 -0
  253. package/dist/core/task-result.d.ts +1 -1
  254. package/dist/core/task-spec.d.ts +7 -7
  255. package/dist/core/tighten-task-spec.d.ts +6 -4
  256. package/dist/core/tighten-task-spec.js +2 -2
  257. package/dist/core/tool-catalog-entries.d.ts +1 -1
  258. package/dist/core/tool-catalog-entries.js +52 -52
  259. package/dist/core/tool-face-reader.d.ts +49 -0
  260. package/dist/core/tool-face-reader.js +94 -0
  261. package/dist/core/tool-face.d.ts +17 -8
  262. package/dist/core/tool-face.js +1 -0
  263. package/dist/core/tool-policy.d.ts +67 -23
  264. package/dist/core/tool-policy.js +72 -60
  265. package/dist/core/tool-roster.d.ts +46 -28
  266. package/dist/core/tool-roster.js +91 -14
  267. package/dist/core/tool-spec-members.js +2 -2
  268. package/dist/core/tool-spec.d.ts +20 -4
  269. package/dist/core/tools.js +2 -0
  270. package/dist/core/trace.d.ts +24 -2
  271. package/dist/core/types.d.ts +3 -3
  272. package/dist/core/types.js +1 -1
  273. package/dist/core/untrusted-text.d.ts +23 -0
  274. package/dist/core/untrusted-text.js +10 -10
  275. package/dist/core/user-attachment.d.ts +56 -2
  276. package/dist/core/user-attachment.js +139 -56
  277. package/dist/core/wiring-manifest.d.ts +4 -2
  278. package/dist/engine/compaction/compaction.d.ts +2 -2
  279. package/dist/engine/compaction/utils.d.ts +2 -2
  280. package/dist/engine/harness/agent-harness.js +2 -1
  281. package/dist/engine/harness/types.d.ts +10 -4
  282. package/dist/engine/llm/types.d.ts +7 -2
  283. package/dist/engine/llm/types.js +2 -1
  284. package/dist/engine/loop/agent-loop.d.ts +12 -1
  285. package/dist/engine/loop/agent-loop.js +17 -5
  286. package/dist/engine/loop/types.d.ts +7 -1
  287. package/dist/engine/session/repo-utils.d.ts +5 -0
  288. package/dist/engine/session/repo-utils.js +9 -0
  289. package/dist/engine/session/storage-base.js +2 -2
  290. package/dist/index.d.ts +37 -17
  291. package/dist/index.js +31 -14
  292. package/dist/internal/harness.d.ts +1 -0
  293. package/dist/internal/harness.js +1 -0
  294. package/dist/internal/llm.d.ts +1 -1
  295. package/dist/internal/llm.js +1 -1
  296. package/dist/orchestration/goal.d.ts +24 -1
  297. package/dist/orchestration/goal.js +3 -2
  298. package/dist/orchestration/run-workflow-tool.d.ts +3 -0
  299. package/dist/orchestration/run-workflow-tool.js +1 -0
  300. package/dist/orchestration/workflow-governance.d.ts +1 -1
  301. package/dist/orchestration/workflow.d.ts +3 -0
  302. package/dist/orchestration/workflow.js +2 -1
  303. package/dist/prompt-assembly/artifact-store.d.ts +44 -9
  304. package/dist/prompt-assembly/artifact-store.js +38 -12
  305. package/dist/prompt-assembly/artifact.d.ts +12 -11
  306. package/dist/prompt-assembly/artifact.js +52 -21
  307. package/dist/prompt-assembly/assemble.d.ts +2 -0
  308. package/dist/prompt-assembly/assemble.js +2 -1
  309. package/dist/prompt-assembly/catalog/index.js +2 -1
  310. package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
  311. package/dist/prompt-assembly/catalog/memory.js +6 -0
  312. package/dist/prompt-assembly/catalog/schema.d.ts +19 -2
  313. package/dist/prompt-assembly/catalog/schema.js +3 -0
  314. package/dist/prompt-assembly/composer.d.ts +2 -0
  315. package/dist/prompt-assembly/composer.js +1 -1
  316. package/dist/prompt-assembly/overlay.d.ts +21 -11
  317. package/dist/prompt-assembly/overlay.js +57 -19
  318. package/dist/prompt-assembly/select.d.ts +21 -6
  319. package/dist/prompt-assembly/select.js +41 -4
  320. package/dist/prompts/default.d.ts +1 -12
  321. package/dist/prompts/default.js +0 -3
  322. package/dist/shell/lex.d.ts +87 -8
  323. package/dist/shell/lex.js +283 -49
  324. package/dist/stores/file/index.d.ts +8 -6
  325. package/dist/stores/file/permission-rule-store.d.ts +1 -1
  326. package/dist/stores/file/permission-rule-store.js +2 -1
  327. package/dist/tools/artifact/artifact-tool.js +14 -3
  328. package/dist/tools/fs/bash-program-position.d.ts +3 -15
  329. package/dist/tools/fs/bash-program-position.js +3 -10
  330. package/dist/tools/fs/bash-readonly-classifier.js +2 -0
  331. package/dist/tools/fs/fs-bash.d.ts +3 -3
  332. package/dist/tools/fs/fs-bash.js +72 -101
  333. package/dist/tools/fs/fs-read.d.ts +12 -0
  334. package/dist/tools/fs/fs-read.js +10 -9
  335. package/dist/tools/fs/fs-shared.d.ts +27 -31
  336. package/dist/tools/fs/fs-shared.js +12 -15
  337. package/dist/tools/fs/fs-write.js +1 -1
  338. package/dist/tools/fs/index.js +2 -2
  339. package/dist/tools/fs/read-deny-home.d.ts +1 -1
  340. package/dist/tools/fs/read-deny-home.js +5 -4
  341. package/dist/tools/fs/read-deny.d.ts +16 -3
  342. package/dist/tools/fs/read-deny.js +12 -9
  343. package/dist/tools/fs/safety.d.ts +7 -6
  344. package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
  345. package/dist/tools/fs/shell-read-bindings.js +15 -16
  346. package/dist/tools/fs/shell-read-device.js +1 -1
  347. package/dist/tools/fs/shell-read-plan.js +36 -47
  348. package/dist/tools/fs/shell-read-word.d.ts +1 -6
  349. package/dist/tools/fs/shell-read-word.js +2 -20
  350. package/dist/tools/fs/shell-reentry-text.js +1 -1
  351. package/dist/tools/web.d.ts +2 -2
  352. package/package.json +4 -1
  353. package/test/export-surface.snapshot.json +5651 -5457
  354. package/wire-changes/core@7.53.0.json +1968 -0
  355. package/wire-changes/core@7.54.0.json +2870 -0
  356. package/dist/core/shell-scan.d.ts +0 -60
  357. package/dist/core/shell-scan.js +0 -183
@@ -1,4 +1,5 @@
1
- import type { PermissionResult } from "./tool-policy.js";
1
+ import { type AssertAllKeysHandled } from "./ask-origin.js";
2
+ import type { DecisionMember, PermissionResult } from "./tool-policy.js";
2
3
  export declare const refusedOutOfContract: (message: string) => PermissionResult;
3
4
  /** Is `d` a refusal the engine itself minted (by object identity — a look-alike `deny` a caller built is not one)? The receipt's
4
5
  * answer is one of two things (the engine's refusal or its detached copy), and this tells them apart. */
@@ -12,6 +13,48 @@ export declare const isEngineRefusal: (d: unknown) => boolean;
12
13
  * two entry-list members) — one word per author, so "who said this needs no asking" is read off the decision. */
13
14
  declare const DECISION_REASONS: readonly ["rule", "mode", "hook", "safety", "classifier", "persisted_rule", "sandbox", "org_rule", "org_unavailable", "read_only", "skill", "deployment_rule", "request_rule"];
14
15
  export type DecisionReason = (typeof DECISION_REASONS)[number];
16
+ /** The arms of the decision union that declare member `M`. */
17
+ type ArmOf<M extends DecisionMember> = PermissionResult extends infer U ? (U extends {
18
+ action: infer A;
19
+ } ? (M extends keyof U ? A : never) : never) : never;
20
+ /**
21
+ * Every member of the decision union with the ARMS that declare it — the one table the receipt's member list is read off
22
+ * (`DECISION_MEMBERS`), fenced to the union both ways: `satisfies` admits no member and no arm the union lacks, and
23
+ * {@link DecisionMemberArmsCoverTheUnion} no arm the union has and a row lacks. A present member on an arm outside its row is
24
+ * out of contract like a word outside a closed set ({@link screenContractValues}): an ask's mark on an allow or a deny is a
25
+ * decision that contradicts itself, and a consumer branching on the member would read the other arm's meaning.
26
+ */
27
+ export declare const DECISION_MEMBER_ARMS: Readonly<{
28
+ readonly action: readonly ["allow", "ask", "deny"];
29
+ readonly updatedInput: readonly ["allow", "ask", "deny"];
30
+ readonly message: readonly ["allow", "ask", "deny"];
31
+ readonly decisionReason: readonly ["allow", "ask", "deny"];
32
+ readonly attestation: readonly ["allow", "ask", "deny"];
33
+ readonly requiresRealApproval: readonly ["ask"];
34
+ readonly denialLimitFallback: readonly ["ask"];
35
+ readonly classifierNoRuling: readonly ["ask"];
36
+ readonly allowRuleShadowed: readonly ["ask"];
37
+ readonly probeReason: readonly ["ask"];
38
+ readonly probeCause: readonly ["ask"];
39
+ readonly probeMandated: readonly ["ask"];
40
+ readonly crossBoundaryMandated: readonly ["ask"];
41
+ readonly mandated: readonly ["ask"];
42
+ readonly mandate: readonly ["ask"];
43
+ readonly probeReadRootCandidate: readonly ["ask"];
44
+ readonly matchedAskRule: readonly ["ask"];
45
+ readonly ruleEvidence: readonly ["ask"];
46
+ readonly segmentCoverage: readonly ["ask"];
47
+ readonly origin: readonly ["ask"];
48
+ readonly askClass: readonly ["ask"];
49
+ readonly classes: readonly ["ask"];
50
+ readonly guards: readonly ["ask"];
51
+ readonly ruleUnreadable: readonly ["ask"];
52
+ readonly authoredBy: readonly ["ask"];
53
+ readonly foldedAskClasses: readonly ["ask"];
54
+ }>;
55
+ export type DecisionMemberArmsCoverTheUnion = AssertAllKeysHandled<{
56
+ [M in DecisionMember]: Exclude<ArmOf<M>, (typeof DECISION_MEMBER_ARMS)[M][number]>;
57
+ }[DecisionMember]>;
15
58
  export declare const RETIRED_WINDOW_MEMBER_DENY_MESSAGE: string;
16
59
  export declare const carriesRetiredWindow: (fallback: unknown) => boolean;
17
60
  /**
@@ -32,8 +32,17 @@ const CLOSED_MEMBERS = [
32
32
  { member: "classifierNoRuling", words: AUTO_MODE_FALLBACK_CAUSES, has: isAutoModeFallbackCause },
33
33
  { member: "requiresRealApproval", words: [true, false], has: (v) => v === true || v === false },
34
34
  { member: "probeMandated", words: [true], has: (v) => v === true },
35
+ { member: "crossBoundaryMandated", words: [true], has: (v) => v === true },
35
36
  { member: "mandated", words: [true], has: (v) => v === true },
36
37
  ];
38
+ const EVERY_ARM = ["allow", "ask", "deny"];
39
+ const ASK = ["ask"];
40
+ export const DECISION_MEMBER_ARMS = Object.freeze({
41
+ action: EVERY_ARM, updatedInput: EVERY_ARM, message: EVERY_ARM, decisionReason: EVERY_ARM, attestation: EVERY_ARM,
42
+ requiresRealApproval: ASK, denialLimitFallback: ASK, classifierNoRuling: ASK, allowRuleShadowed: ASK, probeReason: ASK, probeCause: ASK,
43
+ probeMandated: ASK, crossBoundaryMandated: ASK, mandated: ASK, mandate: ASK, probeReadRootCandidate: ASK, matchedAskRule: ASK, ruleEvidence: ASK,
44
+ segmentCoverage: ASK, origin: ASK, askClass: ASK, classes: ASK, guards: ASK, ruleUnreadable: ASK, authoredBy: ASK, foldedAskClasses: ASK,
45
+ });
37
46
  const RETIRED_WINDOW_MEMBER = "autoDenyAfterMs";
38
47
  export const RETIRED_WINDOW_MEMBER_DENY_MESSAGE = `a permission decision's "denialLimitFallback" carries "${RETIRED_WINDOW_MEMBER}" — the decision names the counts only; ` +
39
48
  `the auto-deny window is decided by the ask's route (a live approver arms it, every other route runs none) and cannot be ` +
@@ -91,6 +100,13 @@ export function screenContractValues(contract, opts) {
91
100
  continue;
92
101
  return unrecognized(member, v, words);
93
102
  }
103
+ const arm = contract.action;
104
+ for (const [member, arms] of Object.entries(DECISION_MEMBER_ARMS)) {
105
+ if (contract[member] === undefined || (arm === undefined ? EVERY_ARM.every((a) => arms.includes(a)) : arms.includes(arm)))
106
+ continue;
107
+ return refusedOutOfContract(`a permission decision carries "${member}" on ${arm === undefined ? "a result with no action (no opinion)" : `its "${arm}" arm`} — the member belongs to ` +
108
+ `the ${arms.join("/")} arm only; denied fail-closed rather than letting consumers branch on a member its arm does not hold`);
109
+ }
94
110
  if (carriesRetiredWindow(contract.denialLimitFallback))
95
111
  return refusedOutOfContract(RETIRED_WINDOW_MEMBER_DENY_MESSAGE);
96
112
  return undefined;
@@ -245,17 +245,39 @@ export declare function readCallTarget(env: ExecutionEnv | undefined, call: Tool
245
245
  readonly root?: string | undefined;
246
246
  readonly cwd?: string | undefined;
247
247
  }, signal?: AbortSignal, leads?: ReadonlyMap<string, string | undefined>): Promise<CallTargetReading>;
248
+ /** The gate's entry records the loop's reading of this call's arguments against the face it stamps on the call. */
249
+ export declare function stampArgsReading(face: object, read: (args: unknown) => unknown): void;
248
250
  /**
249
- * `req` with its arguments replaced by `args` and — when `req` carries a reading of its own args — a reading of the NEW
250
- * args taken the same way (same environment, same root / cwd, same face). Every seat that rewrites a request's arguments
251
- * and hands it on (a composition's rewrite cascade, a re-judgment of the final or an edited form) goes through here, so the
252
- * next layer never judges a request its reading does not describe. `signal` is the CURRENT call's: a reading never carries
253
- * one call's cancellation into another's. `undefined` ⇒ nothing to re-read: use `{ ...req, args }`.
251
+ * `args` as the called tool will run them — its argument preparation and schema coercions, the reading the loop gave the
252
+ * model's call — for the call `req` belongs to; `args` itself when nothing was stamped (a bare request outside a Runner:
253
+ * a host judging a call reads it the tool's way first). Every seat that adopts a rewrite (a hook's, a policy's, a
254
+ * person's edit) takes it through here before anything judges it, so what is judged is what runs. A value the reading
255
+ * leaves as it is comes back as ITSELF (the same object: a target reading taken for it still describes it).
256
+ */
257
+ export declare function argsAsTheToolReads(req: {
258
+ readonly face?: object | undefined;
259
+ }, args: unknown): unknown;
260
+ /** `args` through one call's reading `read` (the face-less seat: the resumed call reads through its live tool): ITSELF when the
261
+ * reading leaves its bytes as they are, else the reading's value, recorded as read from `args`. */
262
+ export declare function argsReadBy(read: (args: unknown) => unknown, args: unknown): unknown;
263
+ /** The value `args` was read from, when a reading moved it; otherwise `args` itself. */
264
+ export declare function argsReadFrom(args: unknown): unknown;
265
+ /** `copy` is a detached copy of `original` (the decision receipt's): the same bytes, so read from what `original` was read
266
+ * from — the recorded step survives the copy instead of being lost to the copy's new identity. */
267
+ export declare function carryArgsReadFrom(original: unknown, copy: unknown): void;
268
+ /**
269
+ * `req` with its arguments replaced by `args` AS THE TOOL READS THEM ({@link argsAsTheToolReads}) and — when `req` carries
270
+ * a reading of its own target — a reading of the NEW args taken the same way (same environment, same root / cwd, same
271
+ * face). Every seat that rewrites a request's arguments and hands it on (a composition's rewrite cascade, a re-judgment of
272
+ * the final or an edited form) goes through here, so the next layer never judges a request its reading does not describe
273
+ * nor arguments the tool would run differently. `signal` is the CURRENT call's: a reading never carries one call's
274
+ * cancellation into another's.
254
275
  */
255
276
  export declare function withArgsReading<R extends {
256
277
  readonly args: unknown;
257
278
  readonly targetReading?: CallTargetReading | undefined;
258
- }>(req: R, args: unknown, signal: AbortSignal | undefined): Promise<R> | undefined;
279
+ readonly face?: object | undefined;
280
+ }>(req: R, args: unknown, signal: AbortSignal | undefined): Promise<R>;
259
281
  /** The request's target reading, when it describes the request's own `args` (the object it was taken for); otherwise
260
282
  * nothing — a rule lane then judges the lexical view it always judged. Every rule lane reads the reading through here. */
261
283
  export declare function callTargetReadingOf(req: {
@@ -3,6 +3,7 @@ import { declaredPathTargetOf, pathTargetValue, protectivePathTargetOf } from ".
3
3
  import { symlinkChainHops } from "./path-identity.js";
4
4
  import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, withGateTimeout } from "./gate-bounded-await.js";
5
5
  import { thrownText } from "./thrown-value.js";
6
+ import { canonicalize } from "./canonical-json.js";
6
7
  export function pathTargetBaseOf(pt, bases) {
7
8
  return (pt?.base ?? "cwd") === "root" ? bases.root : (bases.cwd ?? bases.root);
8
9
  }
@@ -146,10 +147,35 @@ export async function readCallTarget(env, call, signal, leads) {
146
147
  REREAD.set(reading, (args, rereadSignal) => readCallTarget(env, { ...call, args }, rereadSignal, leads));
147
148
  return reading;
148
149
  }
149
- export function withArgsReading(req, args, signal) {
150
+ const ARGS_READING = new WeakMap();
151
+ export function stampArgsReading(face, read) {
152
+ ARGS_READING.set(face, read);
153
+ }
154
+ export function argsAsTheToolReads(req, args) {
155
+ const read = req.face === undefined ? undefined : ARGS_READING.get(req.face);
156
+ return read === undefined ? args : argsReadBy(read, args);
157
+ }
158
+ export function argsReadBy(read, args) {
159
+ const out = read(args);
160
+ if (out === args || canonicalize(out) === canonicalize(args))
161
+ return args;
162
+ if (typeof out === "object" && out !== null)
163
+ READ_FROM.set(out, args);
164
+ return out;
165
+ }
166
+ const READ_FROM = new WeakMap();
167
+ export function argsReadFrom(args) {
168
+ return typeof args === "object" && args !== null && READ_FROM.has(args) ? READ_FROM.get(args) : args;
169
+ }
170
+ export function carryArgsReadFrom(original, copy) {
171
+ if (typeof original === "object" && original !== null && READ_FROM.has(original) && typeof copy === "object" && copy !== null)
172
+ READ_FROM.set(copy, READ_FROM.get(original));
173
+ }
174
+ export async function withArgsReading(req, args, signal) {
175
+ const read = argsAsTheToolReads(req, args);
150
176
  const reading = callTargetReadingOf(req);
151
177
  const reread = reading === undefined ? undefined : REREAD.get(reading);
152
- return reread === undefined ? undefined : reread(args, signal).then((targetReading) => ({ ...req, args, targetReading }));
178
+ return reread === undefined ? { ...req, args: read } : { ...req, args: read, targetReading: await reread(read, signal) };
153
179
  }
154
180
  function viewFrom(v) {
155
181
  if (v.lexical === undefined)
@@ -68,7 +68,7 @@ export interface EngineNotice {
68
68
  * as usual. Audience `user`; once per prepared task for the skill and request lists (`detail.sessionId` present), once
69
69
  * per Runner for the deployment's (at construction, no session). `detail: { sessionId?, entries: [{ source, entry,
70
70
  * reason, skill?, list? }] }` (`source` ∈ `skill` / `request` / `deployment`; `skill` and `list` only on a skill's;
71
- * `reason` ∈ {@link import("./allow-entry.js").ALLOW_ENTRY_INERT_REASONS}).
71
+ * `reason` ∈ {@link import("./allow-entry.js").ALLOW_ENTRY_INERT_REASONS}; `entry` is the entry as written — the message spells it — when it is at most `NOTICE_RULE_TEXT_MAX` (1 024) code points; a longer one is cut to 1 024 plus the truncation mark, 1 025 in all).
72
72
  * - `"mods.registration_refused"` — an extension registration was not mounted: a registration admitted before it
73
73
  * answered `{ refuse }` on `plugin.register`, or failed while judging it (fail-closed). The run goes on without it.
74
74
  * Audience `user` (the seat is in `detail`). Once per refused registration per run. `detail: { name, seat, tier,
@@ -399,6 +399,16 @@ export interface EngineNotice {
399
399
  * ({@link import("./secret-env.js").holdOwnCredentialValues}); `detail: { reason }` — the kind, never the value. Delivered
400
400
  * to `RunnerDeps.onNotice` from the `new Runner` receipt; the intakes with no structured seat (a brain's construction,
401
401
  * a request's route reading, a peer's dial, a directly built distiller seat) print its line on `console.warn`.
402
+ * - `"config.cross_boundary_posture_undeclared"` — a run whose tree executes on a target device (armed) on a deployment that
403
+ * configured no `RunnerDeps.crossBoundaryPosture`: the permissive `"single_user"` reading is in force. Once per run;
404
+ * `detail: { sessionId, runId, taint }` — `taint` lists the tree's taint bits
405
+ * ({@link import("./cross-boundary-taint.js").CROSS_BOUNDARY_TAINT_BITS}). Minted by
406
+ * {@link import("./cross-boundary-taint.js").crossBoundaryPostureUndeclaredNotice}. Audience `operator`.
407
+ * - `"config.cross_boundary_record_unwritten"` — a session's cross-boundary record the engine owed could not be written (the
408
+ * store threw, or answered with no entry id) and refused nothing: on a tree that is not governed no judgment reads the record;
409
+ * at a governed leg's prepare or settle the next leg reads the content after the newest record as off-target. Per failed write;
410
+ * `detail: { sessionId, runId, phase, governed, cause }` (`phase`: `prepare` / `exit` / `suspend` / `settle`). Minted by
411
+ * {@link import("./cross-boundary-taint.js").crossBoundaryRecordUnwrittenNotice}. Audience `operator`.
402
412
  * - `"config.permission_mode_unavailable"` — the permission mode a task requested is not the mode the leg runs
403
413
  * under: the deployment denies it (`by: "operator_deny"` — `RuntimeCaps.bypassPermissions` / `autoMode` is
404
414
  * `false`), a requested `auto` has no classifier face (`by: "face_absent"` — neither the leg's deps nor its chain carry
@@ -439,7 +449,18 @@ export interface EngineNotice {
439
449
  * complete absolute path (only a foreign writer or a hand edit makes one; every engine write face refuses it): they
440
450
  * take no part in this run's checks — a deny or ask row among them protects nothing — until they are rewritten or
441
451
  * removed. Once per run, by whichever read met them first. `detail: { rows, count, sessionId, runId }` — `rows`
442
- * the first eight `{ rule, scopeRoot }` (each bounded), `count` all of them. Audience `user`.
452
+ * the first eight `{ rule, scopeRoot }`, `count` all of them; `rule` is the rule text as the store served it, through the same reading
453
+ * as `permission.allowed_tool_not_granted`'s `entries[].entry` (the original, not a spelling of it, when it is at most 1 024 code points;
454
+ * a longer one is cut to 1 024 plus the truncation mark, 1 025 in all), `scopeRoot` neutralized and bounded. Audience `user`.
455
+ * - `"config.session_rules_earlier_grammar"` — a session's stored permission rules (`RunnerDeps.sessionPolicyStore`) hold
456
+ * entries the rule grammar they were written in reads as nothing: an allow entry admits nothing, and a `toolDeny` entry
457
+ * this engine's grammar reads as no rule makes those rules refuse every call until they are rewritten. The message names
458
+ * the session, the bucket, the entries (the first five, the rest counted), the reader's word and the way out. Audience
459
+ * `user`. Once per distinct entry set per bucket per rule layer (the run's own rules, each ancestor's inherited ones).
460
+ * `detail: { bucket, storedGrammar, entries: [{ entry, reader }], rewrite: { by, stampsGrammar }, rulesSessionId,
461
+ * sessionId }` — `entries` every one, `entry` through the same reading as `permission.allowed_tool_not_granted`'s;
462
+ * `reader` ∈ `no_rule` / `named_no_tool`; `rewrite.by` = `operator_write_without_rev`; `rulesSessionId` the session
463
+ * whose rules they are (an ancestor's on a delegated child), `sessionId` the run that read them.
443
464
  * - `"config.durable_gate_unavailable"` — a durable-approval MANDATE is in force on a leg with NO
444
465
  * checkpoint store: the per-principal `forceDurableGate` entitlement, or the task's own
445
466
  * `durableApproval` opt-in with no live approver seat (`durableMandateSourceOf`, the predicate the
@@ -466,7 +487,13 @@ export interface EngineNotice {
466
487
  * frame's two count keys). They are NOT redelivered (a steer aimed at a finished run must not
467
488
  * fire at the next one — unlike ENGINE notes, which pend per session); the loud half of the
468
489
  * #257 contract's "accepted = enqueued, not consumed" sentence;
469
- * `detail: { <countKey>, cause, taskId?, sessionId?, runId? }` (the `code` and the `countKey` of each family are the
490
+ * The words a person wrote on an approval card (a durable `policy_ask` allow's `feedback`) that did not reach the
491
+ * model are counted in the SAME account under the steer code — one more row of that family, `detail.kind:
492
+ * "approval_feedback"` (absent on every other row) — when the message carrying them never committed and the row did not come back
493
+ * (a returned row does not carry them: its next decide is fresh); when the pause tried to carry them forward and the parked-steer
494
+ * carrier refused, the row also names the store's refusal as `detail.carry` (`steering.invalid_content` | `steering.queue_full`) and
495
+ * its sentence says so (`cause` stays `leg_end`: the leg ended with them owed — the closed set has no carrier words);
496
+ * `detail: { <countKey>, cause, kind?, carry?, taskId?, sessionId?, runId? }` (the `code` and the `countKey` of each family are the
470
497
  * table {@link USER_INPUT_UNDRAINED_FAMILIES}, keyed by the lane the input was queued on — `injectionLaneOf` — and the
471
498
  * minter writes exactly those; `cause` on both rows, closed `USER_INPUT_UNDRAINED_CAUSES`: `"leg_end"` — this engine's arm, the
472
499
  * leg ended with it still owed; `"crashed"` — minted by the SERVING LAYER, not this engine, before the
@@ -863,7 +890,8 @@ export interface EngineNotice {
863
890
  * toolCallId, cause, parkLaneExisted, remedy? }` — `parkLaneExisted` says whether a park lane was armed and
864
891
  * declined/failed (`true`) or none existed (`false`, the usual shape); `remedy` (the two causes that say nobody
865
892
  * could be asked — `approver_unavailable` and `no_approver_wired`; never `mandate_unreconstructible`)
866
- * is the one sentence naming the caller-side change that would have let the call run, as the tool text carries it. Audience `"user"`: the refused
893
+ * is the one sentence naming the caller-side change that would have let the call run, as the tool text carries it (for arguments
894
+ * no approval can be bound to, the binding's own sentence: the remedy's approver half would not let them run). Audience `"user"`: the refused
867
895
  * work is the session user's own; the operator hears it through the sink like every code.
868
896
  *
869
897
  * - `"memory.consolidation_recommended"` (design/339 §2.2/§6.2) — the engine-minted per-scope
@@ -1004,6 +1032,16 @@ export interface OwedUserInputAccount {
1004
1032
  seq?: number;
1005
1033
  }>;
1006
1034
  carried: number;
1035
+ /** The approving person's words (a `policy_ask` allow's `feedback`), counted APART from `frames`: owed, and how many the migration carried.
1036
+ * They are on no row of their own, and a returned row (the compensation reopened it) does not carry them either — its next decide is a
1037
+ * fresh one the host may re-supply them on — so, unlike the caller's own frames, they are not announced when the row came back (the row is
1038
+ * pending again and the person is asked again). `refusal`: the code the parked-steer carrier answered when the pause tried to carry them
1039
+ * (`steering.invalid_content` — over its bound; `steering.queue_full`), the store's own words; absent when no carry was refused. */
1040
+ approval?: {
1041
+ owed: number;
1042
+ carried: number;
1043
+ refusal?: ApprovalCarryRefusal;
1044
+ };
1007
1045
  };
1008
1046
  }
1009
1047
  /**
@@ -1058,6 +1096,9 @@ export declare function settleOwedUserInput(account: OwedUserInputAccount, rowRe
1058
1096
  */
1059
1097
  export declare const USER_INPUT_UNDRAINED_CAUSES: readonly ["leg_end", "crashed"];
1060
1098
  export type UserInputUndrainedCause = (typeof USER_INPUT_UNDRAINED_CAUSES)[number];
1099
+ /** What the parked-steer carrier answered (the checkpoint store's own refusal codes, not a set of this notice) when a pause tried to carry an
1100
+ * approving person's words: refused as content / over its bound, or its queue was full. `detail.carry` of the approval row, absent otherwise. */
1101
+ export type ApprovalCarryRefusal = "steering.invalid_content" | "steering.queue_full";
1061
1102
  /**
1062
1103
  * The two families of the stranded-input account, keyed by the lane the input was queued on (`injectionLaneOf` maps a steer's
1063
1104
  * tier to it): the notice `code` the family is announced under, and the `detail` key that carries its count
@@ -1102,5 +1143,7 @@ export declare function parentLegIds(internals: {
1102
1143
  export declare function undrainedUserInputNotices(counts: {
1103
1144
  steer: number;
1104
1145
  followUp: number;
1146
+ approvalFeedback?: number;
1147
+ approvalCarry?: ApprovalCarryRefusal;
1105
1148
  }, taskId?: string, sessionId?: string, runId?: string, parent?: ParentLegIds): EngineNotice[];
1106
1149
  export {};
@@ -44,7 +44,10 @@ export function settleOwedUserInput(account, rowReturned, ids, onNotice) {
44
44
  SETTLED_OWED_ACCOUNTS.add(account);
45
45
  try {
46
46
  const owed = owedUserInputCounts(account.held, account.migrated, { ...account.continuation, rowReturned });
47
- for (const notice of undrainedUserInputNotices(owed, ids.taskId, ids.sessionId, ids.runId, ids.parent))
47
+ const approval = account.continuation.approval;
48
+ const approvalFeedback = approval === undefined || rowReturned ? 0 : Math.max(0, approval.owed - approval.carried);
49
+ const approvalCarry = approvalFeedback > 0 ? approval?.refusal : undefined;
50
+ for (const notice of undrainedUserInputNotices({ ...owed, ...(approvalFeedback > 0 ? { approvalFeedback } : {}), ...(approvalCarry !== undefined ? { approvalCarry } : {}) }, ids.taskId, ids.sessionId, ids.runId, ids.parent))
48
51
  deliverEngineNotice(onNotice, notice);
49
52
  }
50
53
  catch {
@@ -84,6 +87,19 @@ export function undrainedUserInputNotices(counts, taskId, sessionId, runId, pare
84
87
  if (toParent)
85
88
  copies.push({ code: STEER_UNDRAINED, message: `${message}${copyTail}`, detail: { ...count, ...ptid, sessionId: parentSession, childSessionId: sessionId } });
86
89
  }
90
+ const approvalFeedback = counts.approvalFeedback ?? 0;
91
+ if (approvalFeedback > 0) {
92
+ const why = counts.approvalCarry === "steering.invalid_content"
93
+ ? "the leg paused with them still owed and the parked-steer carrier refused them (longer than a parked steer may be)"
94
+ : counts.approvalCarry === "steering.queue_full"
95
+ ? "the leg paused with them still owed and the parked-steer carrier refused them (its queue was full)"
96
+ : "the leg ended before the message carrying them was committed";
97
+ const message = `${approvalFeedback} approval-card feedback(s) (the words a person wrote when approving a tool call) never reached the model — ${why}. They are NOT redelivered; re-send if still wanted.`;
98
+ const count = { [USER_INPUT_UNDRAINED_FAMILIES.steer.countKey]: approvalFeedback, cause, kind: "approval_feedback", ...(counts.approvalCarry !== undefined ? { carry: counts.approvalCarry } : {}) };
99
+ own.push({ code: STEER_UNDRAINED, message, detail: { ...count, ...tid, ...sid, ...rid } });
100
+ if (toParent)
101
+ copies.push({ code: STEER_UNDRAINED, message: `${message}${copyTail}`, detail: { ...count, ...ptid, sessionId: parentSession, childSessionId: sessionId } });
102
+ }
87
103
  if (counts.followUp > 0) {
88
104
  const message = `${counts.followUp} user follow-up(s) ${tail}`;
89
105
  const count = { [USER_INPUT_UNDRAINED_FAMILIES.followUp.countKey]: counts.followUp, cause };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The engine's storage seats as ONE backend contract.
3
+ *
4
+ * A backend is the thing a deployment constructs over one storage — a data root, a database — and whose
5
+ * members it wires into {@link import("./runner-deps.js").RunnerDeps}. Before this contract the shape of
6
+ * "a backend" was whatever the file family's aggregate class happened to carry, so a second backend had
7
+ * nothing to implement and a host composing several had nothing to type its engine half against. The
8
+ * shape now lives here, once: the bundled file backend implements it, and so does any other.
9
+ *
10
+ * Membership rule: a member is an engine seat whose durable store lives in the backend's storage and that
11
+ * every backend supplies, the bundled file backend included — `retention` is the one optional member,
12
+ * and a seat the file backend does not supply is not a member. Each member's NAME is its `RunnerDeps` seat and its TYPE is
13
+ * that seat's store type, so each member wires into its seat as it is (name the seats; a backend object may carry
14
+ * members that are not seats, so it is not spread). The member-to-seat correspondence is pinned in both directions
15
+ * by the test suite, together with the classification of the store-shaped seats that are not members. One reading per member:
16
+ * - `fileHistoryStore` is the store only. The seat also accepts the word `"disabled"`; that word is the
17
+ * deployment's wiring decision, not something a storage supplies.
18
+ *
19
+ * A host's own product stores do not belong here: a host that keeps more stores beside these composes
20
+ * an `EngineStorageBackend` with its own, rather than widening this contract.
21
+ */
22
+ import type { SessionStore } from "./session.js";
23
+ import type { CheckpointStore } from "./checkpoint-store.js";
24
+ import type { ToolResultStore } from "./tool-result-store.js";
25
+ import type { SessionPolicyStore } from "./session-policy-store.js";
26
+ import type { WorkflowJournalStore } from "./workflow-journal-store.js";
27
+ import type { UsageWindowStore } from "./usage-window-store.js";
28
+ import type { BackgroundAgentStore } from "./background-agent-store.js";
29
+ import type { MailboxStore } from "./mailbox-store.js";
30
+ import type { FileHistoryStore } from "./file-history-store.js";
31
+ import type { ManagedRetentionCapability } from "./retention.js";
32
+ import type { RosterStore } from "../agents/roster-store.js";
33
+ export interface EngineStorageBackend {
34
+ /** `RunnerDeps.sessionStore`. */
35
+ readonly sessionStore: SessionStore;
36
+ /** `RunnerDeps.checkpointStore`. */
37
+ readonly checkpointStore: CheckpointStore;
38
+ /** `RunnerDeps.toolResultStore`. */
39
+ readonly toolResultStore: ToolResultStore;
40
+ /** `RunnerDeps.sessionPolicyStore`. */
41
+ readonly sessionPolicyStore: SessionPolicyStore;
42
+ /** `RunnerDeps.workflowJournalStore`. */
43
+ readonly workflowJournalStore: WorkflowJournalStore;
44
+ /** `RunnerDeps.usageWindowStore`. */
45
+ readonly usageWindowStore: UsageWindowStore;
46
+ /** `RunnerDeps.backgroundAgentStore` — and the same instance wherever a deployment-composed Agent tool
47
+ * takes its agent store (the seat's pairing rule). */
48
+ readonly backgroundAgentStore: BackgroundAgentStore;
49
+ /** `RunnerDeps.mailboxStore` (same single-instance pairing as `backgroundAgentStore`). */
50
+ readonly mailboxStore: MailboxStore;
51
+ /** `RunnerDeps.rosterStore`. */
52
+ readonly rosterStore: RosterStore;
53
+ /** `RunnerDeps.fileHistoryStore` — the store; the seat's `"disabled"` word is not a backend's to give. */
54
+ readonly fileHistoryStore: FileHistoryStore;
55
+ /**
56
+ * The backend-level managed-retention executor, when this backend has one.
57
+ *
58
+ * Retention execution belongs to the backend, not to each store: the capability's session deletion
59
+ * re-checks live references inside the deletion transaction, and those references are held by several
60
+ * stores at once (pending checkpoints, background-agent rows, workflow runs), so only a party that
61
+ * spans them can honor it. Absent ⇒ this backend executes no managed retention — the bundled file
62
+ * backend is one such backend.
63
+ *
64
+ * Not the same field as a store's `retention`: on a store that member is the declaration WORD
65
+ * ({@link import("./retention.js").RetentionDeclaring}, `"managed" | "none"`); here it is the
66
+ * executor OBJECT. A backend is not a store: handed to `assertRetentionCapability` as one, a backend
67
+ * that carries this member is refused as an unparseable declaration.
68
+ */
69
+ readonly retention?: ManagedRetentionCapability;
70
+ /** Release everything the backend holds (handles, locks) so a successor over the same storage can open
71
+ * it. Call it at shutdown. */
72
+ dispose(): Promise<void>;
73
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -75,6 +75,10 @@ export declare function trackKeyOf(root: string, absPath: string): string;
75
75
  * before; pass it and a foreign-family record is refused by name instead of coerced.
76
76
  */
77
77
  export declare function resolveTrackKey(root: string, key: string, minted?: FileHistoryTrackKeyDescriptor): FileHistoryTrackKeyResolution;
78
+ /** The file-history failure codes, spelled once: {@link FileHistoryError}'s `code` is derived from this tuple (each
79
+ * word is documented there). A host that recognizes a code in failure text, or keeps a code in a store, reads this
80
+ * value rather than keeping its own list. */
81
+ export declare const FILE_HISTORY_ERROR_CODES: readonly ["track_failed", "snapshot_failed", "restore_failed", "adopt_failed", "not_found", "aborted", "unsupported_format", "conflict"];
78
82
  export interface FileHistoryError {
79
83
  /** `not_found` = no boundary for that (scope, entryId); `aborted` = the signal fired (or the
80
84
  * fencing discarded a timed-out attempt); `conflict` = a non-destructive-target law refused
@@ -89,7 +93,7 @@ export interface FileHistoryError {
89
93
  * work its shape drove; a fault in the PUBLISH step itself — the disk refusing a good graph —
90
94
  * answers `adopt_failed`); `unsupported_format`
91
95
  * = a foreign-epoch or wrong-version envelope; the rest name the verb that failed. */
92
- code: "track_failed" | "snapshot_failed" | "restore_failed" | "adopt_failed" | "not_found" | "aborted" | "unsupported_format" | "conflict";
96
+ code: (typeof FILE_HISTORY_ERROR_CODES)[number];
93
97
  message: string;
94
98
  }
95
99
  export type FileHistoryResult = {
@@ -291,7 +295,7 @@ export interface FileHistoryStore {
291
295
  * discarding that baseline would be the fake-pristine hole DV-14 exists to weld shut.
292
296
  *
293
297
  * `verify` is the EVIDENCE arm (adversarial round 2). A caller whose write failed with a code the
294
- * FileSystem contract defines as "nothing was written" (`already_exists`, `precondition_failed`)
298
+ * FileSystem contract defines as "nothing was written" (`already_exists`, `precondition_failed`, `target_unavailable`)
295
299
  * has proof and passes nothing. Every other failure — a plain write is not contractually atomic,
296
300
  * so ENOSPC, a dying transport or a thrown backend may have TRUNCATED the file before reporting —
297
301
  * must hand an `env` here: the store then re-reads the path ONCE and retracts only if the world
@@ -66,6 +66,7 @@ export function resolveTrackKey(root, key, minted) {
66
66
  return { ok: false, reason: `unsafe recorded key escapes root: ${key}` };
67
67
  return { ok: true, abs: joinUnder(root, keyN) };
68
68
  }
69
+ export const FILE_HISTORY_ERROR_CODES = Object.freeze(["track_failed", "snapshot_failed", "restore_failed", "adopt_failed", "not_found", "aborted", "unsupported_format", "conflict"]);
69
70
  export const FILE_HISTORY_DIFF_LINE_BUDGET = 2000;
70
71
  export function countRestoreLineDiff(current, target) {
71
72
  const isBinary = (b) => b !== undefined && b.includes(0);
@@ -8,6 +8,7 @@ import { createGateDeadline, awaitWriteProtectionJudgment, gateWaitFailureOf, wi
8
8
  import { shellReadFaceWords } from "../tools/fs/shell-read-face.js";
9
9
  import { failedJudgmentDeny } from "./runner/policy-throw.js";
10
10
  import { thrownText } from "./thrown-value.js";
11
+ import { argsAsTheToolReads } from "./effective-path-target.js";
11
12
  import { foldHookAsk, refuseOutOfContractDecision } from "./tool-policy.js";
12
13
  import { applyUnansweredSilence, readStationAnswer, silencedBy } from "./shell-read-silence.js";
13
14
  import { asksUnstated, permissionSeatAt, unstatedAskMessage } from "./mode-policy.js";
@@ -37,10 +38,10 @@ export async function runGateFold(pass) {
37
38
  pass.classifierRound = attributed;
38
39
  }
39
40
  if ((pass.decision.action === "allow" || pass.decision.action === "ask") && pass.decision.updatedInput !== undefined) {
40
- pass.policyRewrite = pass.decision.updatedInput;
41
+ pass.policyRewrite = argsAsTheToolReads(pass.req, pass.decision.updatedInput);
41
42
  }
42
43
  else if (pass.hookAsk?.updatedInput !== undefined) {
43
- pass.policyRewrite = pass.hookAsk.updatedInput;
44
+ pass.policyRewrite = argsAsTheToolReads(pass.req, pass.hookAsk.updatedInput);
44
45
  }
45
46
  if (pass.hookAsk !== undefined && pass.decision.action !== "deny") {
46
47
  pass.decision = foldHookAsk(pass.decision, pass.hookAsk);
@@ -164,8 +165,16 @@ export async function runGateFold(pass) {
164
165
  pass.deniedBy = "write_protection";
165
166
  }
166
167
  }
167
- if (asksUnstated(pass.decision, permissionSeatAt(input, pass.policyRewrite !== undefined ? pass.policyRewrite : pass.currentInput)?.seat, input.permissionMode?.())) {
168
- pass.decision = { action: "ask", message: permissionSeatAt(input, pass.policyRewrite !== undefined ? pass.policyRewrite : pass.currentInput)?.question ?? unstatedAskMessage(toolName), ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}) };
168
+ if (pass.decision.action !== "deny" && input.crossBoundaryCheck?.obligation({ toolName, args: pass.policyRewrite !== undefined ? pass.policyRewrite : pass.currentInput, toolCallId }).mandate === true) {
169
+ const marks = { requiresRealApproval: true, crossBoundaryMandated: true };
170
+ if (pass.decision.action === "ask")
171
+ pass.decision = { ...pass.decision, ...marks };
172
+ else
173
+ [pass.decision, pass.tightenedBy] = [{ action: "ask", message: `tool "${toolName}" crosses the device boundary of a run that executes on a target device — a person must confirm it`, decisionReason: "safety", ...marks }, "safety"];
174
+ }
175
+ const unstatedRuns = pass.policyRewrite !== undefined ? pass.policyRewrite : pass.currentInput;
176
+ if (asksUnstated(pass.decision, pass.req, unstatedRuns, permissionSeatAt(input, unstatedRuns)?.seat, input.permissionMode?.())) {
177
+ pass.decision = { action: "ask", message: permissionSeatAt(input, unstatedRuns)?.question ?? unstatedAskMessage(toolName), ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}) };
169
178
  pass.tightenedBy = "unstated";
170
179
  }
171
180
  return await pass.afterFold(pass);