@sema-agent/core 7.48.0 → 7.49.1

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 (421) hide show
  1. package/CHANGELOG.md +457 -0
  2. package/README.md +2 -2
  3. package/dist/agents/cascade.d.ts +2 -5
  4. package/dist/agents/child-failure.d.ts +20 -0
  5. package/dist/agents/child-failure.js +28 -0
  6. package/dist/agents/cross-session-ref.d.ts +11 -3
  7. package/dist/agents/cross-session-ref.js +6 -0
  8. package/dist/agents/cumulative-stats.d.ts +12 -15
  9. package/dist/agents/cumulative-stats.js +1 -1
  10. package/dist/agents/extra-tool-names.js +1 -1
  11. package/dist/agents/inherited-tool-pool.d.ts +17 -13
  12. package/dist/agents/inherited-tool-pool.js +4 -3
  13. package/dist/agents/peer-session-drain.d.ts +3 -2
  14. package/dist/agents/peer-session-drain.js +5 -4
  15. package/dist/agents/send-message-tool.js +2 -9
  16. package/dist/agents/subagent.d.ts +10 -29
  17. package/dist/agents/subagent.js +65 -53
  18. package/dist/agents/suspend-guard.d.ts +1 -1
  19. package/dist/agents/tool-filter.d.ts +76 -65
  20. package/dist/agents/tool-filter.js +122 -92
  21. package/dist/agents/verify.d.ts +4 -7
  22. package/dist/agents/verify.js +6 -7
  23. package/dist/bench/metrics.d.ts +1 -2
  24. package/dist/brain/anthropic.js +133 -79
  25. package/dist/brain/errors.d.ts +53 -6
  26. package/dist/brain/errors.js +22 -2
  27. package/dist/brain/input-too-long.d.ts +40 -19
  28. package/dist/brain/input-too-long.js +3 -16
  29. package/dist/brain/open-responses.js +19 -10
  30. package/dist/brain/openai.js +123 -32
  31. package/dist/brain/provider-dialect.d.ts +214 -0
  32. package/dist/brain/provider-dialect.js +186 -0
  33. package/dist/brain/request-params.d.ts +27 -8
  34. package/dist/brain/request-params.js +7 -12
  35. package/dist/brain/request-route.d.ts +84 -0
  36. package/dist/brain/request-route.js +41 -0
  37. package/dist/brain/retry.d.ts +8 -0
  38. package/dist/brain/retry.js +10 -4
  39. package/dist/brain/route-adjudicator.d.ts +7 -0
  40. package/dist/brain/route-adjudicator.js +5 -1
  41. package/dist/brain/status-sink.d.ts +20 -0
  42. package/dist/brain/status-sink.js +11 -0
  43. package/dist/brain/stream-engine.d.ts +52 -1
  44. package/dist/brain/stream-engine.js +81 -24
  45. package/dist/brain/stream-shared.d.ts +57 -11
  46. package/dist/brain/stream-shared.js +28 -2
  47. package/dist/core/a2a.d.ts +1 -0
  48. package/dist/core/agent-config-dir.js +3 -2
  49. package/dist/core/allow-entry.d.ts +21 -12
  50. package/dist/core/allow-entry.js +38 -31
  51. package/dist/core/arg-summary.d.ts +21 -28
  52. package/dist/core/arg-summary.js +235 -88
  53. package/dist/core/ask-origin.d.ts +42 -24
  54. package/dist/core/ask-origin.js +18 -13
  55. package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
  56. package/dist/core/auto-mode-prompt-assets.js +1 -1
  57. package/dist/core/auto-mode-prompt.d.ts +11 -1
  58. package/dist/core/auto-mode-prompt.js +13 -10
  59. package/dist/core/auto-mode-rebuild.d.ts +7 -0
  60. package/dist/core/auto-mode-rebuild.js +4 -1
  61. package/dist/core/canonical-json.d.ts +3 -0
  62. package/dist/core/canonical-json.js +2 -1
  63. package/dist/core/checkpoint-requirements.d.ts +892 -0
  64. package/dist/core/checkpoint-requirements.js +333 -0
  65. package/dist/core/checkpoint-store.d.ts +94 -65
  66. package/dist/core/checkpoint-store.js +14 -26
  67. package/dist/core/child-clamp-seats.d.ts +228 -69
  68. package/dist/core/child-clamp-seats.js +211 -17
  69. package/dist/core/classifier-unavailable-arm.d.ts +5 -1
  70. package/dist/core/classifier-unavailable-arm.js +3 -3
  71. package/dist/core/compliance.d.ts +3 -0
  72. package/dist/core/compliance.js +9 -4
  73. package/dist/core/credential-vocabulary.d.ts +140 -0
  74. package/dist/core/credential-vocabulary.js +131 -0
  75. package/dist/core/decision-receipt.js +1 -1
  76. package/dist/core/effective-path-target.d.ts +73 -16
  77. package/dist/core/effective-path-target.js +63 -33
  78. package/dist/core/engine-notice.d.ts +43 -11
  79. package/dist/core/file-history-store.js +2 -2
  80. package/dist/core/fs-write-gate-policy.d.ts +4 -0
  81. package/dist/core/fs-write-gate-policy.js +22 -5
  82. package/dist/core/gate-bounded-await.d.ts +28 -13
  83. package/dist/core/gate-bounded-await.js +41 -4
  84. package/dist/core/gate-fold.d.ts +1 -1
  85. package/dist/core/gate-fold.js +24 -25
  86. package/dist/core/gate-lanes.js +16 -15
  87. package/dist/core/gate-outcome.d.ts +20 -5
  88. package/dist/core/gate-outcome.js +5 -0
  89. package/dist/core/governance-codes.d.ts +11 -4
  90. package/dist/core/governance-codes.js +14 -3
  91. package/dist/core/hooks.d.ts +21 -0
  92. package/dist/core/locked-config.d.ts +8 -0
  93. package/dist/core/locked-config.js +14 -2
  94. package/dist/core/lsp.js +3 -5
  95. package/dist/core/mailbox-store.d.ts +3 -4
  96. package/dist/core/mcp-connection-pool.d.ts +75 -0
  97. package/dist/core/mcp-connection-pool.js +128 -0
  98. package/dist/core/mcp-failure.d.ts +2 -1
  99. package/dist/core/mcp-failure.js +13 -2
  100. package/dist/core/mcp.d.ts +4 -0
  101. package/dist/core/mcp.js +70 -43
  102. package/dist/core/memory-engine/content-origin.js +2 -14
  103. package/dist/core/memory-engine/engine.d.ts +32 -22
  104. package/dist/core/memory-engine/engine.js +103 -55
  105. package/dist/core/memory-engine/file-backend.d.ts +13 -30
  106. package/dist/core/memory-engine/file-backend.js +37 -252
  107. package/dist/core/memory-engine/index.d.ts +1 -1
  108. package/dist/core/memory-engine/index.js +1 -1
  109. package/dist/core/memory-engine/layout.d.ts +139 -9
  110. package/dist/core/memory-engine/layout.js +350 -7
  111. package/dist/core/memory-engine/scope-contract.d.ts +10 -0
  112. package/dist/core/memory-engine/scope-contract.js +8 -0
  113. package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
  114. package/dist/core/memory-engine/session-incarnation.js +5 -2
  115. package/dist/core/memory-engine/types.d.ts +10 -1
  116. package/dist/core/memory.js +5 -17
  117. package/dist/core/model-seat.d.ts +4 -4
  118. package/dist/core/model-seat.js +1 -1
  119. package/dist/core/mounted-mcp.d.ts +57 -0
  120. package/dist/core/mounted-mcp.js +19 -0
  121. package/dist/core/namespaced-rule-name.d.ts +29 -0
  122. package/dist/core/namespaced-rule-name.js +52 -0
  123. package/dist/core/park-selfcheck.d.ts +1 -1
  124. package/dist/core/park-selfcheck.js +16 -6
  125. package/dist/core/path-guard-rows.d.ts +99 -0
  126. package/dist/core/path-guard-rows.js +45 -0
  127. package/dist/core/path-identity.d.ts +22 -16
  128. package/dist/core/path-identity.js +10 -10
  129. package/dist/core/path-rule-binding.d.ts +2 -1
  130. package/dist/core/path-rule-binding.js +3 -1
  131. package/dist/core/pause-registry.d.ts +16 -2
  132. package/dist/core/pause-registry.js +6 -0
  133. package/dist/core/permission-rule-consent.js +0 -7
  134. package/dist/core/permission-rule-model.d.ts +68 -24
  135. package/dist/core/permission-rule-model.js +105 -24
  136. package/dist/core/permission-rule-store.js +2 -2
  137. package/dist/core/permission-rules.d.ts +3 -30
  138. package/dist/core/permission-rules.js +4 -54
  139. package/dist/core/read-root-candidate.d.ts +1 -3
  140. package/dist/core/read-root-candidate.js +4 -15
  141. package/dist/core/real-approval.d.ts +4 -3
  142. package/dist/core/retired-keys.d.ts +13 -0
  143. package/dist/core/retired-keys.js +14 -0
  144. package/dist/core/roles.d.ts +2 -3
  145. package/dist/core/roles.js +15 -6
  146. package/dist/core/runner/assemble-result.d.ts +0 -7
  147. package/dist/core/runner/assemble-result.js +19 -31
  148. package/dist/core/runner/caller-roster.d.ts +78 -0
  149. package/dist/core/runner/caller-roster.js +219 -0
  150. package/dist/core/runner/checkpoint-scope.d.ts +16 -16
  151. package/dist/core/runner/checkpoint-scope.js +2 -2
  152. package/dist/core/runner/child-tree.js +2 -2
  153. package/dist/core/runner/contracts.d.ts +68 -35
  154. package/dist/core/runner/decide-continuation.d.ts +51 -3
  155. package/dist/core/runner/decide-continuation.js +76 -21
  156. package/dist/core/runner/denial-limit-arms.d.ts +17 -5
  157. package/dist/core/runner/denial-limit-arms.js +11 -9
  158. package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
  159. package/dist/core/runner/extra-body-dropped-seat.js +22 -0
  160. package/dist/core/runner/finish-reason-seat.d.ts +9 -0
  161. package/dist/core/runner/finish-reason-seat.js +40 -0
  162. package/dist/core/runner/gate-exit.d.ts +44 -32
  163. package/dist/core/runner/gate-exit.js +112 -25
  164. package/dist/core/runner/inherited-ask-grants.js +6 -4
  165. package/dist/core/runner/mcp-redial.d.ts +3 -3
  166. package/dist/core/runner/mcp-redial.js +3 -4
  167. package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
  168. package/dist/core/runner/permission-rule-lanes.js +5 -4
  169. package/dist/core/runner/prepare-artifact.d.ts +4 -0
  170. package/dist/core/runner/prepare-artifact.js +2 -1
  171. package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
  172. package/dist/core/runner/prepare-ask-lane.js +12 -6
  173. package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
  174. package/dist/core/runner/prepare-boundary-parks.js +4 -7
  175. package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
  176. package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
  177. package/dist/core/runner/prepare-config-doors.d.ts +9 -9
  178. package/dist/core/runner/prepare-config-doors.js +10 -5
  179. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  180. package/dist/core/runner/prepare-defer-classify.js +5 -3
  181. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  182. package/dist/core/runner/prepare-delegation-surface.js +3 -3
  183. package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
  184. package/dist/core/runner/prepare-gate-stations.js +9 -7
  185. package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
  186. package/dist/core/runner/prepare-hands-readface.js +7 -7
  187. package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
  188. package/dist/core/runner/prepare-inherited-gate.js +14 -15
  189. package/dist/core/runner/prepare-listings.d.ts +7 -24
  190. package/dist/core/runner/prepare-listings.js +4 -5
  191. package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
  192. package/dist/core/runner/prepare-memory-engine-session.js +4 -3
  193. package/dist/core/runner/prepare-memory.d.ts +6 -0
  194. package/dist/core/runner/prepare-memory.js +13 -13
  195. package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
  196. package/dist/core/runner/prepare-offload-wrappers.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.d.ts +7 -8
  198. package/dist/core/runner/prepare-park-ask.js +17 -10
  199. package/dist/core/runner/prepare-policy-chain.js +24 -44
  200. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  201. package/dist/core/runner/prepare-project-context.js +3 -4
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -4
  204. package/dist/core/runner/prepare-run-refs.d.ts +3 -0
  205. package/dist/core/runner/prepare-run-refs.js +2 -1
  206. package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
  207. package/dist/core/runner/prepare-safety-scan.js +1 -2
  208. package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
  209. package/dist/core/runner/prepare-suspend-saga.js +28 -12
  210. package/dist/core/runner/prepare-task.d.ts +1 -1
  211. package/dist/core/runner/prepare-task.js +67 -56
  212. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
  213. package/dist/core/runner/prepare-wiring-manifest.js +5 -8
  214. package/dist/core/runner/resume-apply.js +7 -14
  215. package/dist/core/runner/resume-checkpoint-screen.js +11 -4
  216. package/dist/core/runner/resume-internals-and-config.js +2 -2
  217. package/dist/core/runner/resume-policy-outcome.js +5 -32
  218. package/dist/core/runner/resume-review-outcome.js +4 -1
  219. package/dist/core/runner/retired-runner-deps.d.ts +6 -0
  220. package/dist/core/runner/retired-runner-deps.js +10 -1
  221. package/dist/core/runner/run-brain-sinks.d.ts +4 -0
  222. package/dist/core/runner/run-brain-sinks.js +5 -2
  223. package/dist/core/runner/run-harness-handlers.js +29 -36
  224. package/dist/core/runner/run-leg.d.ts +0 -2
  225. package/dist/core/runner/run-leg.js +32 -33
  226. package/dist/core/runner/run-terminal-adoption.js +4 -3
  227. package/dist/core/runner/runtask.js +8 -8
  228. package/dist/core/runner/session-rule-policy.js +5 -5
  229. package/dist/core/runner/stream-settle-backstop.js +7 -4
  230. package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
  231. package/dist/core/runner/sub-agent-deny-lists.js +111 -80
  232. package/dist/core/runner/terminal-account.d.ts +48 -0
  233. package/dist/core/runner/terminal-account.js +186 -0
  234. package/dist/core/runner/tool-end-body.d.ts +54 -17
  235. package/dist/core/runner/tool-end-body.js +27 -2
  236. package/dist/core/runner/tool-face-overlay.d.ts +3 -1
  237. package/dist/core/runner/tool-face-overlay.js +6 -4
  238. package/dist/core/runner/tool-occurrence.d.ts +106 -0
  239. package/dist/core/runner/tool-occurrence.js +123 -0
  240. package/dist/core/runner/tool-progress-lane.d.ts +16 -8
  241. package/dist/core/runner/tool-progress-lane.js +25 -34
  242. package/dist/core/runner/tool-turn-repair-seat.d.ts +28 -4
  243. package/dist/core/runner/tool-turn-repair-seat.js +36 -13
  244. package/dist/core/runner/turn-attachments.d.ts +1 -1
  245. package/dist/core/runner/write-protection-station.d.ts +21 -9
  246. package/dist/core/runner/write-protection-station.js +20 -17
  247. package/dist/core/runner-deps.d.ts +12 -22
  248. package/dist/core/secret-env.d.ts +16 -5
  249. package/dist/core/secret-env.js +23 -63
  250. package/dist/core/sensitive-path-policy.d.ts +19 -18
  251. package/dist/core/sensitive-path-policy.js +73 -49
  252. package/dist/core/session-reconcile.d.ts +3 -0
  253. package/dist/core/session-reconcile.js +5 -4
  254. package/dist/core/session-store.d.ts +5 -0
  255. package/dist/core/session-store.js +12 -0
  256. package/dist/core/session.d.ts +13 -0
  257. package/dist/core/shared-memory/normalize.d.ts +17 -4
  258. package/dist/core/shared-memory/normalize.js +7 -7
  259. package/dist/core/shell-lexer.js +5 -4
  260. package/dist/core/shell-wrapper-projection.js +1 -1
  261. package/dist/core/skill-grant.js +1 -1
  262. package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
  263. package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
  264. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
  265. package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
  266. package/dist/core/stub-env.d.ts +2 -0
  267. package/dist/core/stub-env.js +1 -0
  268. package/dist/core/synthetic-read-verdict.js +2 -2
  269. package/dist/core/task-event.d.ts +59 -5
  270. package/dist/core/task-result.d.ts +1 -1
  271. package/dist/core/task-spec.d.ts +42 -9
  272. package/dist/core/terminal-cause.d.ts +94 -11
  273. package/dist/core/tighten-task-spec.d.ts +5 -4
  274. package/dist/core/tighten-task-spec.js +20 -25
  275. package/dist/core/tool-catalog-entries.d.ts +5 -7
  276. package/dist/core/tool-catalog-entries.js +14 -15
  277. package/dist/core/tool-face.d.ts +47 -0
  278. package/dist/core/tool-face.js +5 -0
  279. package/dist/core/tool-model-gate.d.ts +5 -5
  280. package/dist/core/tool-name-aliases.d.ts +26 -2
  281. package/dist/core/tool-name-aliases.js +13 -0
  282. package/dist/core/tool-name-list.d.ts +30 -0
  283. package/dist/core/tool-name-list.js +55 -0
  284. package/dist/core/tool-name-readings.d.ts +1 -9
  285. package/dist/core/tool-name-readings.js +1 -3
  286. package/dist/core/tool-policy.d.ts +195 -85
  287. package/dist/core/tool-policy.js +165 -380
  288. package/dist/core/tool-registry.d.ts +44 -1
  289. package/dist/core/tool-registry.js +26 -0
  290. package/dist/core/tool-roster.d.ts +19 -1
  291. package/dist/core/tool-roster.js +21 -2
  292. package/dist/core/tool-spec-members.d.ts +45 -0
  293. package/dist/core/tool-spec-members.js +169 -0
  294. package/dist/core/tool-spec.d.ts +46 -8
  295. package/dist/core/tools.d.ts +6 -2
  296. package/dist/core/tools.js +8 -4
  297. package/dist/core/untrusted-egress.js +29 -26
  298. package/dist/core/untrusted-text.d.ts +13 -10
  299. package/dist/core/untrusted-text.js +6 -44
  300. package/dist/core/workflow-journal-store.d.ts +4 -1
  301. package/dist/core/write-protect.d.ts +65 -29
  302. package/dist/core/write-protect.js +235 -177
  303. package/dist/engine/compaction/compaction.d.ts +2 -2
  304. package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
  305. package/dist/engine/execution-env/node-execution-env.js +16 -38
  306. package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
  307. package/dist/engine/execution-env/symlink-chain.js +32 -0
  308. package/dist/engine/harness/types.d.ts +7 -0
  309. package/dist/engine/llm/tool-turns.d.ts +3 -0
  310. package/dist/engine/llm/tool-turns.js +1 -0
  311. package/dist/engine/llm/types.d.ts +59 -23
  312. package/dist/engine/loop/agent-loop.js +16 -2
  313. package/dist/engine/session/memory-repo.js +11 -3
  314. package/dist/engine/session/repo-utils.d.ts +8 -0
  315. package/dist/engine/session/repo-utils.js +13 -0
  316. package/dist/index.d.ts +20 -23
  317. package/dist/index.js +17 -20
  318. package/dist/internal/harness.d.ts +1 -1
  319. package/dist/internal/harness.js +1 -1
  320. package/dist/internal/llm.d.ts +2 -2
  321. package/dist/internal/llm.js +1 -1
  322. package/dist/orchestration/governance-baseline-validity.js +1 -1
  323. package/dist/orchestration/run-spec.js +3 -3
  324. package/dist/orchestration/run-workflow-tool.d.ts +16 -35
  325. package/dist/orchestration/run-workflow-tool.js +38 -78
  326. package/dist/orchestration/workflow-governance.d.ts +21 -25
  327. package/dist/orchestration/workflow-governance.js +38 -78
  328. package/dist/orchestration/workflow-primitives.d.ts +7 -22
  329. package/dist/orchestration/workflow-primitives.js +5 -12
  330. package/dist/orchestration/workflow-types.d.ts +1 -1
  331. package/dist/orchestration/workflow.d.ts +61 -11
  332. package/dist/orchestration/workflow.js +189 -117
  333. package/dist/scenarios/full-body.d.ts +1 -1
  334. package/dist/shell/lex.d.ts +0 -21
  335. package/dist/shell/lex.js +250 -68
  336. package/dist/shell/shell-words.d.ts +35 -0
  337. package/dist/shell/shell-words.js +65 -0
  338. package/dist/stores/file/checkpoint-store.d.ts +5 -5
  339. package/dist/stores/file/checkpoint-store.js +5 -2
  340. package/dist/stores/file/index.d.ts +0 -8
  341. package/dist/stores/file/index.js +0 -12
  342. package/dist/stores/file/session-store.d.ts +3 -0
  343. package/dist/stores/file/session-store.js +44 -19
  344. package/dist/tools/artifact/artifact-tool.js +2 -0
  345. package/dist/tools/fs/bash-program-position.d.ts +12 -4
  346. package/dist/tools/fs/bash-program-position.js +32 -9
  347. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  348. package/dist/tools/fs/bash-readonly-classifier.js +8 -7
  349. package/dist/tools/fs/containment-face.d.ts +4 -0
  350. package/dist/tools/fs/containment-face.js +3 -0
  351. package/dist/tools/fs/fs-shared.d.ts +10 -0
  352. package/dist/tools/fs/fs-shared.js +18 -0
  353. package/dist/tools/fs/fs-write.js +4 -2
  354. package/dist/tools/fs/index.d.ts +1 -1
  355. package/dist/tools/fs/program-read-model.d.ts +46 -20
  356. package/dist/tools/fs/program-read-model.js +50 -20
  357. package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
  358. package/dist/tools/fs/program-read-model.rows.js +16 -15
  359. package/dist/tools/fs/read-deny-home.d.ts +33 -47
  360. package/dist/tools/fs/read-deny-home.js +35 -49
  361. package/dist/tools/fs/read-deny.d.ts +23 -22
  362. package/dist/tools/fs/read-deny.js +114 -89
  363. package/dist/tools/fs/removal-targets.d.ts +56 -23
  364. package/dist/tools/fs/removal-targets.js +65 -87
  365. package/dist/tools/fs/safety.d.ts +29 -22
  366. package/dist/tools/fs/safety.js +17 -11
  367. package/dist/tools/fs/search.js +10 -8
  368. package/dist/tools/fs/shell-read-argv.js +1 -1
  369. package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
  370. package/dist/tools/fs/shell-read-bindings.js +324 -86
  371. package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
  372. package/dist/tools/fs/shell-read-builtins.js +43 -0
  373. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  374. package/dist/tools/fs/shell-read-check.js +67 -43
  375. package/dist/tools/fs/shell-read-establish.js +6 -6
  376. package/dist/tools/fs/shell-read-functions.d.ts +22 -0
  377. package/dist/tools/fs/shell-read-functions.js +29 -0
  378. package/dist/tools/fs/shell-read-judge.d.ts +51 -18
  379. package/dist/tools/fs/shell-read-judge.js +40 -4
  380. package/dist/tools/fs/shell-read-plan.d.ts +12 -6
  381. package/dist/tools/fs/shell-read-plan.js +118 -113
  382. package/dist/tools/fs/shell-read-start.d.ts +29 -6
  383. package/dist/tools/fs/shell-read-start.js +16 -9
  384. package/dist/tools/fs/shell-read-word.js +4 -0
  385. package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
  386. package/dist/tools/fs/shell-read-workdir.js +281 -0
  387. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
  388. package/dist/tools/fs/shell-read-write-targets.js +14 -5
  389. package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
  390. package/dist/tools/fs/shell-reentry-text.js +3 -11
  391. package/dist/tools/task-list.d.ts +1 -2
  392. package/dist/tools/web.js +1 -1
  393. package/package.json +4 -1
  394. package/test/export-surface.snapshot.json +107 -143
  395. package/wire-changes/core@7.49.0.json +4765 -0
  396. package/dist/agents/teacher.d.ts +0 -159
  397. package/dist/agents/teacher.js +0 -577
  398. package/dist/agents/team.d.ts +0 -226
  399. package/dist/agents/team.js +0 -257
  400. package/dist/core/strategy-store.d.ts +0 -238
  401. package/dist/core/strategy-store.js +0 -253
  402. package/dist/orchestration/builtin-workflows.d.ts +0 -133
  403. package/dist/orchestration/builtin-workflows.js +0 -159
  404. package/dist/scenarios/env.d.ts +0 -40
  405. package/dist/scenarios/env.js +0 -39
  406. package/dist/scenarios/scenario-registry.d.ts +0 -110
  407. package/dist/scenarios/scenario-registry.js +0 -140
  408. package/dist/scenarios/teacher-quickstart.d.ts +0 -40
  409. package/dist/scenarios/teacher-quickstart.js +0 -12
  410. package/dist/stores/cc/lockfile.d.ts +0 -10
  411. package/dist/stores/cc/lockfile.js +0 -98
  412. package/dist/stores/cc/mailbox-store.d.ts +0 -20
  413. package/dist/stores/cc/mailbox-store.js +0 -255
  414. package/dist/stores/cc/roster-adapter.d.ts +0 -12
  415. package/dist/stores/cc/roster-adapter.js +0 -117
  416. package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
  417. package/dist/stores/cc/sidecar-transcript.js +0 -264
  418. package/dist/stores/cc/task-list-store.d.ts +0 -2
  419. package/dist/stores/cc/task-list-store.js +0 -125
  420. package/dist/stores/file/strategy-store.d.ts +0 -97
  421. package/dist/stores/file/strategy-store.js +0 -341
