@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
@@ -6,7 +6,7 @@ import { classicEventBase, failedHandlerClause, screenUserPrompt } from "../mods
6
6
  import { buildHumanInputEvent, frameMidTurnUserInput, projectHumanInput } from "../human-input-projection.js";
7
7
  import { injectionLaneOf, isSystemInjectionPriority, steerPriorityOf, SYSTEM_INJECTION_PRIORITIES } from "../task-notification.js";
8
8
  import { deliverEngineNotice } from "../types.js";
9
- import { inlineUntrusted, sanitizeUntrustedText } from "../untrusted-text.js";
9
+ import { inlineUntrusted, sanitizeUntrustedText, sentinelTagRefusal } from "../untrusted-text.js";
10
10
  import { announceAcceptance, armAcceptance, nextHumanInputSeq } from "./steer-admission.js";
11
11
  import { steerReplayRefusal } from "../steer-replay.js";
12
12
  export function streamSteerVerb(input) {
@@ -18,7 +18,7 @@ export function streamSteerVerb(input) {
18
18
  }
19
19
  const trusted = options?.trusted ? true : false;
20
20
  if (trusted && sanitizeUntrustedText(text) !== text) {
21
- throw steeringError("trusted steering text must not contain a </system-reminder> tag", "steering.invalid_content");
21
+ throw steeringError(sentinelTagRefusal("trusted steering text"), "steering.invalid_content");
22
22
  }
23
23
  const inputId = options?.inputId;
24
24
  if (inputId !== undefined) {
@@ -34,8 +34,9 @@
34
34
  * another spelling of a tool of the universe) — synchronous, like the phase that calls it.
35
35
  */
36
36
  import type { AgentTool } from "../../internal/harness.js";
37
+ import { valveLeaves } from "../../agents/tool-filter.js";
37
38
  import { type ProtocolId } from "../protocol-table.js";
38
- import type { ToolLegKind, ToolSelector, ToolSelectorLayer } from "../tool-face.js";
39
+ import type { ToolLegKind, ToolSelectorLayer } from "../tool-face.js";
39
40
  import { type ConstraintChainEntry } from "../tool-policy.js";
40
41
  import type { TaskSpec } from "../types.js";
41
42
  import type { CheckpointErrorReason } from "../checkpoint-store.js";
@@ -65,14 +66,15 @@ export declare function renamedDelegationMountSentence(...rosters: unknown[]): s
65
66
  */
66
67
  export declare function refuseRenamedDelegationMounts(...rosters: unknown[]): void;
67
68
  /**
68
- * THE reading of what the delegation tools a run mounts hand their children, read after the run's own selector: only a delegation
69
- * tool the selector leaves (`selector.admits`) counts — one it takes out hands nobody anything, and neither its pool names nor an
70
- * unlistable pool of it belong to any universe. The tools themselves, every pool tool's name and alias (a declared `extraToolNames`
71
- * roster included), and whether every one of those pools can be listed: a delegation tool whose factory injects tools per spawn with no
72
- * declared roster has no listable pool (the static `tools` it was given included), so a name outside the listed ones may be one of its
73
- * own. This is the one place the pool side of both universes is defined; the list judgment below and the policy name audit ask it.
69
+ * THE reading of what the delegation tools a run mounts hand their children, read after the run's own tool valve: only a delegation
70
+ * tool the valve leaves (`leaves`, the valve's one reading — `valveLeaves`) counts — one it takes out hands nobody anything, and neither
71
+ * its pool names nor an unlistable pool of it belong to any universe. The tools themselves, every pool tool's name and alias (a declared
72
+ * `extraToolNames` roster included), and whether every one of those pools can be listed: a delegation tool whose factory injects tools
73
+ * per spawn with no declared roster has no listable pool (the static `tools` it was given included), so a name outside the listed ones
74
+ * may be one of its own. This is the one place the pool side of both universes is defined; the list judgment below and the policy name
75
+ * audit ask it.
74
76
  */
75
- export declare function delegationPoolReading(callerTools: NonNullable<TaskSpec["tools"]>, selector: Pick<ToolSelector, "admits">): DelegationPoolReading;
77
+ export declare function delegationPoolReading(callerTools: NonNullable<TaskSpec["tools"]>, leaves: ReturnType<typeof valveLeaves>): DelegationPoolReading;
76
78
  /** The arms of this module's `config.tool_face_control` refusal that a resume carries before the claim as `detail.reason` of a `checkpoint.config_refused` — the same words
77
79
  * ({@link CheckpointErrorReason}): a list the row recorded that is not a list / has a member that is not a string / has a spelling no tool name can have / is not in the
78
80
  * spelling a row records, a record that is not one, a renamed delegation mount, an occupied table of declared names. */
@@ -123,8 +125,8 @@ export declare function boundOwnToolLists(record: unknown, own: {
123
125
  readonly exclude: readonly string[] | undefined;
124
126
  }): RunInternals["ownToolLists"];
125
127
  export interface RunToolListsInput {
126
- /** borrowed-readonly — the REBOUND spec: `agents` (the task's definitions) and `tools` (the caller's tools and the mounted delegation tools). */
127
- spec: Pick<TaskSpec, "agents" | "tools">;
128
+ /** borrowed-readonly — the REBOUND spec: `agents` (the task's definitions), `tools` (the caller's tools and the mounted delegation tools) and `excludeAllTools` (the valve's whole-face switch, which takes the delegation tools out too). */
129
+ spec: Pick<TaskSpec, "agents" | "tools" | "excludeAllTools">;
128
130
  /** borrowed-readonly — the assembled mount array BEFORE the selector's valve (names + aliases = the roster half of the universe). */
129
131
  tools: ReadonlyArray<AgentTool>;
130
132
  /** borrowed-readonly — the peers the task declares, per protocol of the protocol table (its MCP servers, its A2A peers): a namespaced
@@ -170,5 +172,5 @@ export declare function judgeRunToolLists(input: RunToolListsInput): void;
170
172
  * `detail.reason: "near_miss"`) rather than kept as a deny that hides nothing. A name no tool answers to stays kept (a tool of that
171
173
  * name may be mounted later), unannounced: the host wrote no spec this leg could name the list of.
172
174
  */
173
- export declare function judgeHostToolCeiling(layers: readonly ToolSelectorLayer[], agent: Pick<TaskSpec, "tools" | "allowTools" | "excludeTools">, above?: readonly ToolSelectorLayer[]): void;
175
+ export declare function judgeHostToolCeiling(layers: readonly ToolSelectorLayer[], agent: Pick<TaskSpec, "tools" | "allowTools" | "excludeTools" | "excludeAllTools">, above?: readonly ToolSelectorLayer[]): void;
174
176
  export {};
@@ -1,4 +1,4 @@
1
- import { compileToolSelector, deniedProtocolTools, delegationListWhere, isCanonicalToolName, isToolSelectorLayer, listReaches, notAListSentence, toolListsDigest, notAStringSentence, readAllowedToolList, readDeniedEntry, readDeniedToolList } from "../../agents/tool-filter.js";
1
+ import { compileToolSelector, valveLeaves, deniedProtocolTools, delegationListWhere, isCanonicalToolName, isToolSelectorLayer, listReaches, notAListSentence, toolListsDigest, notAStringSentence, readAllowedToolList, readDeniedEntry, readDeniedToolList } from "../../agents/tool-filter.js";
2
2
  import { readToolNameList, valueTypeLabel } from "../tool-name-list.js";
3
3
  import { compileAllowList, inertEntryPhrase, renderListEntry, rootFacingToolNames } from "../allow-entry.js";
4
4
  import { inlineUntrusted } from "../untrusted-text.js";
@@ -37,8 +37,8 @@ export function refuseRenamedDelegationMounts(...rosters) {
37
37
  function poolOf(tools) {
38
38
  return { tools, names: mountedToolNames([...tools.map((t) => ({ name: t.name })), ...tools.flatMap((t) => t.agentToolPool ?? [])]), complete: tools.every((t) => t.agentToolPool !== undefined) };
39
39
  }
40
- export function delegationPoolReading(callerTools, selector) {
41
- return poolOf(callerTools.filter((t) => t.agentToolFaces !== undefined && selector.admits(t)));
40
+ export function delegationPoolReading(callerTools, leaves) {
41
+ return poolOf(callerTools.filter((t) => t.agentToolFaces !== undefined && leaves(t)));
42
42
  }
43
43
  function peersOf(known, declaredPeers) {
44
44
  const peers = new Set(PROTOCOL_TABLE.flatMap((ns) => (declaredPeers[ns.id] ?? []).map((p) => mintNamespacePrefix(ns, p.name))));
@@ -275,7 +275,8 @@ export function judgeRunToolLists(input) {
275
275
  const by = [...deniedBy(true, ownWhere ?? "this run's own excludeTools"), ...deniedBy(false, "an inherited list (the spawning run's excludeTools, or one it inherits)")];
276
276
  throw refused(`This kind of leg (${input.legs.join(", ")}) relies on ${names.join(", ")}, but ${by.join(" and ")} — the leg cannot work without ${one ? "it" : "them"}. Remove the entry from the list that names it.`, "config.tool_face_control", "protocol_denied");
277
277
  }
278
- const pool = delegationPoolReading(spec.tools ?? [], compileToolSelector(input.layers, input.legs));
278
+ const selector = compileToolSelector(input.layers, input.legs);
279
+ const pool = delegationPoolReading(spec.tools ?? [], valveLeaves(spec, selector));
279
280
  const mounted = mountedToolNames([...tools, ...(spec.tools ?? [])]);
280
281
  if (!input.judgedUpstream) {
281
282
  const root = rootFacingUniverse(input.ownBy !== undefined ? new Set([...mounted, input.ownBy.toolName]) : mounted, pool, input.declaredPeers);
@@ -310,7 +311,8 @@ export function judgeHostToolCeiling(layers, agent, above = []) {
310
311
  layers.forEach((layer, i) => readSelectorLayer(layer, where(i), () => where(i), false));
311
312
  const callerTools = agent.tools ?? [];
312
313
  const own = { ...(agent.allowTools != null ? { allow: agent.allowTools } : {}), deny: agent.excludeTools ?? [] };
313
- const pool = delegationPoolReading(callerTools, compileToolSelector([...above, ...layers, own], ["delegated"]));
314
+ const selector = compileToolSelector([...above, ...layers, own], ["delegated"]);
315
+ const pool = delegationPoolReading(callerTools, valveLeaves(agent, selector));
314
316
  const universe = rootFacingUniverse(mountedToolNames(callerTools), pool, { mcp: undefined, a2a: undefined });
315
317
  const unannounced = () => undefined;
316
318
  layers.forEach((layer, i) => {
@@ -110,8 +110,11 @@ export interface SkillListingEntry {
110
110
  * and the tool, so what the prompt promises is exactly what the tool serves. Each field is READ ONCE, BY NAME, and
111
111
  * both consumers see the copy: the gate screens this list while the invoke substitutes from it, so a `get baseDir()`
112
112
  * answering those two reads differently passed a screen that measured a value the model never received — and a rest
113
- * copy would silently drop an INHERITED or non-enumerable list. The two tables are read by {@link readSkillTable}. */
114
- export declare function normalizeSkills(skills: SkillLike[] | undefined): SkillLike[];
113
+ * copy would silently drop an INHERITED or non-enumerable list. The two tables are read by {@link readSkillTable}.
114
+ *
115
+ * The sorted view of {@link readSkillsSeat} (skills-seat.ts), which holds the seat's one reading — total over what a caller can
116
+ * hand it, and once. */
117
+ export declare function normalizeSkills(skills: unknown): SkillLike[];
115
118
  /** [c209-C] The skills listing's lead line — single-sourced so the run loop's durable-resume
116
119
  * transcript probe (MED-3② skills arm, runtask) matches EXACTLY what the initial frame carries
117
120
  * (probing the bare `<skills>` fence would be more collision-prone in model-quoted text). */
@@ -5,6 +5,7 @@ import { SKILL_LISTING_DEFAULT_BUDGET, SKILL_LISTING_DESC_MAX_CHARS_DEFAULT } fr
5
5
  import { readSkillTable, skillNamedBy } from "../skill-grant.js";
6
6
  import { toolFace } from "../tool-catalog-entries.js";
7
7
  import { substituteSkillRoot, substituteSkillRootBounded } from "./skill-base-dir.js";
8
+ import { readSkillsSeat } from "./skills-seat.js";
8
9
  import { foldListingWithin } from "./listing-fold.js";
9
10
  export const OUTPUT_TOOL_NAME = "StructuredOutput";
10
11
  export const REPORT_BLOCKED_TOOL_NAME = "ReportBlocked";
@@ -109,18 +110,7 @@ function skillFilesSuffix(files) {
109
110
  return ` [files: ${shown.join(", ")}${more > 0 ? `, +${more} more` : ""}]`;
110
111
  }
111
112
  export function normalizeSkills(skills) {
112
- const seen = new Set();
113
- const out = [];
114
- for (const s of skills ?? []) {
115
- if (!seen.has(s.name)) {
116
- seen.add(s.name);
117
- const { name, description, content, allowedTools, disallowedTools, files, baseDir, ...rest } = s;
118
- void rest;
119
- const [allowed, denied] = [readSkillTable(allowedTools), readSkillTable(disallowedTools)];
120
- out.push({ name, description, content, ...(allowed !== undefined ? { allowedTools: allowed } : {}), ...(denied !== undefined ? { disallowedTools: denied } : {}), ...(files !== undefined ? { files } : {}), ...(baseDir !== undefined ? { baseDir } : {}) });
121
- }
122
- }
123
- return out.toSorted((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
113
+ return readSkillsSeat(skills).toSorted((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
124
114
  }
125
115
  export const SKILLS_LISTING_PROBE_HEADER = "The following skills are available for this task.";
126
116
  export function skillListingLine(s, descMaxChars = SKILL_LISTING_DESC_MAX_CHARS_DEFAULT) {
@@ -4,13 +4,15 @@
4
4
  * tool name like `allowTools` / `toolAxes`). Validated against the MATERIALIZED schema at every listing
5
5
  * (the first mount and every RefreshMcpTools): `pathTarget.param` must be a top-level property; an
6
6
  * `approvalCard` must find the keys the card needs; a `ruleFace` may only name scalar keys; `family`
7
- * must be a member of the closed set. A failing overlay does NOT mount — the tool mounts BARE (no
7
+ * must be a member of the closed set; every `resultCards` entry must live under the tool's own card namespace.
8
+ * A failing overlay does NOT mount — the tool mounts BARE (no
8
9
  * face) and the leg announces `config.tool_face_invalid` once per tool; a `toolFaces` key naming a
9
10
  * tool the listing does not carry announces `config.tool_face_undeclared` once per name. Never a
10
11
  * silent partial application: an overlay is one declaration, applied whole or not at all.
11
12
  *
12
13
  * Prepare-path machinery (layer 1): pure over the tool objects and the server specs; the notice sink
13
- * is a parameter.
14
+ * is a parameter. The declaration itself is read once, by `readToolFaces` (`core/tool-face-reader.ts`), at the preparation door and at the
15
+ * materialization entrance; the specs this file consumes carry that reader's copy, never the caller's object.
14
16
  */
15
17
  import type { AgentTool } from "../../internal/harness-types.js";
16
18
  import type { McpServerSpec } from "../mcp-server-spec.js";
@@ -20,10 +22,24 @@ import { type EngineNotice } from "../engine-notice.js";
20
22
  * card's full schema; this is the pre-filter the overlay is validated against). */
21
23
  export declare const APPROVAL_CARD_REQUIRED_KEYS: Readonly<Record<ToolApprovalCard, readonly string[]>>;
22
24
  export type McpToolFace = NonNullable<McpServerSpec["toolFaces"]>[string];
23
- /** Why an overlay is refused — a closed reason list, each a distinct declaration mistake. */
24
- export declare function toolFaceProblem(face: McpToolFace, schema: AgentTool["parameters"], tool?: {
25
+ /** The tool a face rides on, as far as the judge needs to know it: its name (a reserved approval card belongs to the engine's own tool of that
26
+ * name) and, when the caller holds the tool's card namespace, the `cardId` its `resultCards` must live under (`<cardId>/<card>`). */
27
+ export interface FaceTool {
25
28
  readonly toolName: string;
26
- }): string | undefined;
29
+ readonly cardId?: string;
30
+ }
31
+ /** Why an overlay is refused — a closed reason list, each a distinct declaration mistake. */
32
+ export declare function toolFaceProblem(face: McpToolFace, schema: AgentTool["parameters"], tool?: FaceTool): string | undefined;
33
+ /**
34
+ * The one judge of a face. With the tool's materialized `schema` it is {@link toolFaceProblem}: the declaration against the
35
+ * schema it rides on. With no schema it is the same judge over the DECLARATION ALONE (`readToolFaces`, `core/tool-face-reader.ts`): every
36
+ * check that needs the schema's keys (a path target's `param` / `patternParam`, an approval card's required keys, the names a summary or a
37
+ * rule face lists) waits for the listing; the closed words, the bounds and the cross-member rules do not need it and are the same
38
+ * checks, in the same order, with the same sentences. A face this judges is a well-typed one (the reader types it first). A tool that
39
+ * holds its card namespace (`tool.cardId`) has its `resultCards` held to it here too, so a caller's face and a server's face are
40
+ * judged by this one function, not by a rule beside it.
41
+ */
42
+ export declare function faceProblem(face: McpToolFace, schema: AgentTool["parameters"] | undefined, tool?: FaceTool): string | undefined;
27
43
  export interface ApplyToolFacesInput {
28
44
  /** The materialized tools of ONE server (namespaced names). */
29
45
  tools: readonly AgentTool[];
@@ -38,5 +54,7 @@ export interface ApplyToolFacesInput {
38
54
  * untyped carry `defineTool` uses, so the roster reads it off the instance). A face that does NOT fit the materialized
39
55
  * schema is a bad declaration: the tool is NOT mounted on this leg (announced once, `config.tool_face_invalid`) —
40
56
  * mounting it bare would silently drop the write protection the operator declared for it (an MCP name has no catalog
41
- * floor), and a schema refresh that invalidates only a display hint must fail loud, never open. */
57
+ * floor), and a schema refresh that invalidates only a display hint must fail loud, never open. The faces it reads are the
58
+ * server entry's own: the reader's copy of the declaration (`readToolFaces`), put there by the preparation door or the
59
+ * materialization entrance, never a caller's object. */
42
60
  export declare function applyMcpToolFaces(input: ApplyToolFacesInput): AgentTool[];
@@ -1,7 +1,8 @@
1
1
  import { MCP_NAMESPACE } from "../protocol-table.js";
2
2
  import { mintNamespacePrefix } from "../protocol-naming.js";
3
3
  import { RESERVED_APPROVAL_CARDS, TOOL_APPROVAL_CARDS, TOOL_FAMILIES, TOOL_PATH_ABSENCES, TOOL_PATH_ACCESSES, TOOL_PATH_BASES, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_CHARS, RENDER_HINT_MAX_LIST, TOOL_KEY_MAX_CHARS, } from "../tool-face.js";
4
- import { engineCardOf, inputKeysOf } from "../tool-roster.js";
4
+ import { engineCardOf, inputKeysOf, offendingResultCard, protocolToolOf, rosterCardId } from "../tool-roster.js";
5
+ import { engineCardTypes } from "../tool-registry.js";
5
6
  import { deliverEngineNotice } from "../engine-notice.js";
6
7
  export const APPROVAL_CARD_REQUIRED_KEYS = Object.freeze({
7
8
  "file-edit": ["file_path"],
@@ -14,17 +15,20 @@ export const APPROVAL_CARD_REQUIRED_KEYS = Object.freeze({
14
15
  generic: [],
15
16
  });
16
17
  export function toolFaceProblem(face, schema, tool) {
17
- const { keys, scalar } = inputKeysOf(schema);
18
- const top = new Set(keys.map((k) => k.name));
18
+ return faceProblem(face, schema, tool);
19
+ }
20
+ export function faceProblem(face, schema, tool) {
21
+ const bound = schema === undefined ? undefined : inputKeysOf(schema);
22
+ const top = bound === undefined ? undefined : new Set(bound.keys.map((k) => k.name));
19
23
  if (face.family !== undefined && !TOOL_FAMILIES.includes(face.family))
20
24
  return `family ${JSON.stringify(face.family)} is outside the closed set`;
21
25
  const overlong = (label, values) => {
22
26
  const k = values.find((v) => v.length > TOOL_KEY_MAX_CHARS);
23
27
  return k === undefined ? undefined : `${label} ${JSON.stringify(k.slice(0, 40) + "…")} is ${k.length} characters; a roster key is at most ${TOOL_KEY_MAX_CHARS}`;
24
28
  };
25
- const vocabulary = new Set(scalar);
29
+ const vocabulary = new Set(bound?.scalar);
26
30
  if (face.pathTarget !== undefined) {
27
- if (typeof face.pathTarget.param !== "string" || !top.has(face.pathTarget.param))
31
+ if (top !== undefined && (typeof face.pathTarget.param !== "string" || !top.has(face.pathTarget.param)))
28
32
  return `pathTarget.param ${JSON.stringify(face.pathTarget.param)} is not a top-level property of the tool's schema`;
29
33
  if (!TOOL_PATH_ACCESSES.includes(face.pathTarget.access))
30
34
  return `pathTarget.access ${JSON.stringify(face.pathTarget.access)} is outside the closed set`;
@@ -40,7 +44,7 @@ export function toolFaceProblem(face, schema, tool) {
40
44
  return `pathTarget.absent "base" needs pathTarget.access "read" — it says the call is about the whole base directory when the slot is empty, which is a search SCOPE; a write with no path names no file, and the containment fences judge a write on its own target`;
41
45
  const patternParam = face.pathTarget.patternParam;
42
46
  if (patternParam !== undefined) {
43
- if (typeof patternParam !== "string" || !top.has(patternParam))
47
+ if (top !== undefined && (typeof patternParam !== "string" || !top.has(patternParam)))
44
48
  return `pathTarget.patternParam ${JSON.stringify(patternParam)} is not a top-level property of the tool's schema`;
45
49
  if (absent !== "base")
46
50
  return `pathTarget.patternParam ${JSON.stringify(patternParam)} is declared but pathTarget.absent is ${JSON.stringify(absent ?? "none")} — the pattern base is only ever read for an absent slot, so this declaration would be inert`;
@@ -62,7 +66,7 @@ export function toolFaceProblem(face, schema, tool) {
62
66
  return `renderHints.approvalCard ${JSON.stringify(card)} is outside the closed set`;
63
67
  if (RESERVED_APPROVAL_CARDS.includes(card) && (tool === undefined || engineCardOf(tool) !== card))
64
68
  return `renderHints.approvalCard ${JSON.stringify(card)} is reserved for the engine's own tool that owns it; a declared tool takes another card or none`;
65
- const missing = APPROVAL_CARD_REQUIRED_KEYS[card].filter((k) => !top.has(k));
69
+ const missing = top === undefined ? [] : APPROVAL_CARD_REQUIRED_KEYS[card].filter((k) => !top.has(k));
66
70
  if (missing.length > 0)
67
71
  return `renderHints.approvalCard ${JSON.stringify(card)} needs ${missing.join(", ")} on the tool's schema`;
68
72
  }
@@ -77,16 +81,21 @@ export function toolFaceProblem(face, schema, tool) {
77
81
  const long = overlong("renderHints.summaryParams", hints.summaryParams ?? []) ?? overlong("renderHints.resultCards", hints.resultCards ?? []);
78
82
  if (long !== undefined)
79
83
  return long;
80
- const stray = (hints.summaryParams ?? []).find((p) => !top.has(p));
84
+ const foreignCard = tool?.cardId === undefined ? undefined : offendingResultCard(hints, tool.cardId, engineCardTypes());
85
+ if (foreignCard !== undefined)
86
+ return `renderHints.resultCards names ${JSON.stringify(foreignCard.slice(0, 80))}, outside the tool's own card namespace "${tool.cardId}/<card>" (engine cards and other contracts' namespaces are reserved)`;
87
+ const stray = top === undefined ? undefined : (hints.summaryParams ?? []).find((p) => !top.has(p));
81
88
  if (stray !== undefined)
82
89
  return `renderHints.summaryParams names ${JSON.stringify(stray)}, which is not a top-level property`;
83
90
  }
84
- for (const p of face.ruleFace?.params ?? [])
85
- if (!vocabulary.has(p))
86
- return `ruleFace.params names ${JSON.stringify(p)}, which is neither a scalar top-level property nor the declared path target`;
87
- for (const p of face.ruleFace?.primaryParams ?? [])
88
- if (!vocabulary.has(p))
89
- return `ruleFace.primaryParams names ${JSON.stringify(p)}, which is neither a top-level property nor the declared path target`;
91
+ if (top !== undefined) {
92
+ for (const p of face.ruleFace?.params ?? [])
93
+ if (!vocabulary.has(p))
94
+ return `ruleFace.params names ${JSON.stringify(p)}, which is neither a scalar top-level property nor the declared path target`;
95
+ for (const p of face.ruleFace?.primaryParams ?? [])
96
+ if (!vocabulary.has(p))
97
+ return `ruleFace.primaryParams names ${JSON.stringify(p)}, which is neither a top-level property nor the declared path target`;
98
+ }
90
99
  const long = overlong("ruleFace.params", face.ruleFace?.params ?? []) ?? overlong("ruleFace.primaryParams", face.ruleFace?.primaryParams ?? []);
91
100
  if (long !== undefined)
92
101
  return long;
@@ -121,7 +130,7 @@ export function applyMcpToolFaces(input) {
121
130
  if (raw === undefined || face === undefined)
122
131
  return t;
123
132
  const hit = { raw, face };
124
- const problem = toolFaceProblem(hit.face, t.parameters, { toolName: t.name });
133
+ const problem = toolFaceProblem(hit.face, t.parameters, { toolName: t.name, cardId: rosterCardId("mcp", t.name, input.server.name, protocolToolOf(t.name), "") });
125
134
  if (problem !== undefined) {
126
135
  const key = `invalid:${t.name}`;
127
136
  if (!input.announced.has(key)) {
@@ -14,7 +14,7 @@ import type { OnElicit } from "./mcp-server-spec.js";
14
14
  import type { Brain, ModelRef, ModelRoles } from "./model-seat.js";
15
15
  import type { RuntimeCaps, WorkflowGovernanceBaseline } from "./runtime-caps.js";
16
16
  /**
17
- * parity-204 — structured `RunnerDeps.loadProjectMemory` return (backward-compatible: a bare
17
+ * parity-204 — structured `RunnerDeps.loadProjectMemory` return (backward-compatible, RL-1336-project-memory-bare: a bare
18
18
  * `string | null` keeps its exact historical meaning). Adds the CC 2.1.204 `seededFromContext`
19
19
  * seam (cc204-bundle @17917159: CC preloads readFileState for CLAUDE.md/nested-memory files it
20
20
  * placed in context, and a DEFAULT whole-file Read of an unchanged seeded file answers with the
@@ -73,8 +73,8 @@ export interface ResumePreflightInfo {
73
73
  * the resume entry's identity-continuity rung already refused a contradicting supplied principal
74
74
  * before this hook runs, so a present value IS the resumed leg's identity. */
75
75
  principal?: string;
76
- /** provider / deviceId / mountPath — the placement facts. */
77
- workspaceHandle?: import("./remote-env.js").WorkspaceHandle;
76
+ /** provider / deviceId / mountPath — the placement facts: the row handle's one frozen copy (as `resumeWorkspace`, `priorHandle`). */
77
+ workspaceHandle?: Readonly<import("./remote-env.js").WorkspaceHandle>;
78
78
  gateKind: import("./checkpoint-store.js").CheckpointGate["kind"];
79
79
  }
80
80
  /** design/380 O2 — a {@link RunnerDeps.resumePreflight} answer. */
@@ -141,10 +141,8 @@ export interface RunnerDeps {
141
141
  * probe must not brick every MCP call) with a once-per-MATERIALIZATION `mcp.revocation_probe_failed`
142
142
  * notice (a resume re-materializes and may announce again — the standing condition is re-news at
143
143
  * each fresh mount, never per-call). The notice is a deployment wiring fact: `detail: { message }`
144
- * only, no session attribution, `"operator"` audience by its explicit {@link NOTICE_AUDIENCE}
145
- * row (#433 made the registry total over the catalog — no engine-minted code is
146
- * audience-defaulted any more) — a
147
- * wire projector forwards it operator-tier and needs no per-session de-duplication of its own.
144
+ * only, no session attribution, `"operator"` audience by its explicit {@link NOTICE_AUDIENCE} row — a wire projector
145
+ * forwards it operator-tier and needs no per-session de-duplication of its own.
148
146
  * The probe's second argument, `{ fingerprint }`, is passed exactly when the server's declaration carries one.
149
147
  */
150
148
  mcpRevocations?: import("./mcp-server-spec.js").McpRevocationLedger;
@@ -159,10 +157,11 @@ export interface RunnerDeps {
159
157
  */
160
158
  rosterStore?: import("../agents/roster-store.js").RosterStore;
161
159
  /**
162
- * The center prompt-artifact source (artifact v2 only — INTEGRATION-CORE §12.11). `current()` supplies the verified
163
- * candidate a top-level session adopts at session start and at a compaction; `get(digest)` resolves a PINNED center
160
+ * The center prompt-artifact source (artifact v3 only — a v2 body is refused `schema`, INTEGRATION-CORE §12.11).
161
+ * `current()` supplies the verified candidate a top-level session adopts at session start and at a compaction; `get(digest)` resolves a PINNED center
164
162
  * layer on resume (a miss is fail-loud `prompt.snapshot_unavailable` — core never recomposes a hybrid from newer
165
- * content); `sourceState()` is the tri-state every adoption decision keys on. What a source hands a run is re-checked
163
+ * content; a copy the engine refuses is told apart from a missing one, in the failure's sentence and on `onError`);
164
+ * `sourceState()` is the tri-state every adoption decision keys on. What a source hands a run is re-checked
166
165
  * before it is adopted. Core bundles `CenterPromptSource` (+ memory/file artifact & source-state stores); a server
167
166
  * feeds it published artifacts via `applySnapshot`. Omitted = no center layer.
168
167
  */
@@ -341,6 +340,9 @@ export interface RunnerDeps {
341
340
  * to CONTINUE instead — zero capture and zero out-of-policy running, availability paid).
342
341
  */
343
342
  memoryCapturePolicy?: "open" | "governed" | "capture-required";
343
+ /** The deployment's cross-boundary posture, one of {@link import("./cross-boundary-taint.js").CROSS_BOUNDARY_POSTURES} (who is hurt when a tree that
344
+ * executes on a target device reaches past it). Absent reads `"single_user"`; any other value refuses the prepare, and a resume before its claim (`config.cross_boundary_posture`). */
345
+ crossBoundaryPosture?: "single_user" | "multi_tenant";
344
346
  /**
345
347
  * design/383 §2.1b — an optional host-supplied carrier for the session capture opt-out record
346
348
  * (per memory plane, keyed by that plane's control dir). Absent = the control-plane file trio
@@ -961,6 +963,19 @@ export interface RunnerDeps {
961
963
  * everywhere (mode parity, the CC default). Inert without {@link peerDirectory}.
962
964
  */
963
965
  crossSessionInbound?: import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers | (() => import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers);
966
+ /**
967
+ * WHO consumes this deployment's principal inbox (`principal.inbox`, where the
968
+ * organization's delivery gate appends cross-principal letters; the mailbox contract's fourth chain).
969
+ * Absent = no consumer, no lane (byte for byte as before); a deployment that mounts a gate DECLARES
970
+ * `{ assistant: "<agent handle>" }` (the scope's assistant is the only consumer; no terminal leg touches the
971
+ * box). `"terminals"` (each terminal drains the box as a second lane) is refused until held decisions are
972
+ * shared across terminals (`detail.reason: "terminals_unsupported"`). Letters are judged on the principal's
973
+ * `crossSessionInbound` layers (managed, user), held when unset. ANY other value fails the prepare of a leg
974
+ * that drains a session box with `config.principal_inbox_consumer_invalid` (`detail.reason: "malformed"`).
975
+ */
976
+ principalInboxConsumer?: "terminals" | {
977
+ assistant: string;
978
+ };
964
979
  /**
965
980
  * design/385 §4.4 (slice 4) — this deployment's `dialogExpiry` (CC settings key, verbatim vocabulary
966
981
  * `"60s" | "5m" | "10m" | "never"`, default `"5m"`): how long a HELD cross-session message whose cause
@@ -1286,18 +1301,18 @@ export interface RunnerDeps {
1286
1301
  * summaries) is invisible without this and lets a session grow until it tops the window.
1287
1302
  * - `"prompt-cache"` — a prefix-cache hazard on the wire: a detected cache break (design/31), or a
1288
1303
  * mis-declared/mismatched `promptCacheFamily`. The prefix cache will miss. Log/alert on both.
1289
- * - `"prompt-constitution"` — a prompt overlay did not take effect as written: a later source (or a later
1290
- * declaration in the same source) superseded it on the same target, its target was not in this run's
1291
- * prompt, or a mods handler's `prompt.compose` answer was skipped (it rewrote another's overlay, or wrote
1292
- * what the band table does not grant its seat). Each overlay is named once per run.
1293
- * - `"degraded"` — the task kept running with a capability quietly reduced: a question auto-answered
1294
- * with no human present, a skipped env git snapshot, a lost checkpoint-put confirmation, an
1295
- * unroutable descendant notification, and similar best-effort arms. The run proceeds; the reduced
1296
- * arm is what is being disclosed (`classification` names it).
1297
- * - `"config"` — a deployment-wiring problem detected while preparing or tearing down a task: tool
1298
- * mount conflicts, refused/ignored knob values, gate-off notices, store/env teardown legs. The
1299
- * broadest class by call-site count — most misconfigurations announce here rather than failing
1300
- * the task.
1304
+ * - `"prompt-constitution"` — a prompt overlay did not take effect as written: a later source (or a later declaration in the same source) superseded
1305
+ * it on the same target, its target was not in this run's prompt, or a mods handler's `prompt.compose` answer was skipped (it rewrote another's
1306
+ * overlay, or wrote what the band table does not grant its seat); each overlay is named once per run. The center prompt too: a pinned session's
1307
+ * stored artifact refused or unreadable (it fails `prompt.snapshot_unavailable`), a fresh session's candidate that does not verify, and a
1308
+ * `CenterPromptSource` boot restore with no usable copy (`sessionId: ""`, `classification: "center-source-restore"`) — that last line only when the
1309
+ * host passes this same sink to `CenterPromptSource` as its own `onError` (the source restores at boot, apart from any Runner; given none, it is silent).
1310
+ * - `"degraded"` — the task kept running with a capability quietly reduced: a question auto-answered with no human
1311
+ * present, a skipped env git snapshot, a lost checkpoint-put confirmation, an unroutable descendant notification, and
1312
+ * similar best-effort arms. The run proceeds; the reduced arm is what is being disclosed (`classification` names it).
1313
+ * - `"config"` — a deployment-wiring problem detected while preparing or tearing down a task: tool mount conflicts,
1314
+ * refused/ignored knob values, gate-off notices, store/env teardown legs. The broadest class by call-site count — most
1315
+ * misconfigurations announce here rather than failing the task.
1301
1316
  * - `"memory"` — a post-task memory-consolidation pass failed or skipped a malformed reconcile
1302
1317
  * decision (design/41). Best-effort: the notes the model saved are kept; the task still succeeds.
1303
1318
  * - `"mcp"` — an MCP server failed to connect / list its tools and was skipped (fail-open, design/29);
@@ -27,6 +27,7 @@
27
27
  * chain); the real isolation boundary is the executionEnv, never these rules.
28
28
  */
29
29
  import { compileAllowList, type AllowEntryInert, type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
30
+ import type { EngineNotice } from "./engine-notice.js";
30
31
  /** The per-session rules. All optional; an absent bucket imposes no constraint of that kind. */
31
32
  export interface SessionPermissionRules {
32
33
  /** If set, ONLY the calls these entries admit are allowed — a narrowing allowlist: a call no entry admits is refused.
@@ -244,6 +245,24 @@ export declare function compileSessionRuleBucket(rules: SessionRulesInGrammar, f
244
245
  * to make a parenthesized one the rule it now spells, or (`*`, which is no allow rule in any grammar) to leave it out.
245
246
  */
246
247
  export declare function earlierGrammarAdvisory(sessionId: string, entries: readonly string[], field: SessionRuleEntryField, grammar: number): string;
248
+ /** The reader's word for one entry an earlier grammar reads as nothing: `no_rule` — this engine's rule grammar reads it as no
249
+ * rule; `named_no_tool` — a grammar-0 allow entry the bare-name comparison of its day read as naming no tool. */
250
+ export type EarlierGrammarReader = "no_rule" | "named_no_tool";
251
+ /**
252
+ * The notice for the entries of one bucket a stored row's earlier grammar reads as nothing (`config.session_rules_earlier_grammar`,
253
+ * audience `user` — the session's rules are what the person's calls are judged by): the message is {@link earlierGrammarAdvisory}
254
+ * verbatim, and `detail` carries the same facts for a machine — `bucket` (the field), `storedGrammar` (the row's), `entries` (every
255
+ * entry, each as `noticeRuleText` hands rule text to a notice, with its reader's word), `rewrite` (the way out: an operator write of
256
+ * a new value without the stored row's rev, which stamps `stampsGrammar`), `rulesSessionId` (whose rules — an ancestor's on a
257
+ * delegated child's inherited layer) and `sessionId` (the run that read them).
258
+ */
259
+ export declare function earlierGrammarNotice(input: {
260
+ rulesSessionId: string;
261
+ sessionId: string;
262
+ entries: readonly string[];
263
+ field: SessionRuleEntryField;
264
+ grammar: number;
265
+ }): EngineNotice;
247
266
  /** The clause a run-time refusal carries when the allowlist it judged by holds entries its earlier grammar reads as nothing
248
267
  * ({@link compileSessionRuleBucket}; `grammar` = the row's): the grammar the rules are stored in, then which entries name no tool
249
268
  * there (grammar 0: `*` by its spelling, the parenthesized ones counted) and how many this engine's grammar reads as no rule —
@@ -4,6 +4,7 @@ import { NAMESPACED_NAME_SHAPES } from "./protocol-table.js";
4
4
  import { callerToolNameProblem } from "./caller-tool-name.js";
5
5
  import { compileAllowList, compiledEntryName, inertEntryPhrase, readRuleEntry, renderListEntry, rootFacingToolNames } from "./allow-entry.js";
6
6
  import { valueTypeLabel } from "./tool-name-list.js";
7
+ import { noticeRuleText } from "./permission-rule-model.js";
7
8
  const SESSION_RULE_BUCKETS = { toolAllow: "allow", toolDeny: "deny", allowDirs: "dirs" };
8
9
  const BUCKETS = Object.keys(SESSION_RULE_BUCKETS);
9
10
  const ENTRY_FIELDS = BUCKETS.filter((f) => SESSION_RULE_BUCKETS[f] !== "dirs");
@@ -162,11 +163,30 @@ function shownEntries(entries, render) {
162
163
  }
163
164
  const REWRITE_THROUGH = `through this engine (an operator write of a new value without the stored row's rev; the write stamps grammar ${SESSION_RULES_GRAMMAR})`;
164
165
  export function earlierGrammarAdvisory(sessionId, entries, field, grammar) {
165
- const noRule = entries.filter((e) => readsAsNoRule(e, field));
166
- const namedNoTool = entries.filter((e) => !readsAsNoRule(e, field));
166
+ const read = earlierGrammarReadings(entries, field);
167
+ const noRule = read.filter((r) => r.reader === "no_rule").map((r) => r.entry);
168
+ const namedNoTool = read.filter((r) => r.reader === "named_no_tool").map((r) => r.entry);
167
169
  const sentences = [...(namedNoTool.length > 0 ? [namedNoToolInGrammar0(namedNoTool)] : []), ...(noRule.length > 0 ? [noRuleInThisGrammar(noRule, field, grammar)] : [])];
168
170
  return `session permission rules of session ${JSON.stringify(sessionId)}: ${sentences.join(" ")}`;
169
171
  }
172
+ function earlierGrammarReadings(entries, field) {
173
+ return entries.map((entry) => ({ entry, reader: readsAsNoRule(entry, field) ? "no_rule" : "named_no_tool" }));
174
+ }
175
+ export function earlierGrammarNotice(input) {
176
+ const { rulesSessionId, sessionId, entries, field, grammar } = input;
177
+ return {
178
+ code: "config.session_rules_earlier_grammar",
179
+ message: earlierGrammarAdvisory(rulesSessionId, entries, field, grammar),
180
+ detail: {
181
+ bucket: field,
182
+ storedGrammar: grammar,
183
+ entries: earlierGrammarReadings(entries, field).map((r) => ({ entry: noticeRuleText(r.entry), reader: r.reader })),
184
+ rewrite: { by: "operator_write_without_rev", stampsGrammar: SESSION_RULES_GRAMMAR },
185
+ rulesSessionId,
186
+ sessionId,
187
+ },
188
+ };
189
+ }
170
190
  function namedNoToolInGrammar0(entries) {
171
191
  const one = entries.length === 1;
172
192
  const wildcard = entries.some(readsAsWildcard);
@@ -1,4 +1,4 @@
1
- import { type SessionRepo } from "../internal/harness.js";
1
+ import { type SessionPlacementRecord, type SessionRepo } from "../internal/harness.js";
2
2
  import { type AcquiredSession, type PlacedSessionRow, type SessionPlacement, type SessionStore, type SessionStoreSummary } from "./session.js";
3
3
  import type { StoreDurability } from "./checkpoint-store.js";
4
4
  import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
@@ -52,6 +52,37 @@ export interface TtlSessionStoreOptions {
52
52
  };
53
53
  };
54
54
  }
55
+ /**
56
+ * The one placement admission of a session store's `acquire` — every backend that declares the placement partition
57
+ * judges each resolved session through it (this module's {@link TtlSessionStore} on its four resolution paths: cache
58
+ * hit, pending-promise inherit, open, idempotent re-create), so no backend writes the two directions a second time.
59
+ *
60
+ * Placement obligation 6 (claim closure): an EXISTING placed session refuses the claim-form
61
+ * acquire — no `requireExisting` (the read/revive form, obligation 5) and no creation placement
62
+ * (the trusted-internals form; the creating call and its dedup/retry re-acquires stay legal, and
63
+ * placement immutability means the argument can never REWRITE anything — the persisted tuple wins).
64
+ *
65
+ * …and the MIRROR direction (the pre-claim SQUAT race): the child session id is
66
+ * published on the spawn frame BEFORE the child's prepare creates the session, so a concurrent
67
+ * claim-form acquire could win first creation and hand the trusted placement caller an ORDINARY
68
+ * session — host-listable, never really-deleted, the declared full tier falsified in silence. On
69
+ * a store that DECLARES the partition (`capability.subagentPlacement`; an ignoring store legally drops
70
+ * placements), an acquire carrying a placement must therefore come back with a PERSISTED tuple whose
71
+ * join identity matches the declaration — a missing or foreign tuple refuses loudly.
72
+ *
73
+ * @param persisted the tuple the backend holds for `sessionId` as resolved (`undefined`: an unplaced session).
74
+ * @param request the `acquire` options the caller passed.
75
+ * @param capability whether the store declares the subagent placement partition. `subagentPlacement` is read once,
76
+ * and only when the mirror direction is judged (a placement request that is not the read form, after `request`'s
77
+ * members are read), so a store may pass a getter over its live declaration.
78
+ * @throws SessionError `placement_refused` on either direction; returns nothing when admitted.
79
+ */
80
+ export declare function admitSessionPlacement(sessionId: string, persisted: SessionPlacementRecord | undefined, request: {
81
+ requireExisting?: boolean;
82
+ placement?: SessionPlacement;
83
+ }, capability: {
84
+ subagentPlacement: boolean;
85
+ }): void;
55
86
  /**
56
87
  * TTL-cached {@link SessionStore} over a pluggable {@link SessionRepo}.
57
88
  *
@@ -120,21 +151,9 @@ export declare class TtlSessionStore implements SessionStore {
120
151
  requireExisting?: boolean;
121
152
  placement?: SessionPlacement;
122
153
  }): Promise<AcquiredSession>;
123
- /**
124
- * Placement obligation 6 (claim closure): an EXISTING placed session refuses the claim-form
125
- * acquire — no `requireExisting` (the read/revive form, obligation 5) and no creation placement
126
- * (the trusted-internals form; the creating call and its dedup/retry re-acquires stay legal, and
127
- * placement immutability means the argument can never REWRITE anything — the persisted tuple wins).
128
- *
129
- * …and the MIRROR direction (the pre-claim SQUAT race): the child session id is
130
- * published on the spawn frame BEFORE the child's prepare creates the session, so a concurrent
131
- * claim-form acquire could win first creation and hand the trusted placement caller an ORDINARY
132
- * session — host-listable, never really-deleted, the declared full tier falsified in silence. On
133
- * a store that DECLARES the partition (an ignoring store legally drops placements), an acquire
134
- * carrying a placement must therefore come back with a PERSISTED tuple whose join identity
135
- * matches the declaration — a missing or foreign tuple refuses loudly, on every resolution path
136
- * (cache hit, pending-promise inherit, open, idempotent re-create).
137
- */
154
+ /** The placement admission ({@link admitSessionPlacement}) for this store, judged on every resolution path. The
155
+ * partition declaration is a getter over this store's live field, so it is read where the admission judges the
156
+ * mirror direction (after the request's own members, and only then) — never captured at construction. */
138
157
  private assertPlacementAdmission;
139
158
  /** Resume an existing session from the repo, or create it if the backend has no such id (unless
140
159
  * `requireExisting`, in which case a genuinely-missing id fails loud instead — design/114 Phase3). */
@@ -4,6 +4,20 @@ import { readMember } from "./thrown-value.js";
4
4
  const DAY_MS = 24 * 60 * 60 * 1000;
5
5
  import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
6
6
  export { SESSION_DEFAULT_TTL_DAYS };
7
+ export function admitSessionPlacement(sessionId, persisted, request, capability) {
8
+ if (persisted !== undefined && request.requireExisting !== true && request.placement === undefined) {
9
+ throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${persisted.kind} placement partition — a claim-form acquire cannot attach to it (read it with requireExisting, or address the agent through its a* handle)`);
10
+ }
11
+ if (request.placement !== undefined && request.requireExisting !== true && capability.subagentPlacement) {
12
+ const want = request.placement;
13
+ if (persisted === undefined) {
14
+ throw new SessionError("placement_refused", `session ${sessionId} exists OUTSIDE the ${want.kind} placement partition it was declared into — refusing to run a subagent transcript on it (a concurrent claim-form create won this id, or the repo dropped the tuple)`);
15
+ }
16
+ if (persisted.kind !== want.kind || persisted.scope !== want.scope || persisted.handle !== want.handle) {
17
+ throw new SessionError("placement_refused", `session ${sessionId}'s persisted placement does not match the declared one (persisted ${persisted.kind}/${persisted.scope ?? "-"}/${persisted.handle ?? "-"} vs declared ${want.kind}/${want.scope ?? "-"}/${want.handle ?? "-"}) — refusing a cross-tuple attach`);
18
+ }
19
+ }
20
+ }
7
21
  export class TtlSessionStore {
8
22
  retention = "none";
9
23
  durability;
@@ -74,18 +88,12 @@ export class TtlSessionStore {
74
88
  return { session, sessionId: meta.id };
75
89
  }
76
90
  assertPlacementAdmission(sessionId, placement, opts) {
77
- if (placement !== undefined && opts?.requireExisting !== true && opts?.placement === undefined) {
78
- throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${placement.kind} placement partition — a claim-form acquire cannot attach to it (read it with requireExisting, or address the agent through its a* handle)`);
79
- }
80
- if (opts?.placement !== undefined && opts.requireExisting !== true && this.placements?.subagent !== undefined) {
81
- const want = opts.placement;
82
- if (placement === undefined) {
83
- throw new SessionError("placement_refused", `session ${sessionId} exists OUTSIDE the ${want.kind} placement partition it was declared into — refusing to run a subagent transcript on it (a concurrent claim-form create won this id, or the repo dropped the tuple)`);
84
- }
85
- if (placement.kind !== want.kind || placement.scope !== want.scope || placement.handle !== want.handle) {
86
- throw new SessionError("placement_refused", `session ${sessionId}'s persisted placement does not match the declared one (persisted ${placement.kind}/${placement.scope ?? "-"}/${placement.handle ?? "-"} vs declared ${want.kind}/${want.scope ?? "-"}/${want.handle ?? "-"}) — refusing a cross-tuple attach`);
87
- }
88
- }
91
+ const store = this;
92
+ admitSessionPlacement(sessionId, placement, opts ?? {}, {
93
+ get subagentPlacement() {
94
+ return store.placements?.subagent !== undefined;
95
+ },
96
+ });
89
97
  }
90
98
  async openOrCreate(id, requireExisting = false, placement) {
91
99
  try {