@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 { McpServerSpec, RunnerDeps, TaskSpec } from "./types.js";
1
+ import type { A2aServerSpec, McpServerSpec, RunnerDeps, TaskSpec } from "./types.js";
2
+ import { type ToolAxesSurface } from "./mcp-server-spec.js";
2
3
  import type { ToolPolicy } from "./tool-policy.js";
3
4
  /** The closed set of administratively lockable configuration keys. Adding a member is a deliberate
4
5
  * edit HERE (tsc forces the registry row), never a free-form string. */
@@ -34,7 +35,7 @@ export interface LockedConfig {
34
35
  keys: readonly LockedKey[];
35
36
  }
36
37
  /** The preflight's resolved snapshot — the ONLY thing downstream task assembly reads for the
37
- * guarded slots (never the raw `spec.mcp` / `spec.toolPolicy` fields). */
38
+ * guarded slots (never the raw `spec.mcp` / `spec.a2a` / `spec.toolPolicy` fields). */
38
39
  export interface LockedPreflight {
39
40
  /** The validated lock set (empty when the deployment declares none). */
40
41
  lockedKeys: ReadonlySet<LockedKey>;
@@ -45,6 +46,10 @@ export interface LockedPreflight {
45
46
  * `undefined` (core has no deployment-level MCP seat — a locked deployment mounts no
46
47
  * task-supplied servers). */
47
48
  mcp: TaskSpec["mcp"];
49
+ /** The task's A2A peers: a frozen SNAPSHOT of `spec.a2a` whose `toolAxes` were judged here by the same reader and with the same
50
+ * disposition as an MCP server's (a declaration that does not fit refused the whole prepare), each peer carrying the reader's plain
51
+ * copy; `undefined` when the spec declares no peer (absent or empty). Read once, here: the peers dialed are the peers judged. */
52
+ a2a: TaskSpec["a2a"];
48
53
  /** Effective caller tool policy: the same `spec.toolPolicy ?? deps.toolPolicy` slot the gate has
49
54
  * always enforced, resolved ONCE here. With `toolPolicy` locked, the deployment's own
50
55
  * `deps.toolPolicy` is the whole answer (a spec-supplied policy has already been refused). */
@@ -56,12 +61,27 @@ export interface LockedPreflight {
56
61
  * site). Unlocked: `spec.basePolicyForResumeEdit ?? deps.basePolicyForResumeEdit`. */
57
62
  basePolicyForResumeEdit: ToolPolicy | undefined;
58
63
  }
64
+ /**
65
+ * How one configuration door is entered: `seat` names the input that carries the value the door judges — the task's configuration or the
66
+ * deployment's deps. A prepare enters each door as it stands ({@link ENTER_AS_IS}: the door's own coded throw); a resume's screen before the
67
+ * claim enters the same door through the resume's refusal channel, which names the seat (`asResumeConfigRefusal`). One door, one function,
68
+ * two entrances: the seat is written once, where the door is called.
69
+ */
70
+ export type ConfigDoorEntry = <T>(seat: "taskConfig" | "deps", door: () => T) => T;
71
+ export declare const ENTER_AS_IS: ConfigDoorEntry;
59
72
  /**
60
73
  * Validate a deployment's lock declaration against the closed registry. Fail-loud on any unknown
61
74
  * key (`config.locked_registry`): a security control that doesn't parse must refuse to arm, not
62
75
  * degrade to "that key silently unlocked". Exported for deployments that want the check at their
63
76
  * own startup; the task-preparation preflight runs it on every leg regardless (a mis-declared lock
64
- * then fails the first prepare instead of silently guarding nothing).
77
+ * then fails the first prepare instead of silently guarding nothing — and, on a resume, the screen before
78
+ * the claim, which calls the same preflight).
79
+ *
80
+ * Total over the declaration: anything but `undefined` (no locks) or plain data whose `keys` is a list of registry names — `null`, a
81
+ * list, a primitive, a `keys` that is absent or not a list, a declaration that is not plain data (a Proxy, a `Set` of keys, a class
82
+ * instance, a getter that throws), an element that is not a string, a string that is not a registry name — is the same coded refusal
83
+ * naming `RunnerDeps.lockedConfig` and the member (an element by its index), never a TypeError and never "no locks". The declaration is
84
+ * read once, by the one declaration reading.
65
85
  */
66
86
  export declare function resolveLockedKeys(config: LockedConfig | undefined): ReadonlySet<LockedKey>;
67
87
  /**
@@ -74,23 +94,33 @@ export declare function resolveLockedKeys(config: LockedConfig | undefined): Rea
74
94
  * read off a LIVE object is only true for as long as the object holds still, and the fail-open
75
95
  * direction is the reachable one — so both boundaries consume data instead.
76
96
  *
77
- * The read discipline is the load-bearing part, and it is why this is a spread rather than
78
- * "read the property, then copy the object": a getter answering ALTERNATING LEGAL values would
97
+ * The read discipline is the load-bearing part, and it is why the copy is made in the pass that reads
98
+ * rather than "read the property, then copy the object": a getter answering ALTERNATING LEGAL values would
79
99
  * satisfy any number of re-reads (`"external"` to a validator, `"local"` to a mint site), and no
80
- * vocabulary guard anywhere has purchase on a legal value. One spread evaluates every own property
81
- * EXACTLY ONCE and materializes the result as plain data; every later reader — validator, connect,
82
- * refresh — then sees the same byte. Frozen so nothing downstream rewrites it.
100
+ * vocabulary guard anywhere has purchase on a legal value. One pass evaluates every member EXACTLY ONCE
101
+ * and materializes the result as plain data; every later reader — validator, connect, refresh — then sees
102
+ * the same byte. Frozen so nothing downstream rewrites it.
103
+ *
104
+ * Plain data, stated: an entry IS plain data, to its whole depth, or it is refused whole. The entry is read
105
+ * by the one declaration reading ({@link receiveDeclaration} — the engine's one receipt of a caller's object,
106
+ * in its declaration mode): every own member read once off its captured descriptor, every level copied, every
107
+ * copied container frozen; an object the entry reaches through two members is read once, and both hold the one
108
+ * copy. So a server's `transport` with its `args` / `env` / `headers`, `allowTools`, `toolFaces`, `toolAxes`
109
+ * and a peer's `headers` and `allowSkills` are the copy's own, and a host that edits its own nested object after
110
+ * the door (a header value, an `allowSkills.push`) reaches nothing the engine holds: the dial, a refresh and a
111
+ * re-dial all work from this copy, on both surfaces. The axes are then judged on that copy by their own reader
112
+ * ({@link judgeEntryToolAxes}), and an MCP server's faces by theirs ({@link judgeEntryToolFaces}).
83
113
  *
84
- * Boundary, stated: the copy is one level deep — nested option objects (transport, headers, env,
85
- * toolAxes) stay shared with the caller's entry exactly as before, because the axis this owns is the
86
- * content class; readers of the nested shapes normalize their own values (the transport DISCRIMINATOR
87
- * is derived, never echoed raw). `toolAxes` is the one nested shape both entrances replace with its
88
- * reader's plain copy ({@link judgeEntryToolAxes}), and the one member this snapshot reads BY NAME rather
89
- * than by spread, once: a declaration an entry carries as a non-enumerable or inherited property would
90
- * otherwise vanish before any reader sees it, and an absent caller axis is a tightening lost, not a
91
- * fail-closed absence. Every other member is taken as own ENUMERABLE properties only: an entry whose
92
- * fields live on a prototype was never the plain configuration record this type describes, and its
93
- * absent class reads fail-closed like any other absence.
114
+ * There is no third outcome between "copied whole" and "refused": a value the copy cannot carry whole is a
115
+ * declaration the engine cannot read — a Proxy at any level, an object whose prototype is neither
116
+ * `Object.prototype` nor null (a class instance, a map, `process.env`, a record built on a prototype or in another
117
+ * realm, whose members a copy would drop or could not tell from its own), a function, a structure that contains
118
+ * itself, a getter that throws (whatever it throws), a nesting too deep to read, a list element that is not a
119
+ * string, a member named `__proto__`, an entry that is not an object. Each is refused as `config.invalid_argument`
120
+ * naming the declarer and the member where reading stopped (`toolFaces.write_file`, `allowTools[2]`), never as the
121
+ * raw error the host's code threw, and never by handing the host's object on: a member some of whose getters have
122
+ * already run would be read again downstream, and could answer differently. The door refuses the whole prepare and
123
+ * a library entrance the whole call, with this one sentence either way.
94
124
  *
95
125
  * Exported because the SECOND boundary needs the identical guarantee: `materializeMcpTools` is public
96
126
  * and a library caller reaches it without passing any door, so it snapshots its own input through
@@ -98,14 +128,64 @@ export declare function resolveLockedKeys(config: LockedConfig | undefined): Rea
98
128
  */
99
129
  export declare function snapshotMcpEntries(entries: readonly McpServerSpec[]): McpServerSpec[];
100
130
  /**
101
- * The `toolAxes` question for ONE snapshot entry — asked by the preparation door (a refusal ends the prepare) and by the materialization
102
- * entrance (a refusal fails that server's dial), so there is one reading, one copy and one sentence whichever time it is asked.
103
- * `ok: true` carries the entry with the reader's PLAIN COPY of the axes in place of the caller's object: what was judged is the very value
104
- * the connect and every refresh read, not a live object that could answer differently later. The copy is frozen with the entry — a tool
105
- * that shallow-copies a server (`mountedMcp()`) to customise its axes must not rewrite what the parent reads. `ok: false` carries the
106
- * coded refusal (`config.invalid_argument`) naming the server, the tool and the member, never a value.
131
+ * The refusal of a declaration the engine cannot read (`config.invalid_argument`). `declarer` is the entry's own name as the engine captured
132
+ * it ({@link capturedName}) — absent when there was none to capture — so a consumer that holds the refused entry by a name (the A2A
133
+ * entrance's warning, its collision check) takes it from here and never reads the caller's object again.
134
+ */
135
+ export type DeclarationRefusal = Error & {
136
+ code: "config.invalid_argument";
137
+ declarer?: string;
138
+ };
139
+ /**
140
+ * The name a refused entry is held under by a consumer that holds it by a name (the A2A entrance): the refusal's own `declarer`, read as
141
+ * own data — never through anything the refusal's prototype carries — or `undefined` (the consumer names the entry by its place).
142
+ */
143
+ export declare function refusedDeclarer(refusal: unknown): string | undefined;
144
+ /**
145
+ * One entry of EITHER surface that declares `toolAxes` (an MCP server, an A2A peer) as frozen plain data, read by the discipline
146
+ * {@link snapshotMcpEntries} states: the entry and everything in it read once by the one declaration reading and copied to its whole
147
+ * depth, or the entry refused ({@link DeclarationRefusal}), naming the member where reading stopped. The A2A peer's door and its
148
+ * materialization snapshot through this same function, so the two surfaces' declarations reach the one judge ({@link judgeEntryToolAxes})
149
+ * read the same way; `at` is the surface the entry sits on, which only decides what the refusal calls the declarer.
150
+ */
151
+ export declare function snapshotDeclaredEntry<E extends McpServerSpec | A2aServerSpec>(entry: E, at: ToolAxesSurface): E;
152
+ /**
153
+ * The `toolAxes` question for ONE snapshot entry of either surface (`at`: an MCP server, an A2A peer) — asked by the preparation door (a
154
+ * refusal ends the prepare) and by each materialization entrance (a refusal fails that server's or that peer's dial), so there is one
155
+ * reading, one copy and one sentence whichever surface and whichever time it is asked. `ok: true` carries the entry with the reader's
156
+ * PLAIN COPY of the axes in place of the caller's object: what was judged is the very value the connect and every refresh read, not a
157
+ * live object that could answer differently later. The copy is frozen with the entry — a tool that shallow-copies a server
158
+ * (`mountedMcp()`) to customise its axes must not rewrite what the parent reads. `ok: false` carries the coded refusal
159
+ * (`config.invalid_argument`) naming the declarer, the tool (or skill) and the member, never a value.
160
+ */
161
+ export declare function judgeEntryToolAxes<E extends McpServerSpec | A2aServerSpec>(copy: E, at: ToolAxesSurface): {
162
+ ok: true;
163
+ entry: E;
164
+ } | {
165
+ ok: false;
166
+ error: Error & {
167
+ code: string;
168
+ };
169
+ };
170
+ /**
171
+ * The `toolFaces` question for ONE snapshot entry of an MCP server — asked by the preparation door and by the materialization entrance exactly
172
+ * as {@link judgeEntryToolAxes} asks its own, so the engine reads a face declaration one way (`readToolFaces`) and the mount consumes the copy
173
+ * that way gave it, never the caller's object: a mistyped member, an axis word written as a face member, a member of the wrong kind or an empty
174
+ * tool name is a refusal here, not a protection dropped in silence or a throw at the listing. `ok: true` carries the entry with the reader's
175
+ * frozen copy in place of the declaration (an empty record declares nothing and leaves no key); `ok: false` carries the coded refusal
176
+ * (`config.tool_face_invalid`) naming the server, the tool and the member.
107
177
  */
108
- export declare function judgeEntryToolAxes(copy: McpServerSpec): {
178
+ export declare function judgeEntryToolFaces(copy: McpServerSpec): {
179
+ ok: true;
180
+ entry: McpServerSpec;
181
+ } | {
182
+ ok: false;
183
+ error: Error & {
184
+ code: string;
185
+ };
186
+ };
187
+ /** Everything a snapshot MCP entry declares per tool, judged: its `toolAxes` ({@link judgeEntryToolAxes}), then its `toolFaces` ({@link judgeEntryToolFaces}). */
188
+ export declare function judgeMcpEntryDeclarations(copy: McpServerSpec): {
109
189
  ok: true;
110
190
  entry: McpServerSpec;
111
191
  } | {
@@ -118,14 +198,13 @@ export declare function judgeEntryToolAxes(copy: McpServerSpec): {
118
198
  * The single preflight resolver: validate the lock declaration, refuse a spec that tries to occupy
119
199
  * a locked slot (`config.locked_key`, two-state — the whole prepare is rejected, nothing is
120
200
  * silently dropped or merged), and produce the resolved snapshot downstream assembly consumes.
121
- * Runs BEFORE any side effect of task preparation.
201
+ * Runs BEFORE any side effect of task preparation — and, on a resume, before the claim: the resume's
202
+ * screen calls this same function (`enter` naming each refusal's seat) and hands the leg the frozen
203
+ * entries it judged, so the leg's prepare judges engine-owned plain data and dials what was judged.
122
204
  */
123
- export declare function preflightLockedConfig(spec: TaskSpec, deps: RunnerDeps): LockedPreflight;
205
+ export declare function preflightLockedConfig(spec: Pick<TaskSpec, "mcp" | "a2a" | "toolPolicy" | "basePolicyForResumeEdit">, deps: RunnerDeps, enter?: ConfigDoorEntry): LockedPreflight;
124
206
  /**
125
207
  * The two caller policies a prepare resolves to: the task's own seat over the deployment's, or the deployment's alone
126
- * under a `toolPolicy` lock. One resolution for the preflight above and for the resume ladder's pre-claim door.
208
+ * under a `toolPolicy` lock — one resolution, read by the preflight above (which a resume enters before its claim).
127
209
  */
128
210
  export declare function callerPolicySeats(spec: Pick<TaskSpec, "toolPolicy" | "basePolicyForResumeEdit">, deps: Pick<RunnerDeps, "toolPolicy" | "basePolicyForResumeEdit">, toolPolicyLocked: boolean): Pick<LockedPreflight, "toolPolicy" | "basePolicyForResumeEdit">;
129
- /** The retired-member door over the resolved caller policies: a member the engine no longer reads is refused by name
130
- * (`config.deps_retired_key`), never dropped in silence. */
131
- export declare function assertCallerPoliciesCurrent(policies: Pick<LockedPreflight, "toolPolicy" | "basePolicyForResumeEdit">): void;
@@ -1,4 +1,8 @@
1
- import { readMcpToolAxes } from "./mcp-server-spec.js";
1
+ import { types } from "node:util";
2
+ import { DECLARATION_PATH_RULES, NO_DECLARATION_PATH_RULES, declarationFaultOf, declarerWord, readToolAxes, receiveDeclaration, toolAxesRefusal } from "./mcp-server-spec.js";
3
+ import { isPlainRecord, receive } from "./mods/kernel/receive.js";
4
+ import { readToolFaces } from "./tool-face-reader.js";
5
+ import { CONFIG_RECEIPT_MODE } from "./own-property-receipt.js";
2
6
  import { isToolContentOrigin, TOOL_CONTENT_ORIGINS } from "./memory-engine/content-origin.js";
3
7
  import { inlineUntrusted } from "./untrusted-text.js";
4
8
  import { assertNoRetiredToolPolicyKeys } from "./retired-keys.js";
@@ -8,6 +12,7 @@ export const LOCKED_KEY_REGISTRY = {
8
12
  compliancePosture: { specFields: [] },
9
13
  retentionPolicy: { specFields: [] },
10
14
  };
15
+ export const ENTER_AS_IS = (_seat, door) => door();
11
16
  function codedError(code, message) {
12
17
  const e = new Error(message);
13
18
  e.code = code;
@@ -17,21 +22,74 @@ export function resolveLockedKeys(config) {
17
22
  if (config === undefined)
18
23
  return new Set();
19
24
  const keys = new Set();
20
- for (const key of config.keys) {
21
- if (!Object.prototype.hasOwnProperty.call(LOCKED_KEY_REGISTRY, key)) {
22
- throw codedError("config.locked_registry", `lockedConfig.keys contains "${String(key)}", which is not a member of the locked-key registry ` +
23
- `(${Object.keys(LOCKED_KEY_REGISTRY).join(", ")}). A lock that does not parse must not half-arm — ` +
24
- `fix the deployment's lock declaration.`);
25
+ const listed = declaredLockKeys(config);
26
+ for (let index = 0; index < listed.length; index++) {
27
+ const key = listed[index];
28
+ if (!Object.hasOwn(LOCKED_KEY_REGISTRY, key)) {
29
+ throw lockDeclarationRefusal(`RunnerDeps.lockedConfig.keys[${index}]`, `is ${inlineUntrusted(JSON.stringify(key), 80)}, which is not a member of the locked-key registry (${Object.keys(LOCKED_KEY_REGISTRY).join(", ")})`);
25
30
  }
26
31
  keys.add(key);
27
32
  }
28
33
  return keys;
29
34
  }
35
+ function declaredLockKeys(config) {
36
+ const received = receiveDeclaration(config, NO_DECLARATION_PATH_RULES);
37
+ if (!received.ok)
38
+ throw lockDeclarationRefusal(memberPath(received.fault.path, "RunnerDeps.lockedConfig"), `cannot be read: ${received.fault.problem}`);
39
+ const copy = received.value;
40
+ if (!isPlainRecord(copy))
41
+ throw lockDeclarationRefusal("RunnerDeps.lockedConfig", `is ${inlineUntrusted(describeDeclared(copy), 80)}, not an object with a \`keys\` list`);
42
+ const keys = Object.hasOwn(copy, "keys") ? copy["keys"] : undefined;
43
+ if (!Array.isArray(keys))
44
+ throw lockDeclarationRefusal("RunnerDeps.lockedConfig.keys", `is ${inlineUntrusted(describeDeclared(keys), 80)}, not a list of locked keys`);
45
+ return keys;
46
+ }
47
+ function lockDeclarationRefusal(member, problem) {
48
+ return codedError("config.locked_registry", `${member} ${problem}. A lock that does not parse must not half-arm — fix the deployment's lock declaration.`);
49
+ }
30
50
  export function snapshotMcpEntries(entries) {
31
- return entries.map((entry) => {
32
- const { toolAxes, ...rest } = entry;
33
- return Object.freeze((toolAxes === undefined ? rest : { ...rest, toolAxes }));
34
- });
51
+ return entries.map((entry) => snapshotDeclaredEntry(entry, "mcp"));
52
+ }
53
+ function memberPath(path, base = "") {
54
+ let rendered = base;
55
+ for (const key of path) {
56
+ const name = typeof key === "string" && /^\d+$/.test(key) ? JSON.stringify(key) : String(key);
57
+ rendered += typeof key === "number" ? `[${key}]` : rendered === "" ? name : `.${name}`;
58
+ if (rendered.length > 120 + base.length)
59
+ break;
60
+ }
61
+ return rendered;
62
+ }
63
+ function unreadableDeclaration(at, declarer, member, problem) {
64
+ const refusal = codedError("config.invalid_argument", `${declarerWord(at)}${declarer === undefined ? "" : ` ${declarerNameOf(declarer)}`} declares ${inlineUntrusted(member, 80)} the engine cannot read: ${problem}. A declaration the engine cannot read is refused ` +
65
+ `rather than partly applied — what the caller wrote and what the engine runs on would otherwise differ without anyone being told.`);
66
+ if (declarer !== undefined)
67
+ Object.defineProperty(refusal, "declarer", { value: declarer, enumerable: true, writable: false, configurable: false });
68
+ return refusal;
69
+ }
70
+ function ownDataString(holder, key) {
71
+ const slot = Object.getOwnPropertyDescriptor(holder, key);
72
+ const value = slot !== undefined && Object.hasOwn(slot, "value") ? slot.value : undefined;
73
+ return typeof value === "string" ? value : undefined;
74
+ }
75
+ export function refusedDeclarer(refusal) {
76
+ return typeof refusal === "object" && refusal !== null && !types.isProxy(refusal) ? ownDataString(refusal, "declarer") : undefined;
77
+ }
78
+ function capturedName(entry) {
79
+ return typeof entry === "object" && entry !== null && !types.isProxy(entry) ? ownDataString(entry, "name") : undefined;
80
+ }
81
+ function entryFaultRefusal(at, declarer, path, problem) {
82
+ return unreadableDeclaration(at, declarer, path.length === 0 ? "an entry" : memberPath(path), problem);
83
+ }
84
+ export function snapshotDeclaredEntry(entry, at) {
85
+ const declared = entry;
86
+ const declarer = capturedName(declared);
87
+ const received = receiveDeclaration(declared, DECLARATION_PATH_RULES[at]);
88
+ if (!received.ok)
89
+ throw entryFaultRefusal(at, declarer, received.fault.path, received.fault.problem);
90
+ if (!isPlainRecord(received.value))
91
+ throw unreadableDeclaration(at, declarer, "an entry", `it is ${inlineUntrusted(describeDeclared(received.value), 80)}, not an object`);
92
+ return received.value;
35
93
  }
36
94
  function describeDeclared(value) {
37
95
  try {
@@ -46,58 +104,117 @@ function describeDeclared(value) {
46
104
  }
47
105
  }
48
106
  }
49
- const serverNameOf = (copy) => inlineUntrusted(typeof copy.name === "string" ? JSON.stringify(copy.name) : describeDeclared(copy.name), 80);
50
- export function judgeEntryToolAxes(copy) {
107
+ const declarerNameOf = (name) => inlineUntrusted(typeof name === "string" ? JSON.stringify(name) : describeDeclared(name), 80);
108
+ export function judgeEntryToolAxes(copy, at) {
51
109
  const { toolAxes: declaredAxes, ...rest } = copy;
52
- const axes = readMcpToolAxes(declaredAxes);
53
- if (!axes.ok) {
54
- return {
55
- ok: false,
56
- error: codedError("config.invalid_argument", `MCP server ${serverNameOf(copy)} declares toolAxes the engine cannot read: ${inlineUntrusted(axes.problem, 240)}. A per-tool safety-axis declaration is the ` +
57
- `caller's trust root for the approval gate, so it is refused rather than partly applied — dropping one key or defaulting one word ` +
58
- `would leave a declaration that reads as in force while it is not.`),
59
- };
60
- }
110
+ const axes = readToolAxes(declaredAxes, at);
111
+ if (!axes.ok)
112
+ return { ok: false, error: toolAxesRefusal(at, declarerNameOf(copy.name), axes.problem) };
61
113
  if (axes.toolAxes !== undefined) {
62
114
  for (const perTool of Object.values(axes.toolAxes))
63
115
  Object.freeze(perTool);
64
116
  Object.freeze(axes.toolAxes);
65
117
  }
66
- return { ok: true, entry: Object.freeze(axes.toolAxes === undefined ? rest : { ...rest, toolAxes: axes.toolAxes }) };
118
+ return { ok: true, entry: Object.freeze((axes.toolAxes === undefined ? rest : { ...rest, toolAxes: axes.toolAxes })) };
119
+ }
120
+ function deepFrozen(value) {
121
+ if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
122
+ Object.freeze(value);
123
+ for (const member of Object.values(value))
124
+ deepFrozen(member);
125
+ }
126
+ return value;
67
127
  }
68
- function snapshotAndValidateMcpEntries(entries) {
69
- return snapshotMcpEntries(entries).map((copy) => {
128
+ function toolFacesRefusal(name, problem) {
129
+ return codedError("config.tool_face_invalid", `${declarerWord("mcp")} ${name} declares toolFaces the engine cannot read: ${inlineUntrusted(problem, 240)}. A per-tool face declaration is the deployment's own ` +
130
+ `narrowing (which argument names a tool's path, how a write is confined, which approval card and rule vocabulary the tool wears), so it is refused ` +
131
+ `rather than partly applied — a member dropped or a word defaulted would mount the tool without the protection its operator declared.`);
132
+ }
133
+ export function judgeEntryToolFaces(copy) {
134
+ const { toolFaces: declared, ...rest } = copy;
135
+ if (declared === undefined)
136
+ return { ok: true, entry: copy };
137
+ const faces = readToolFaces(declared);
138
+ if (!faces.ok)
139
+ return { ok: false, error: toolFacesRefusal(declarerNameOf(copy.name), faces.problem) };
140
+ return { ok: true, entry: Object.freeze((faces.faces === undefined ? rest : { ...rest, toolFaces: deepFrozen(faces.faces) })) };
141
+ }
142
+ export function judgeMcpEntryDeclarations(copy) {
143
+ const axes = judgeEntryToolAxes(copy, "mcp");
144
+ return axes.ok ? judgeEntryToolFaces(axes.entry) : axes;
145
+ }
146
+ function declaredEntriesOf(declared, at) {
147
+ const field = at;
148
+ const received = receive(declared, CONFIG_RECEIPT_MODE);
149
+ let problem;
150
+ if (received.ok) {
151
+ const list = received.value;
152
+ if (Array.isArray(list) && list !== declared)
153
+ return Array.from({ length: list.length }, (_, index) => list[index]);
154
+ problem = typeof list === "object" && list !== null ? "it is not an array" : `it is ${inlineUntrusted(describeDeclared(list), 80)}`;
155
+ }
156
+ else {
157
+ const fault = declarationFaultOf(received.fault);
158
+ if (fault.path.length > 0)
159
+ throw unreadableDeclaration(at, undefined, "an entry", fault.problem);
160
+ problem = fault.problem;
161
+ }
162
+ throw codedError("config.invalid_argument", `TaskSpec.${field} must be a readable array of ${declarerWord(at)} declarations; ${problem}. A list the engine cannot read ` +
163
+ `is refused rather than read as "no entries" — the declared ${declarerWord(at)}s would otherwise be dropped without anyone being told.`);
164
+ }
165
+ function snapshotAndValidateA2aEntries(declared) {
166
+ const entries = declaredEntriesOf(declared, "a2a");
167
+ if (entries.length === 0)
168
+ return undefined;
169
+ return entries.map((entry) => {
170
+ const judged = judgeEntryToolAxes(snapshotDeclaredEntry(entry, "a2a"), "a2a");
171
+ if (!judged.ok)
172
+ throw judged.error;
173
+ return judged.entry;
174
+ });
175
+ }
176
+ function snapshotAndValidateMcpEntries(declared) {
177
+ return snapshotMcpEntries(declaredEntriesOf(declared, "mcp")).map((copy) => {
70
178
  const declared = copy.contentOrigin;
71
179
  if (declared !== undefined && !isToolContentOrigin(declared)) {
72
- throw codedError("config.mcp_content_class", `MCP server ${serverNameOf(copy)} declares contentOrigin ` +
180
+ throw codedError("config.mcp_content_class", `MCP server ${declarerNameOf(copy.name)} declares contentOrigin ` +
73
181
  `${inlineUntrusted(describeDeclared(declared), 80)}, which is not one of ` +
74
182
  `${TOOL_CONTENT_ORIGINS.map((v) => `"${v}"`).join(" | ")}. The declaration decides whether this server's tools ` +
75
183
  `mark the session's memory externally exposed — a value this engine cannot read is refused at the door rather ` +
76
184
  `than folded to a class, because folding it would let a deployment believe a declaration is in force while every ` +
77
185
  `call keeps writing a one-way durable mark.`);
78
186
  }
79
- const judged = judgeEntryToolAxes(copy);
187
+ const judged = judgeMcpEntryDeclarations(copy);
80
188
  if (!judged.ok)
81
189
  throw judged.error;
82
190
  return judged.entry;
83
191
  });
84
192
  }
85
- export function preflightLockedConfig(spec, deps) {
86
- const lockedKeys = resolveLockedKeys(deps.lockedConfig);
87
- for (const key of lockedKeys) {
88
- for (const field of LOCKED_KEY_REGISTRY[key].specFields) {
89
- if (spec[field] !== undefined) {
90
- throw codedError("config.locked_key", `TaskSpec.${field} is administratively locked by this deployment (locked key "${key}") — ` +
91
- `the task-supplied value is refused wholesale (locks are config-time; there is no request-time ` +
92
- `unlock). Remove the field from the task, or change the deployment's lock configuration.`);
193
+ export function preflightLockedConfig(spec, deps, enter = ENTER_AS_IS) {
194
+ const lockedKeys = enter("deps", () => resolveLockedKeys(deps.lockedConfig));
195
+ enter("taskConfig", () => {
196
+ for (const key of lockedKeys) {
197
+ for (const field of LOCKED_KEY_REGISTRY[key].specFields) {
198
+ if (spec[field] !== undefined) {
199
+ throw codedError("config.locked_key", `TaskSpec.${field} is administratively locked by this deployment (locked key "${key}") — ` +
200
+ `the task-supplied value is refused wholesale (locks are config-time; there is no request-time ` +
201
+ `unlock). Remove the field from the task, or change the deployment's lock configuration.`);
202
+ }
93
203
  }
94
204
  }
95
- }
96
- const policies = callerPolicySeats(spec, deps, lockedKeys.has("toolPolicy"));
97
- assertCallerPoliciesCurrent(policies);
205
+ });
206
+ const toolPolicyLocked = lockedKeys.has("toolPolicy");
207
+ const task = toolPolicyLocked ? {} : { toolPolicy: spec.toolPolicy, basePolicyForResumeEdit: spec.basePolicyForResumeEdit };
208
+ const policies = callerPolicySeats(task, deps, toolPolicyLocked);
209
+ enter(!toolPolicyLocked && task.toolPolicy != null ? "taskConfig" : "deps", () => assertNoRetiredToolPolicyKeys(policies.toolPolicy));
210
+ enter(!toolPolicyLocked && task.basePolicyForResumeEdit != null ? "taskConfig" : "deps", () => assertNoRetiredToolPolicyKeys(policies.basePolicyForResumeEdit));
211
+ const declaredServers = lockedKeys.has("mcp") ? undefined : spec.mcp;
212
+ const mcp = declaredServers === undefined ? undefined : enter("taskConfig", () => snapshotAndValidateMcpEntries(declaredServers));
213
+ const declaredPeers = spec.a2a;
98
214
  return {
99
215
  lockedKeys,
100
- mcp: lockedKeys.has("mcp") || spec.mcp === undefined ? undefined : snapshotAndValidateMcpEntries(spec.mcp),
216
+ mcp,
217
+ a2a: declaredPeers === undefined ? undefined : enter("taskConfig", () => snapshotAndValidateA2aEntries(declaredPeers)),
101
218
  ...policies,
102
219
  };
103
220
  }
@@ -107,7 +224,3 @@ export function callerPolicySeats(spec, deps, toolPolicyLocked) {
107
224
  basePolicyForResumeEdit: toolPolicyLocked ? deps.basePolicyForResumeEdit : (spec.basePolicyForResumeEdit ?? deps.basePolicyForResumeEdit),
108
225
  };
109
226
  }
110
- export function assertCallerPoliciesCurrent(policies) {
111
- assertNoRetiredToolPolicyKeys(policies.toolPolicy);
112
- assertNoRetiredToolPolicyKeys(policies.basePolicyForResumeEdit);
113
- }
@@ -260,7 +260,7 @@ export interface LspServerManager {
260
260
  * ExecutionEnv (the SAME sandbox the agent runs in) — core passes it because a real in-env language
261
261
  * server must run inside that sandbox, and core already holds the per-task env at mount time. Supplying
262
262
  * it here means a manager can be **stateless** (it doesn't need a sessionId→env registry). It is
263
- * optional/last for back-compat: a manager that resolves the env another way (e.g. a sessionId registry
263
+ * optional/last by contract: a manager that resolves the env another way (e.g. a sessionId registry
264
264
  * keyed off `executionEnvFactory`) may ignore it.
265
265
  */
266
266
  sessionFor(filePath: string, signal?: AbortSignal, env?: ExecutionEnv): Promise<LspSession | undefined>;
@@ -128,5 +128,5 @@ export async function probeMcpServers(specs, opts = {}) {
128
128
  fatal = { cause: abortCause(signal) };
129
129
  if (fatal !== undefined)
130
130
  throw fatal.cause;
131
- return { entries: mcpManifestEntries(declared, statuses), servers };
131
+ return { entries: mcpManifestEntries(declared, statuses, servers.flatMap((s) => s?.tools ?? [])), servers };
132
132
  }