@@ -1,10 +1,11 @@
1
+ import { shellWordsOf } from "../shell/shell-words.js";
1
2
  import { evaluatesOperand, SHELL_WRAPPER_TABLE } from "./shell-wrapper-table.js";
2
3
  import { arithmeticHidesRun, carriesSubstitution, freshWord, matchOp, readDoubleQuoted, readExpansion, skipBalanced, skipHeredocBodies } from "./shell-scan.js";
3
4
  export { SHELL_WRAPPER_TABLE } from "./shell-wrapper-table.js";
4
5
  const WRAPPERS = new Map(Object.entries(SHELL_WRAPPER_TABLE));
5
- const PREFIX_KEYWORDS = new Set(["if", "then", "else", "elif", "while", "until", "do", "!"]);
6
- const CLOSING_KEYWORDS = new Set(["fi", "done", "esac", "}", "{", "]]"]);
7
- const OPAQUE_KEYWORDS = new Set(["for", "select", "case", "function", "coproc", "in"]);
6
+ const PREFIX_KEYWORDS = shellWordsOf("stripped_prefix");
7
+ const CLOSING_KEYWORDS = shellWordsOf("stripped_standalone");
8
+ const OPAQUE_KEYWORDS = shellWordsOf("opaque");
8
9
  const ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*(?:\[[^\]]*\])?\+?=/;
9
10
  const DURATION_RE = /^\d+(?:\.\d+)?[smhd]?$/;
10
11
  const REDIRECT_OPS = ["<<<", "<<-", "<<", "<>", "<&", "<", ">>", ">|", ">&", "&>>", "&>", ">"];
@@ -222,7 +223,7 @@ export function readShellCommand(source) {
222
223
  i = end;
223
224
  continue;
224
225
  }
225
- if (ch === "(" && source[i + 1] === "(" && (!word.started || word.raw === "!") && pendingRedirect === undefined && seg.words.every((w) => w.expands === false && (PREFIX_KEYWORDS.has(w.text) || CLOSING_KEYWORDS.has(w.text) || w.text === "time" || w.text === "-p"))) {
226
+ if (ch === "(" && source[i + 1] === "(" && (!word.started || word.raw === "!") && pendingRedirect === undefined && seg.words.every((w) => w.expands === false && (shellWordsOf("control_prefix").has(w.text) || CLOSING_KEYWORDS.has(w.text) || w.text === "-p"))) {
226
227
  endWord();
227
228
  const end = skipBalanced(source, i, "(", ")");
228
229
  if (end === undefined) {
@@ -38,7 +38,7 @@ function projectRow(row) {
38
38
  return executes.length > 0 ? { valueFlags: [], flags: [], commandString: [], executes } : undefined;
39
39
  }
40
40
  const commandString = sorted(all.filter((o) => o.spec.value?.kind === "shell-text").map((o) => o.spelling));
41
- const values = all.filter((o) => o.spec.value?.kind !== "shell-text");
41
+ const values = all.filter((o) => o.spec.value?.kind !== "shell-text" && o.spec.mode !== "queries");
42
42
  const attached = sorted(values.filter((o) => o.arity === "attached" && !o.spelling.startsWith("--")).map((o) => o.spelling));
43
43
  const required = row.launcher.requiredOperands ?? 0;
44
44
  const duration = required > 0 && row.launcher.requiredOperandSpelling === DURATION_SPELLING;
@@ -3,7 +3,7 @@ import { parsePermissionRule } from "./permission-rules.js";
3
3
  import { mountedToolPermissionSeat } from "./mode-policy.js";
4
4
  import { inlineUntrusted } from "./untrusted-text.js";
5
5
  import { boundInputHashOf } from "./canonical-json.js";
6
- import { readToolNameList } from "../agents/tool-filter.js";
6
+ import { readToolNameList } from "./tool-name-list.js";
7
7
  import { allowingRows, autoStripped, compileAllowEntry, compileAllowList, inertEntryPhrase, judgeAllowEntries, nonListValueLabel, nonStringMemberLabel, renderListEntry } from "./allow-entry.js";
8
8
  export const SKILL_GRANT_ORIGINS = Object.freeze(["model", "user", "inherited"]);
9
9
  const FROZEN_SKILL_GRANT_ORIGINS = new Set(["model", "inherited"]);
@@ -405,6 +405,19 @@ export async function checkpointStoreContract(make, runAssertion) {
405
405
  assert.equal("askClass" in bare, false, "an absent askClass must stay OMITTED on the row");
406
406
  assert.equal("classes" in bare, false, "an absent classes must stay OMITTED on the row");
407
407
  });
408
+ run("#1320 requirement list: a v10 row's `requires` survives the round-trip verbatim (the whole array, in its order) beside its version; a row without one stays without", async () => {
409
+ const store = make();
410
+ const listed = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "rq-list", version: 10, requires: ["binding", "hands_coordinates", "requirements_digest"], requirementsDigest: "a".repeat(64) });
411
+ const bare = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "rq-none" });
412
+ for (const cp of [listed, bare])
413
+ await store.put(cp.token, cp);
414
+ const back = (await store.get(listed.token));
415
+ assert.equal(back.version, 10, "the frozen stamp must survive the round-trip");
416
+ assert.deepEqual(back.requires, ["binding", "hands_coordinates", "requirements_digest"], "the requirement list must survive the round-trip as the same array, in the same order");
417
+ assert.equal(back.requirementsDigest, "a".repeat(64), "the row digest must survive the round-trip verbatim");
418
+ const bareBack = (await store.get(bare.token));
419
+ assert.equal("requires" in bareBack || "requirementsDigest" in bareBack, false, "a row without a list or digest must stay without (never [] / null / \"\")");
420
+ });
408
421
  run("decider pair: a policy-decided allow resolves like a person's — its `policy_allowed` record (party `policy`, the attribution) round-trips verbatim on the resolved winner and as the execution record; the same record is already_recorded, a person's record for that row conflicts", async () => {
409
422
  const store = make();
410
423
  const cp = createCheckpointFixture();
@@ -6,22 +6,16 @@ import { type ContractAssertionRunner } from "./contract-harness.js";
6
6
  * `contract-harness.ts` for the runner seam and `contract-kit-version.ts` for version lockstep).
7
7
  *
8
8
  * 为什么这个家族有契约(结构性复发机制,两次实证):契约 harness 此前只喂 InMemory + File 两个
9
- * 后端,于是没进 harness 的那个后端反复独立漂移 ——
10
- * - RB-86:`stores/cc/mailbox-store.ts` 注释自陈 "the contract both bundled backends already
11
- * honor, missing only here"(空箱 claimLease 不返回 null,还装上幽灵租约);
12
- * - MB1:CC 后端的 `ack` 根本不查 lease 表,注释自陈 "worse than the other two
13
- * backends' pre-fix bug"。
14
- * 两次漏的都是同一个后端。修法不是再补一次点,而是把契约本身变成全后端共跑的资产 —— design/159
15
- * 再进一步:第三方后端也拿得到同一份资产。
9
+ * 后端,于是没进 harness 的第三个后端(已退役的 CC inbox 适配器)反复独立漂移 —— RB-86 空箱
10
+ * claimLease 不返回 null 还装上幽灵租约;MB1 `ack` 根本不查 lease 表。修法不是再补一次点,而是
11
+ * 把契约本身变成全后端共跑的资产 —— design/159 再进一步:第三方后端也拿得到同一份资产。
16
12
  *
17
13
  * 分层(与 test/mailbox-contract.ts 的挂载对应):
18
14
  * - T1 核心不变量层 = {@link mailboxStoreContract} + {@link mailboxAckOwnershipContract}:每个后端
19
15
  * 都必须过。任何后端在这一层变红 = 该后端违约,**不许通过放宽契约让它变绿**。核心层一律用
20
- * `scope: "default"`({@link MAILBOX_CONTRACT_SCOPE}),这样租户轴受限的后端(CC inbox 文件)
21
- * 也跑同一段代码。
16
+ * `scope: "default"`({@link MAILBOX_CONTRACT_SCOPE}),这样租户轴受限的第三方后端也跑同一段代码。
22
17
  * - T2 可选能力层 = {@link mailboxBundledOnlyContract}:多租户隔离 + 键注入性,只挂具备租户轴/
23
- * 自有键空间的后端(core 的两个捆绑后端;CC 按 D-3 边界豁免,豁免明细在
24
- * `src/stores/cc/mailbox-store.ts` 模块头)。
18
+ * 自有键空间的后端(core 的两个捆绑后端)。
25
19
  *
26
20
  * `make()` must return a FRESH, empty store each call. Stores a contract case creates are closed
27
21
  * (`close?.()`) when the case ends — the file backend's append fd is refcounted, so a harness that
@@ -41,9 +35,8 @@ export declare function mailboxStoreContract(mk: () => MailboxStore, runAssertio
41
35
  */
42
36
  export declare function mailboxAckOwnershipContract(mk: () => MailboxStore, runAssertion?: ContractAssertionRunner): Promise<void>;
43
37
  /**
44
- * T2 可选能力层 —— 只挂具备租户轴 + 自有键空间的后端(core 的两个捆绑后端)。CC 后端按其形态
45
- * 明文豁免(豁免明细在 `src/stores/cc/mailbox-store.ts` 模块头,D-3 裁定),不是「没做到所以
46
- * 放宽」:核心层没有一条为了任何后端而弱化。第三方后端若有租户轴,应当也挂这一层。
38
+ * T2 可选能力层 —— 只挂具备租户轴 + 自有键空间的后端(core 的两个捆绑后端)。核心层没有一条
39
+ * 为了任何后端而弱化。第三方后端若有租户轴,应当也挂这一层。
47
40
  */
48
41
  export declare function mailboxBundledOnlyContract(mk: () => MailboxStore, runAssertion?: ContractAssertionRunner): Promise<void>;
49
42
  export interface MailboxCrossProcessContractHooks {
@@ -0,0 +1,27 @@
1
+ import { type SessionStore } from "../session.js";
2
+ import { type ContractAssertionRunner } from "./contract-harness.js";
3
+ /**
4
+ * The cross-backend contract for the SESSION INCARNATION face ({@link SessionStore.incarnationOf}):
5
+ * the token naming WHICH occupant holds a session id. vitest-free (see `contract-harness.ts`); mount it
6
+ * on every store that implements `incarnationOf` — the face is optional on the interface, so this kit
7
+ * is a separate opt-in kit rather than a skipped case inside another one (a skipped case is how an
8
+ * unimplemented face reads green). A store WITHOUT the face fails every case loudly.
9
+ *
10
+ * The law the cases pin: the token is a cell of the session ROW, minted once when the row comes into
11
+ * existence and answered unchanged by every later read of that row — a cache forgetting the session or
12
+ * a second store over the same rows does not change it, deleting the row and handing the id out again
13
+ * does, a fork is a new occupant. A `SessionRepo` author runs it through the bundled cache layer
14
+ * (`make: () => new TtlSessionStore({ repo })`, `reopen: () => new TtlSessionStore({ repo })`).
15
+ *
16
+ * `make()` must return a FRESH, empty store each call; each case disposes the store it built.
17
+ */
18
+ export interface SessionStoreIncarnationContractHooks {
19
+ /** Delete `sessionId`'s session ROW through the backend's own deletion path (history and all), so
20
+ * that a later `acquire(sessionId)` creates a new session under the same id. */
21
+ deleteSession: (store: SessionStore, sessionId: string) => Promise<void>;
22
+ /** A SECOND, independent store instance over the same rows as `store` — what a restarted process or
23
+ * another replica constructs. The kit disposes it. */
24
+ reopen: (store: SessionStore) => SessionStore;
25
+ runAssertion?: ContractAssertionRunner;
26
+ }
27
+ export declare function sessionStoreIncarnationContract(make: () => SessionStore, hooks: SessionStoreIncarnationContractHooks): Promise<void>;
@@ -0,0 +1,92 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { hasSessionFork } from "../session.js";
3
+ import { beginContract } from "./contract-harness.js";
4
+ const ID_A = "0198fa00-0000-7000-8000-0000000014a1";
5
+ const ID_B = "0198fa00-0000-7000-8000-0000000014b2";
6
+ const ID_NONE = "0198fa00-0000-7000-8000-0000000014ff";
7
+ async function tokenOf(store, sessionId) {
8
+ assert.equal(typeof store.incarnationOf, "function", "this kit is for stores that implement SessionStore.incarnationOf");
9
+ return store.incarnationOf(sessionId);
10
+ }
11
+ function assertToken(token, what) {
12
+ assert.equal(typeof token, "string", `${what}: a session row created through this store must carry an incarnation`);
13
+ assert.notEqual(token, "", `${what}: an empty token names nobody`);
14
+ }
15
+ export async function sessionStoreIncarnationContract(make, hooks) {
16
+ const { run: runRaw, settle } = beginContract(hooks.runAssertion);
17
+ const run = (name, fn) => runRaw(name, async () => {
18
+ const store = make();
19
+ try {
20
+ await fn(store);
21
+ }
22
+ finally {
23
+ await store.dispose();
24
+ }
25
+ });
26
+ run("a session created through acquire carries a token, and every later read of the row answers the same one", async (store) => {
27
+ await store.acquire(ID_A);
28
+ const first = await tokenOf(store, ID_A);
29
+ assertToken(first, "after acquire");
30
+ await store.acquire(ID_A);
31
+ assert.equal(await tokenOf(store, ID_A), first, "a re-acquire reopens the same occupant");
32
+ await store.forget?.(ID_A);
33
+ assert.equal(await tokenOf(store, ID_A), first, "the token is the row's, not the cache's: forgetting the cached view must not change it");
34
+ await store.acquire(ID_A, { requireExisting: true });
35
+ assert.equal(await tokenOf(store, ID_A), first, "a require-existing reopen after the cache forgot it is still the same occupant");
36
+ });
37
+ run("the session acquire hands back carries the token incarnationOf answers — a run's memory seat reads the former", async (store) => {
38
+ const acquired = await store.acquire(ID_A);
39
+ const byId = await tokenOf(store, ID_A);
40
+ assertToken(byId, "by-id read");
41
+ assert.equal((await acquired.session.getMetadata()).incarnation, byId, "two faces of one row cell must agree, or the seat and the by-id read name two occupants");
42
+ });
43
+ run("a second store over the same rows (a restart, a replica) answers the same token — the token lives in the row, not in an instance", async (store) => {
44
+ await store.acquire(ID_A);
45
+ const first = await tokenOf(store, ID_A);
46
+ assertToken(first, "first instance");
47
+ const second = hooks.reopen(store);
48
+ try {
49
+ assert.equal(await tokenOf(second, ID_A), first, "an instance-local token would name a different occupant after every restart");
50
+ await second.acquire(ID_A, { requireExisting: true });
51
+ assert.equal(await tokenOf(second, ID_A), first, "reopening the session on the second instance is still the same occupant");
52
+ }
53
+ finally {
54
+ await second.dispose();
55
+ }
56
+ });
57
+ run("two sessions never share a token, and an id with no session answers undefined (unknown — not an error, not a token)", async (store) => {
58
+ await store.acquire(ID_A);
59
+ const generated = await store.acquire();
60
+ const a = await tokenOf(store, ID_A);
61
+ const g = await tokenOf(store, generated.sessionId);
62
+ assertToken(a, "explicit id");
63
+ assertToken(g, "generated id");
64
+ assert.notEqual(a, g, "two sessions are two occupants");
65
+ assert.equal(await tokenOf(store, ID_NONE), undefined, "no row ⇒ unknown");
66
+ });
67
+ run("a deleted id handed out again is a NEW occupant: a fresh token, never the previous occupant's", async (store) => {
68
+ await store.acquire(ID_A);
69
+ const before = await tokenOf(store, ID_A);
70
+ assertToken(before, "first occupant");
71
+ await hooks.deleteSession(store, ID_A);
72
+ assert.equal(await tokenOf(store, ID_A), undefined, "a deleted row has no occupant");
73
+ await store.acquire(ID_A);
74
+ const after = await tokenOf(store, ID_A);
75
+ assertToken(after, "second occupant");
76
+ assert.notEqual(after, before, "the id was recycled — the previous occupant's records must not bind this one");
77
+ });
78
+ run("a fork is a new occupant: a fresh token, and the source keeps its own", async (store) => {
79
+ if (!hasSessionFork(store))
80
+ return;
81
+ await store.acquire(ID_B);
82
+ const source = await tokenOf(store, ID_B);
83
+ assertToken(source, "fork source");
84
+ const forkedId = await store.fork(ID_B);
85
+ assert.ok(forkedId !== null, "forking an existing session returns the fork's id");
86
+ const forked = await tokenOf(store, forkedId);
87
+ assertToken(forked, "fork");
88
+ assert.notEqual(forked, source, "a fork copies the history, never the occupant");
89
+ assert.equal(await tokenOf(store, ID_B), source, "forking does not touch the source's occupant");
90
+ });
91
+ await settle();
92
+ }
@@ -12,6 +12,8 @@ export declare class StubExecutionEnv implements ExecutionEnv {
12
12
  * member off a `ExecutionEnv | StubExecutionEnv` union; the no-I/O stub produces no target
13
13
  * content, so it never declares (always `undefined` — the trusted-side default). */
14
14
  readonly externalContentTarget?: boolean;
15
+ /** `ExecutionEnv.cdPath`: the stub runs no shell, so no shell receives a `CDPATH`. */
16
+ readonly cdPath: readonly string[];
15
17
  constructor(cwd?: string);
16
18
  private fsErr;
17
19
  absolutePath(path: string): Promise<Result<string, FileError>>;
@@ -2,6 +2,7 @@ import { ExecutionError, FileError, err, } from "../internal/harness.js";
2
2
  export class StubExecutionEnv {
3
3
  cwd;
4
4
  externalContentTarget;
5
+ cdPath = [];
5
6
  constructor(cwd = "/") {
6
7
  this.cwd = cwd;
7
8
  }
@@ -1,4 +1,4 @@
1
- import { describeThrown, engineSettlementOf, refuseOutOfContractDecision } from "./tool-policy.js";
1
+ import { attestationOf, describeThrown, refuseOutOfContractDecision } from "./tool-policy.js";
2
2
  import { skillRowRefusal } from "./skill-grant.js";
3
3
  import { applyPersistedTightening } from "./persisted-rule-arms.js";
4
4
  import { readOrgRuleLane, readPersistedRuleLane } from "./rule-lane-reads.js";
@@ -62,7 +62,7 @@ export function createSyntheticReadJudge(layers) {
62
62
  }
63
63
  if (read.action !== "deny")
64
64
  return undefined;
65
- if (engineSettlementOf(d, req) !== undefined) {
65
+ if (attestationOf(read, req)?.settlement !== undefined) {
66
66
  cannot(layer, "approval_seat", "the policy would ask an approver, who is never consulted for a visibility probe");
67
67
  return undefined;
68
68
  }
@@ -9,6 +9,7 @@
9
9
  import type { ActorAssertion } from "../internal/llm.js";
10
10
  import type { TaskResult } from "./task-result.js";
11
11
  import type { TaskNotificationPayload } from "./task-notification.js";
12
+ import type { ProviderCondition, ProviderConditionSource } from "../brain/provider-dialect.js";
12
13
  /**
13
14
  * Identity fields carried by every CONTENT event of a task stream (design/99 §E2 — lightweight message
14
15
  * identity). Both are OPTIONAL and ADDITIVE; a consumer that ignores them is unchanged.
@@ -198,6 +199,25 @@ export interface BrainStatus {
198
199
  * `control_request_progress` frame, where CC itself makes the key optional.
199
200
  */
200
201
  errorStatus?: number;
202
+ /**
203
+ * What the provider said about the account or its capacity on the response being retried — one word of
204
+ * `PROVIDER_CONDITIONS`, judged off the provider dialect table keyed by where the request went
205
+ * (`brain/provider-dialect.ts`). Fills the gap {@link errorStatus} states: {@link errClass} `rate_limit`
206
+ * buckets a 429 that clears on its own with one the provider pinned on the account. Only the RETRYABLE words
207
+ * ride a retry frame (`throttled` / `overloaded` / `unknown`): a response judged `quota_exhausted` or
208
+ * `stop_other` is not retried — its word rides the terminal (`terminal.detail.providerCondition`) — unless
209
+ * the provider's own `x-should-retry: true` sent it back into the ladder, in which case the frame states the
210
+ * word the response was judged by.
211
+ *
212
+ * PRESENCE — exactly the connect-ladder backoff frames that carry {@link errorStatus}: a response named the
213
+ * failure. ABSENT on a transport failure, a stall, a mid-stream tear, the output-cap re-send (a re-shaped
214
+ * request, not a provider condition), `circuit_open`, and the terminal `recovered` / `gave_up` frames. A
215
+ * consumer reads absence as "not judged" — the same reading as `unknown`: back off, do not claim a quota.
216
+ */
217
+ providerCondition?: ProviderCondition;
218
+ /** Which evidence {@link providerCondition} rests on (`unified_header` / `provider_code` / `status`). Present
219
+ * exactly when it is. */
220
+ providerConditionSource?: ProviderConditionSource;
201
221
  }
202
222
  /**
203
223
  * design/97 CORE-8 (③): one lightweight TOOL-ACTIVITY beat surfaced from a running task, for a per-agent live
@@ -282,9 +302,19 @@ export type TaskEvent = ({
282
302
  } & TaskEventIdentity) | ({
283
303
  type: "tool_start";
284
304
  toolCallId: string;
285
- toolName: string; /** {@link ToolSpec.label} when distinct from `toolName` (design/71 liveness); omitted otherwise. */
305
+ toolName: string;
306
+ /** {@link ToolSpec.label} when distinct from `toolName`; omitted otherwise. */
286
307
  label?: string;
287
308
  args: unknown;
309
+ /**
310
+ * @contract tool_start.recovered — `true` on a start frame the engine minted at the moment it CLOSED a call
311
+ * whose start never went out on this stream: an interrupted run's reconcile closing a call the loop never
312
+ * reached, or a resumed process closing a call a dead process had announced. Every `tool_end` is preceded by
313
+ * a `tool_start` with the same id on the same stream — this frame is how that holds for those closes. `args`
314
+ * is the call's input as the transcript recorded it. It asserts NOTHING about execution (whether the call
315
+ * ran is the paired `tool_end.notExecuted`); absent on every start the loop announced itself.
316
+ */
317
+ recovered?: true;
288
318
  } & TaskEventIdentity) | ({
289
319
  /**
290
320
  * A LIVENESS TICK for a tool call that is still running. Emitted on the run's own queue between this
@@ -477,17 +507,41 @@ export type TaskEvent = ({
477
507
  * denied by which LAYER), `settlement` (how the ask this pass consumed ENDED — a person's yes or
478
508
  * no with the channel's attribution and, on a bare "no", no note; a window that elapsed and whose;
479
509
  * nobody reachable; the wait aborted…) and `origin` (who ASKED). It is the SAME object the
480
- * `permissionDenied` observer received and the engine's per-call sideband recorded — one mint, three
510
+ * `permissionDenied` observer received and the call's occurrence record holds — one mint, three
481
511
  * faces — so a renderer never branches on prose to tell "a person refused" from "the window closed".
482
512
  *
483
513
  * WHERE IT COMES FROM — never inferred here: the gate mints it at its exit, the decide lane mints it
484
514
  * from the host's decision facts joined with the row's persisted origin.
485
515
  *
486
- * ABSENT on every frame of a call the gate never saw (a tool call on an unarmed gate, a deferred
487
- * re-issue, a reconcile-recovered orphan). A gated call's frame ALWAYS carries it — an absence is
488
- * not a fact about the call, and a consumer must not read one out of it.
516
+ * THE LATEST RECORD: a station that refuses the call AFTER its gate exit (the shell read admission that
517
+ * re-establishes what the gate judged and finds it moved, a resumed leg's re-checks) adds one more
518
+ * record, minted as a veto — the refusing layer is the disposition, an approval already given stays on
519
+ * the settlement — and this member is that record. A call refused after its gate allowed it therefore
520
+ * reads `denied`, never `allowed` beside an error, and {@link isError} is `true` whatever the tool's own
521
+ * result said.
522
+ *
523
+ * ABSENT on every frame of a call no gate exit on this stream saw (a tool call on an unarmed gate, a
524
+ * deferred re-issue, a call a reconcile closed that never reached a gate here). A gated call's frame
525
+ * ALWAYS carries it — an absence is not a fact about the call, and a consumer must not read one out of it.
489
526
  */
490
527
  gate?: import("./gate-outcome.js").GateOutcome;
528
+ /**
529
+ * @contract tool_end.wireName — on a REFUSED call's frame only (`gate.disposition.kind === "denied"`): the name of
530
+ * the tool the call dispatched to on the live roster — the name the gate judged and the deny observer's `toolName`
531
+ * — where {@link toolName} is the model's spelling (an alias, a retired name the roster still answers to). A
532
+ * refusals list keyed on the tool reads this member; absent on every other frame, and on a refused frame whose
533
+ * station did not know the roster name.
534
+ */
535
+ wireName?: string;
536
+ /**
537
+ * @contract tool_end.notExecuted — `true` when the engine has POSITIVE evidence this call never executed:
538
+ * the loop closed it without running it (a sibling settled by a halted batch, a call a turn interrupt
539
+ * reached before it started) or an interrupted run's reconcile closed a call this run never started.
540
+ * ABSENT means only that no such evidence exists — never "it ran": a call closed with its outcome unknown
541
+ * (a reconcile at the next process start, a call interrupted mid-execution) carries no mark, because
542
+ * telling a consumer it did not run could invite a replay of a write that already took effect.
543
+ */
544
+ notExecuted?: true;
491
545
  /**
492
546
  * design/99 §E1 — `true` when {@link output} was SIZE-bounded by core (the full body exceeded the cap and
493
547
  * was degraded to a truncated string). Lets a consumer detect truncation programmatically instead of
@@ -11,7 +11,7 @@ import type { NestedUsage } from "./tool-spec.js";
11
11
  * `"suspended"` (design/45) is a **non-terminal** outcome: the task hit a durable tool-policy gate
12
12
  * (`ask` with a `CheckpointStore` wired) and persisted a checkpoint instead of finishing — resume it
13
13
  * with `runner.resume(checkpointToken, outcome)`. v1 contract: it is only safe at the **top-level
14
- * `runTask` boundary**; the orchestrators (cascade/teacher/verify/team) map an unexpected `suspended`
14
+ * `runTask` boundary**; the orchestrators (cascade/verify/repair-loop) map an unexpected `suspended`
15
15
  * to `failed` + `errorCode:"unexpected.suspended"` and pass the token up (§11 Q6, hard boundary).
16
16
  *
17
17
  * `"needs_review"` (design/76 §2.5, dry-run / shadow; design/80 D-B, plan-gate) is also a **non-terminal,
@@ -426,15 +426,34 @@ export interface TaskSpec {
426
426
  */
427
427
  tools?: ToolSpec[];
428
428
  /**
429
- * per-scenario tool ROSTER (true unmount, NOT a permission gate): wire names listed here
430
- * are removed from the mounted tool set AFTER assembly — their schemas never reach `tools[]`
431
- * (unlike a policy deny, which gates calls but still ships the schema bytes every request —
432
- * measured at zero token savings). Exact wire-name match (aliases resolve to their
433
- * canonical wire name at mount, so list the canonical name). Unknown names are ignored (a
434
- * center-distributed roster may cover tools this deployment doesn't mount). The injected
435
- * ToolSearch cannot be excluded (it mounts after this filter, only when deferred tools exist).
436
- * ⚠️ Excluding structural tools (submit_output / ReportBlocked) can strand the task — the caller
437
- * owns that risk, same trust level as assembling `spec.tools` itself.
429
+ * The run's tool ALLOWLIST — the availability selector (true unmount, NOT a permission gate): when set, the run may use only the
430
+ * tools it names, out of everything the engine would otherwise mount for it (the caller's tools, the hands, the engine's own
431
+ * tools, MCP and A2A tools, scenario and leg tools); the rest never reach `tools[]`. Omitted ⇒ the default set (a delegated
432
+ * child: whatever its parent could use). `[]` ⇒ no tools. An entry `"*"` ⇒ no allowlist on this layer (still bounded by the
433
+ * ceiling a spawning run handed down — a child's `"*"` never widens its parent).
434
+ *
435
+ * Entries are read like {@link excludeTools}: a wire name or an alias it declares, `mcp__srv` / `mcp__srv__*` for every tool
436
+ * of a server (including tools it lists later), a glob over the tool segment. The engine resolves the list against its OWN
437
+ * roster at prepare — a host passes the names it was given and never computes a complement. A name no tool answers to, a
438
+ * protocol-wide `mcp__*`, or an entry that is not a string is REFUSED at prepare (`config.tool_face_control`). The same
439
+ * compiled selector is applied on every later roster change (an MCP refresh or re-dial, the deferred-search lane): an exact name
440
+ * admits no new tool. Deferral is reached through ToolSearch, so a list that leaves it out defers nothing.
441
+ *
442
+ * A tool a leg relies on (a delegated child's ReportBlocked, a teammate's SendMessage, a goal iteration's DeclareDone, an
443
+ * observer's report tool — the catalog's protocol column) is not an allowlist's to name; a denylist that reaches one on such a
444
+ * leg is refused at prepare.
445
+ */
446
+ allowTools?: string[];
447
+ /**
448
+ * per-scenario tool ROSTER removal (true unmount, NOT a permission gate): the tools this list names are removed from the
449
+ * mounted tool set AFTER assembly — their schemas never reach `tools[]` (unlike a policy deny, which gates calls but still
450
+ * ships the schema bytes every request — measured at zero token savings). The DENY half of the run's tool selector, read
451
+ * exactly as a sub-agent's denied tools are: a wire name or an alias it declares; `Tool(content)` names the whole `Tool`;
452
+ * `mcp__srv` / `mcp__srv__*` / a glob over the tool segment for a server's tools, `mcp__*` for every MCP tool (including tools
453
+ * a server lists later). A name no tool answers to, `"*"`, or an entry that is not a string is REFUSED at prepare
454
+ * (`config.tool_face_control`) — a misspelt exclusion guards nothing. A deny wins over {@link allowTools}.
455
+ * ⚠️ A list that reaches a tool this kind of leg relies on (see {@link allowTools}) is refused; excluding a host-requested
456
+ * structural tool (the structured-output tool) can still strand the task — the caller owns that risk.
438
457
  */
439
458
  excludeTools?: string[];
440
459
  /**
@@ -854,6 +873,20 @@ export interface TaskSpec {
854
873
  * (#123). The write faces are untouched.
855
874
  */
856
875
  readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
876
+ /**
877
+ * TASK-layer rows ADDED to the write-protection table ({@link readDenyPatterns}' law): the run's table is the deployment's
878
+ * ({@link RunnerDeps.writeProtectedPaths}) UNION these; `[]` ≡ absent; no task-layer value removes a deployment row. A
879
+ * covered write is the judge's mandated ask (`write_protection`), as for a deployment row. Bad shapes refuse at prepare
880
+ * (`config.invalid_argument`); rides every engine-built child spec add-only.
881
+ */
882
+ writeProtectedAdditions?: readonly import("./write-protect.js").WriteProtectedEntry[];
883
+ /**
884
+ * TASK-layer denies ADDED to the compliance posture ({@link readDenyPatterns}' law): the run's deny set is the deployment's
885
+ * ({@link RunnerDeps.compliancePostureResolver}) UNION these; `[]` ≡ absent; nothing here removes a deployment deny. A hit
886
+ * is refused exactly as a deployment deny is. Members outside the closed capability set refuse at prepare
887
+ * (`config.invalid_argument`) — never a silently dropped deny; rides every engine-built child spec add-only.
888
+ */
889
+ complianceAdditionalDenies?: readonly import("./compliance.js").ComplianceCapability[];
857
890
  /**
858
891
  * design/199 件A — the TASK layer's read-face containment declaration
859
892
  * ({@link import("../tools/fs/containment-face.js").ContainmentFace}). "roots" pins this task (and, via the
@@ -9,6 +9,9 @@
9
9
  * `runner/terminal-projection.ts` — the one cause→plane derivation.
10
10
  */
11
11
  import type { AssertAllKeysHandled } from "./ask-origin.js";
12
+ import type { ProviderCondition, ProviderConditionSource } from "../brain/provider-dialect.js";
13
+ import type { RequestRoute } from "../brain/request-route.js";
14
+ import type { ModelOutputInvalidCause, OutputLimit } from "../internal/llm.js";
12
15
  /**
13
16
  * WHY a run ended — ONE tagged cause, and the ONLY terminal record a {@link TaskResult} carries. The run
14
17
  * loop's terminal arbitration used to decide a `status` word plus seven optional plane fields one by one
@@ -32,6 +35,14 @@ import type { AssertAllKeysHandled } from "./ask-origin.js";
32
35
  */
33
36
  export type TerminalCause = {
34
37
  kind: "completed";
38
+ /**
39
+ * A machine-readable account beside a completion, for the one shape that completes yet may be short:
40
+ * `finish_reason_unknown` — the final turn's provider ended it with a stream-end word the brain does not read
41
+ * (`AssistantMessage.finishReasonUnknown`); the turn was kept as the model left it, and the run announced the
42
+ * word (`route.finish_reason_unknown`). PRESENCE: only when the final assistant message carried that seat and the
43
+ * run completed on it; every other completion leaves it absent.
44
+ */
45
+ detail?: TerminalCompletedDetail;
35
46
  } | {
36
47
  kind: "failed";
37
48
  /**
@@ -69,14 +80,16 @@ export type TerminalCause = {
69
80
  /** Human-readable failure text. */
70
81
  message?: string;
71
82
  /**
72
- * A machine-readable account of the failure beside `code`, for the one shape a consumer offers its own way out
73
- * of: `provider_rejected` — the provider REFUSED the request as sent (a 4xx answer) and that refusal named this
74
- * result. `status` is the refusal's HTTP status (`TaskResult.apiFailure.status`, the same value);
75
- * `code` the provider's own word for it from its error envelope (`AssistantMessage.apiErrorCode`), when it
76
- * stated one. PRESENCE: only on that terminal — a run whose higher-ranked cause named the result, a 5xx, a
77
- * connect / stream failure, an in-band error inside a 200 and every non-provider failure leave it absent.
78
- * A 401 / 403 (credentials) and a 429 (rate limit) are refusals too: branch on `status` (or on `code` above)
79
- * to tell a request a consumer may change and resend from one it may not.
83
+ * A machine-readable account of the failure beside `code` — the fact the failure's mint point stated, projected
84
+ * once ({@link TerminalFailureDetail}; branch on `detail.kind`, never on `message`):
85
+ * - `provider_rejected` — the provider REFUSED the request as sent (a 4xx answer) and that refusal named this
86
+ * result. `status` is the refusal's HTTP status (`TaskResult.apiFailure.status`, the same value); `code` the
87
+ * provider's own word (`AssistantMessage.apiErrorCode`); `route` the request's route. A run whose
88
+ * higher-ranked cause named the result, a 5xx, a connect / stream failure and an in-band error inside a 200
89
+ * carry none. A 401 / 403 (credentials) and a 429 (rate limit) are refusals too: branch on `status`.
90
+ * - `coded` — the run threw a coded error (this `code`) whose throw site stated a structured `detail`.
91
+ * - `model_output_invalid` — the model's final turn failed because its tool calls could not be executed.
92
+ * PRESENCE: only when the arm that named this result has such a fact to project; absent otherwise.
80
93
  */
81
94
  detail?: TerminalFailureDetail;
82
95
  /**
@@ -144,14 +157,84 @@ export type TerminalCause = {
144
157
  */
145
158
  restoreMode?: "snapshot" | "park_only";
146
159
  };
147
- /** The machine-readable account a `failed` cause may carry ({@link TerminalCause}). `kind` is closed; one member. */
148
- export interface TerminalFailureDetail {
160
+ /** The machine-readable account a `completed` cause may carry ({@link TerminalCause}). `kind` is closed; one member. */
161
+ export interface TerminalCompletedDetail {
162
+ kind: "finish_reason_unknown";
163
+ /** The provider's stream-end word, in the provider echo shape (`?` when it had bytes outside it). */
164
+ finishReasonUnknown: string;
165
+ }
166
+ /**
167
+ * The machine-readable account a `failed` cause may carry ({@link TerminalCause}) — the ONE projection of a fact the
168
+ * failure's mint point already stated, carried as it was stated (a missing fact stays missing; nothing is re-derived
169
+ * from the message). `kind` is closed:
170
+ * - `provider_rejected` — the provider REFUSED the request as sent (a 4xx answer) and that refusal named the result:
171
+ * the terminal message's refusal account (`apiErrorStatus` / `apiErrorCode` / the provider-condition verdict / the
172
+ * request's `apiErrorRoute`).
173
+ * - `coded` — a failure THROWN with a machine code whose throw site stated a structured `detail` beside it (e.g. a
174
+ * `CheckpointError`'s `{ reason }` on `resume.parent_constraint_mismatch`): that detail, as a JSON-plain snapshot.
175
+ * `terminal.code` is the same error's code.
176
+ * - `model_output_invalid` — the model's turn failed because its tool calls could not be executed: the terminal
177
+ * message's `modelOutputInvalid` account.
178
+ * New members join with a minor; a consumer that branches on `kind` keeps a default arm.
179
+ */
180
+ export type TerminalFailureDetail = {
149
181
  kind: "provider_rejected";
150
182
  /** The refusal's HTTP status, 400–499. */
151
183
  status: number;
152
184
  /** The provider's own word for the refusal (its error envelope's `error.code`, else `error.type`), when stated. */
153
185
  code?: string;
154
- }
186
+ /**
187
+ * What the refusal says about the account — the engine's closed word for it (`PROVIDER_CONDITIONS`), judged off
188
+ * the provider dialect table by where the request went: `quota_exhausted` (stop; someone must act or a window
189
+ * must reset), `throttled` / `overloaded` / `unknown` (a retryable condition the retry budget ran out on, or the
190
+ * provider's own `x-should-retry: false` stopped), `stop_other` (credentials, model access, the request itself).
191
+ * The terminal's reading of the same verdict the call's retry frames carried. Present whenever the terminal
192
+ * message states one (`AssistantMessage.providerCondition`); a host-minted message that did not judge leaves it
193
+ * absent.
194
+ */
195
+ providerCondition?: ProviderCondition;
196
+ /** Which evidence {@link providerCondition} rests on. Present exactly when it is. */
197
+ providerConditionSource?: ProviderConditionSource;
198
+ /** The terminal message's `providerResetAtMs`, carried beside a `quota_exhausted` condition. */
199
+ providerResetAtMs?: number;
200
+ /**
201
+ * The route the refused request took (`AssistantMessage.apiErrorRoute`): where its credential came from, how it
202
+ * rode, which knob named its root — the knob words of `ROUTE_CREDENTIAL_PAIRING`, never a key value or a URL.
203
+ * Present whenever the terminal message states one (the first-party brains always do); a host-minted message that
204
+ * states none leaves it absent.
205
+ */
206
+ route?: RequestRoute;
207
+ } | {
208
+ kind: "coded";
209
+ /**
210
+ * The thrown error's own `detail`, as its throw site stated it — a snapshot of its JSON-plain members (strings,
211
+ * finite numbers, booleans, null, and arrays / plain objects of those, bounded; a member of any other kind is left
212
+ * out; bytes — an `ArrayBuffer` view, an index-keyed object — are never a record). A member under a credential
213
+ * name (the env-credential judge's names: `password`, `apiKey`, `token` …) stands as `"[redacted]"`, unread; every
214
+ * other string leaf passes the secret-tier redaction (credential shapes masked; a path is kept — it rides this
215
+ * structured seat, never `message`); a member whose key is not JSON-safe (visible ASCII 1–128, nothing the
216
+ * redaction would mask) is left out. Read off the same link of the error's `cause` chain whose `code` became
217
+ * `terminal.code`. PRESENCE: only when that link carried a plain-object `detail` with at least one such member — a
218
+ * code thrown without one (or a higher-ranked terminal that named the result) leaves the whole account absent.
219
+ */
220
+ detail: CodedFailureDetail;
221
+ } | {
222
+ kind: "model_output_invalid";
223
+ /** Which drop cause failed the turn (`MODEL_OUTPUT_INVALID_CAUSES`: `truncated` — arguments cut before they
224
+ * parsed; `unnamed` — a call with no tool name). The same words `task.tool_turn_incomplete` announces. */
225
+ cause: ModelOutputInvalidCause;
226
+ /** With `truncated`: the output-cap facts the stream reported (the cap stop, the counted output, the request's
227
+ * cap) — what the brain's sentence words as its cap clause. */
228
+ outputLimit?: OutputLimit;
229
+ };
230
+ /** A JSON-plain value as a `coded` account carries it. */
231
+ export type CodedFailureDetailValue = string | number | boolean | null | readonly CodedFailureDetailValue[] | {
232
+ readonly [key: string]: CodedFailureDetailValue;
233
+ };
234
+ /** The `coded` account's body: the thrown error's `detail` members ({@link TerminalFailureDetail}). */
235
+ export type CodedFailureDetail = {
236
+ readonly [key: string]: CodedFailureDetailValue;
237
+ };
155
238
  /**
156
239
  * The endings of a `userPromptSubmit` hook that keep a prompt from being submitted — the closed set of
157
240
  * {@link TerminalBlockedDetail.cause} words:
@@ -11,10 +11,11 @@ export declare class TaskSpecTightenError extends Error {
11
11
  * - **`toolPolicy`** — both set ⇒ `combinePolicies(base, override)` (deny-wins; the base's denials survive,
12
12
  * the override can only ADD denials/asks). Either alone ⇒ that one. This is the central fix: a blind spread
13
13
  * would drop the base policy entirely.
14
- * - **`handsReadOnly`** — `true` is stricter. `base:true` + `override:false` ⇒ **throw** (loosening). Otherwise
15
- * the stricter (`true` if either is true) wins.
16
- * - **`approvalReach`** — `"none"` (nobody can be asked) is the only word; a base declaration survives an override that
17
- * omits it or writes `undefined`.
14
+ * - **every row of the clamp table** (`CLAMP_SET`: `handsReadOnly`, `interactiveTools`, `approvalReach`, the two
15
+ * containment faces, `readDenyPatterns`, `oneShot`, `memoryPersistenceCapable`, `fileHistory`) — a base carrying the
16
+ * row's restricting value meeting an override that states a different one ⇒ **throw** (loosening); otherwise the
17
+ * restricting value wins if either side carries it, an add-only list unions, and an override writing `undefined`
18
+ * erases nothing.
18
19
  * - **`shellGate`** — one value (`"always"`, the operator floor). Either side set ⇒ the floor stands; it cannot
19
20
  * be loosened because absence is the only other state and an override cannot unset a base value.
20
21
  * - **`onAsk`** — no safe automatic merge (the approver and the policy must be coordinated). Both set to