@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
@@ -25,3 +25,15 @@ export interface SessionStoreIncarnationContractHooks {
25
25
  runAssertion?: ContractAssertionRunner;
26
26
  }
27
27
  export declare function sessionStoreIncarnationContract(make: () => SessionStore, hooks: SessionStoreIncarnationContractHooks): Promise<void>;
28
+ /**
29
+ * The incarnation cases themselves, registered through the caller's `run` (which builds and disposes the
30
+ * store each case gets). One text for both kits that carry them: this one, and `sessionStoreContract`'s
31
+ * incarnation group — a case changed here changes in both, so the two can never pin different laws.
32
+ * Module-internal (not a package export).
33
+ */
34
+ export declare function registerSessionIncarnationCases(run: (name: string, fn: (store: SessionStore) => Promise<void>) => void, hooks: {
35
+ deleteSession: SessionStoreIncarnationContractHooks["deleteSession"];
36
+ /** This kit's own hook is synchronous; `sessionStoreContract`'s may also resolve later (a backend whose
37
+ * restart must first release an exclusive lock). Awaiting a plain value changes nothing for this kit. */
38
+ reopen: (store: SessionStore) => SessionStore | Promise<SessionStore>;
39
+ }): void;
@@ -23,6 +23,10 @@ export async function sessionStoreIncarnationContract(make, hooks) {
23
23
  await store.dispose();
24
24
  }
25
25
  });
26
+ registerSessionIncarnationCases(run, hooks);
27
+ await settle();
28
+ }
29
+ export function registerSessionIncarnationCases(run, hooks) {
26
30
  run("a session created through acquire carries a token, and every later read of the row answers the same one", async (store) => {
27
31
  await store.acquire(ID_A);
28
32
  const first = await tokenOf(store, ID_A);
@@ -44,7 +48,7 @@ export async function sessionStoreIncarnationContract(make, hooks) {
44
48
  await store.acquire(ID_A);
45
49
  const first = await tokenOf(store, ID_A);
46
50
  assertToken(first, "first instance");
47
- const second = hooks.reopen(store);
51
+ const second = await hooks.reopen(store);
48
52
  try {
49
53
  assert.equal(await tokenOf(second, ID_A), first, "an instance-local token would name a different occupant after every restart");
50
54
  await second.acquire(ID_A, { requireExisting: true });
@@ -88,5 +92,4 @@ export async function sessionStoreIncarnationContract(make, hooks) {
88
92
  assert.notEqual(forked, source, "a fork copies the history, never the occupant");
89
93
  assert.equal(await tokenOf(store, ID_B), source, "forking does not touch the source's occupant");
90
94
  });
91
- await settle();
92
95
  }
@@ -677,6 +677,10 @@ export type TaskEvent = ({
677
677
  * pre-compaction context size. CC parity = `SDKCompactBoundary{trigger, pre_tokens, preserved_segment}`. */
678
678
  type: "compacted";
679
679
  trigger: "auto" | "manual" | "forced";
680
+ /** The pass this frame CONCLUDES — the value the pass's `compaction_started` frame carried. A landing is
681
+ * always preceded by its start, so the engine sets it on every `compacted` frame; it is optional in the
682
+ * type only so a literal a host builds by hand stays valid. */
683
+ pass?: string;
680
684
  tokensBefore: number;
681
685
  /** How much context this compaction FREED, as the engine measured it: one structural sum over
682
686
  * the pre-messages minus the same sum over the post-messages, inside the pass. **The only
@@ -712,10 +716,12 @@ export type TaskEvent = ({
712
716
  * handed over again by the engine (once; a second loss is disclosed `resupply_exhausted`), so
713
717
  * this counts what the summary swallowed, not what is gone. Present iff at least one. */
714
718
  summarizedCrossSessionMessages?: number;
715
- /** CC post-compact file-restore parity (2026-07-03): the working files the engine
716
- * re-read into the summary (attachWorkingFiles), attachment order. A shell renders CC's
717
- * `Read {path} (…)` cards under the compaction divider from this. Absent when the feature is
718
- * off or nothing was attached. `truncated` = the content was clipped at the per-file cap. */
719
+ /** CC post-compact file-restore parity: the files this compaction's restore delivered (attachWorkingFiles) —
720
+ * projected from the restore frames' delivery records, frame order. A shell renders CC's `Read {path} (…)`
721
+ * cards under the compaction divider from this. `chars` = the characters of the file's data the frame
722
+ * delivered (the line-numbered contents — the share itself counts the file's own text); `truncated` = the frame
723
+ * holds a partial view. A file the restore named but did not send (too large, refused, gone, a PDF reference)
724
+ * is not listed. Absent when the feature is off or nothing was delivered. */
719
725
  attachedFiles?: Array<{
720
726
  path: string;
721
727
  chars: number;
@@ -763,8 +769,9 @@ export type TaskEvent = ({
763
769
  * - `"mooted"` — the pass did not run to its end: a manual request no boundary could serve
764
770
  * (`trigger:"manual"`, `reason:"task_ending"`), or a request withdrawn / a pass cut in flight by its
765
771
  * caller — a `compact({ signal })` withdrawal, `halt()`, `interrupt()`, `destroy()` or the task's own
766
- * `signal` (`reason:"cancelled"`), on any trigger: `"auto"`, `"manual"` or `"forced"`. Read
767
- * `"mooted"` with its `trigger` — it is not a manual-only outcome.
772
+ * `signal` (`reason:"cancelled"`), on any trigger: `"auto"`, `"manual"` or `"forced"`, or a turn-boundary
773
+ * pass in flight that the task's own end cut — a limit, a timeout (`reason:"task_ending"`, with the
774
+ * `pass` the start announced). Read `"mooted"` with its `trigger` — it is not a manual-only outcome.
768
775
  */
769
776
  type: "compaction_outcome";
770
777
  outcome: Exclude<CompactOutcome, "compacted"> | "suppressed";
@@ -772,6 +779,37 @@ export type TaskEvent = ({
772
779
  trigger: "auto" | "manual" | "forced";
773
780
  /** Optional human-readable detail (mirrors the trace frame's reason where one exists). */
774
781
  reason?: string;
782
+ /** The pass this frame CONCLUDES — the value its `compaction_started` frame carried. Present exactly when a
783
+ * start was announced for the pass this frame ends, which makes it `failed` or `mooted` (a pass that ran its
784
+ * summary call and did not land, or was cut while it ran). Absent on every outcome of a pass that never
785
+ * started — `blocked`, `suppressed`, `disabled`, `noop`, a `mooted` request no boundary served — and on the
786
+ * rapid-refill `disabled` frame, which reports a state the landing before it caused, not that pass's end. */
787
+ pass?: string;
788
+ } & TaskEventIdentity) | ({
789
+ /**
790
+ * A compaction pass is under way: the engine has committed to it and is about to produce its summary.
791
+ * Until now compaction was announced only when it ended (`compacted`, or `compaction_outcome` for a pass
792
+ * that did not land), so a consumer watching a summary call wait out a provider's retries had nothing to
793
+ * show for the minutes in between.
794
+ *
795
+ * Minted at the one place a pass commits — after the trigger gate, a valid cut point and the
796
+ * `preCompact` hook (a blocking hook ends the pass before it starts), before the summary provider is
797
+ * consulted or the model is called — by every lane that compacts: the turn boundary (`"auto"`, `"manual"`,
798
+ * and `"forced"` under trim pressure), the prompt-too-long recovery and guard-chain arm B (`"forced"`), and
799
+ * the end-of-task summary (`"auto"`). Retries inside the pass's model call never mint a second one.
800
+ *
801
+ * Pairing: exactly one frame concludes each started pass, carrying the same `pass` — `compacted` when it
802
+ * lands, `compaction_outcome{failed | mooted}` when it does not. A pass that never reaches this point
803
+ * (`blocked`, `suppressed`, `disabled`, `noop`) announces nothing here and its outcome carries no `pass`.
804
+ * `pass` is unique across runs, so a consumer multiplexing several tasks on one stream pairs on `pass` alone.
805
+ */
806
+ type: "compaction_started";
807
+ /** Same three-state vocabulary (and the same value) the pass hands the `preCompact` hook and its concluding frame. */
808
+ trigger: "auto" | "manual" | "forced";
809
+ /** The pass's identity: an opaque engine-minted id (uuidv7), carried by the frame that concludes the pass. */
810
+ pass: string;
811
+ /** The engine clock (epoch ms) at the moment the pass committed. */
812
+ startedAt: number;
775
813
  } & TaskEventIdentity) | ({
776
814
  /**
777
815
  * TB telemetry B3: the engine injected a SYSTEM steering message (a deadline nudge
@@ -2,7 +2,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { escapeEnvelopeTag, inlineUntrusted, sanitizeUntrustedText } from "./untrusted-text.js";
3
3
  import { egressText } from "./untrusted-egress.js";
4
4
  import { PEER_MESSAGE_NOTICE } from "../agents/peer-admission.js";
5
- import { buildCrossSessionEnvelope, CROSS_SESSION_MESSAGE_NOTICE, neutralizePeerBody } from "../agents/cross-session-envelope.js";
5
+ import { buildCrossSessionEnvelope, CROSS_PRINCIPAL_MESSAGE_NOTICE, CROSS_SESSION_MESSAGE_NOTICE, neutralizePeerBody } from "../agents/cross-session-envelope.js";
6
6
  export const TASK_NOTIFICATION_TERMINAL_STATUSES = Object.freeze(["completed", "failed", "killed", "cancelled"]);
7
7
  export const NOTIFICATION_INJECTION_OUTCOMES = Object.freeze(["queued", "parked", "parked_durably", "dropped_duplicate"]);
8
8
  export function isNotificationInjectionOutcome(value) {
@@ -88,7 +88,7 @@ export function renderAgentMessageFrame(frame) {
88
88
  }
89
89
  export function renderCrossSessionMessageFrame(frame) {
90
90
  const { body, ...fields } = frame;
91
- return `${buildCrossSessionEnvelope(fields, body)}\n\n${CROSS_SESSION_MESSAGE_NOTICE}`;
91
+ return `${buildCrossSessionEnvelope(fields, body)}\n\n${fields.fromScope !== undefined ? CROSS_PRINCIPAL_MESSAGE_NOTICE : CROSS_SESSION_MESSAGE_NOTICE}`;
92
92
  }
93
93
  export function renderCrossSessionNoticeLine(notice) {
94
94
  return sanitizeUntrustedText(notice.text);
@@ -380,6 +380,7 @@ export function registerBackgroundAgentLane(core, input) {
380
380
  ...(input.parentTaskId !== undefined ? { parentTaskId: input.parentTaskId } : {}),
381
381
  ...(input.parentSessionId !== undefined ? { parentSessionId: input.parentSessionId } : {}),
382
382
  ...(input.rootSessionId !== undefined ? { rootSessionId: input.rootSessionId } : {}),
383
+ ...(input.crossBoundaryTree !== undefined ? { crossBoundaryTree: input.crossBoundaryTree } : {}),
383
384
  ...(input.deliveryChannel !== undefined ? { channelState: input.deliveryChannel } : {}),
384
385
  ...(input.retainedContinuation !== undefined ? { retainedContinuation: input.retainedContinuation } : {}),
385
386
  ...(input.owner !== undefined ? { owner: input.owner } : {}),
@@ -248,6 +248,8 @@ export interface DurableAgentLane {
248
248
  }
249
249
  export interface BackgroundAgentTaskHandle extends SemaTaskHandle {
250
250
  type: "background_agent";
251
+ /** The spawning tree's cross-boundary token ({@link RegisterBackgroundAgentInput.crossBoundaryTree}), compared by identity. */
252
+ crossBoundaryTree?: object;
251
253
  /** design/115 P3: aborting this cancels the child run (TaskStop / parent teardown). */
252
254
  abort: AbortController;
253
255
  /** Lineage axes, declared (registration always spread them into the handle; readers used
@@ -908,6 +910,9 @@ export declare function commitCompletionIdIfEmpty(target: {
908
910
  completionId?: string;
909
911
  }, value: string): string;
910
912
  export interface RegisterBackgroundAgentInput extends TaskAccess {
913
+ /** The spawning tree's cross-boundary token (`RunInternals.crossBoundaryTree`, the tree the child joins): a poll of this handle
914
+ * from a leg of the SAME tree moves nothing across the device boundary — the child's bits are already in that tree. */
915
+ crossBoundaryTree?: object;
911
916
  /** design/153 §7.2d (件3c) — parked-resume registration shape: the handle is born PARKED (the
912
917
  * pre-consume window keeps the live face's checkpoint arbitration; the consume flip
913
918
  * {@link TaskRegistry.adoptParkedResume} is the only path to running). Pass the ticket's token
@@ -1,6 +1,7 @@
1
1
  import type { AgentTool, ExecutionEnv } from "../internal/harness.js";
2
2
  import type { ToolEffect } from "./types.js";
3
3
  import { type BackgroundHardKillOutcome, type BackgroundShellCapability, type BackgroundShellId } from "./background-shell.js";
4
+ import type { BoundaryLegsGiven } from "./cross-boundary-taint.js";
4
5
  import type { WorkflowRunStore } from "./workflow-run-store.js";
5
6
  import { type BackgroundAgentStore } from "./background-agent-store.js";
6
7
  import type { TaskNotificationPayload } from "./task-notification.js";
@@ -638,6 +639,12 @@ export declare class TaskRegistry {
638
639
  * one terminal notification (exit code / timeout / auto-stop / env-side kill). */
639
640
  registerMonitor(input: RegisterMonitorInput): string;
640
641
  registerWorkflow(input: RegisterWorkflowInput): string;
642
+ /** The legs one TaskOutput (`"output"`) or TaskStop (`"stop"`) call travels, read off the handle the call dispatches to by the same
643
+ * resolution the poll and the stop take: a background shell's or a monitor's process lives in its own environment (the `env` leg
644
+ * reaches that one); an agent handle that recorded the calling tree's own token moves nothing across the boundary; any other handle — an
645
+ * agent of another tree, a workflow run, an id no live handle answers (a durable row read back) — is content from beyond the
646
+ * boundary on a poll and nothing on a stop. */
647
+ boundaryLegsOf(id: string, access: TaskAccess, verb: "output" | "stop", tree: object): BoundaryLegsGiven;
641
648
  pollTask(id: string, access: TaskAccess, opts?: TaskPollOptions): Promise<UnifiedTaskResult>;
642
649
  stopTask(id: string, access: TaskAccess, opts?: TaskStopOptions): Promise<UnifiedTaskResult>;
643
650
  /** S2b — bounded delivery-confirmation wait (see deliverToRunningAgent). Test-tunable. */
@@ -863,6 +863,14 @@ export class TaskRegistry {
863
863
  registerWorkflow(input) {
864
864
  return registerWorkflowLane(this.core, input);
865
865
  }
866
+ boundaryLegsOf(id, access, verb, tree) {
867
+ const handle = this.resolveInternal(id.trim(), access);
868
+ if (handle?.type === "background_bash" || handle?.type === "monitor")
869
+ return { legs: ["env"], env: handle.env };
870
+ if (handle?.type === "background_agent" && handle.crossBoundaryTree === tree)
871
+ return { legs: [] };
872
+ return { legs: verb === "output" ? ["outside_read"] : [] };
873
+ }
866
874
  async pollTask(id, access, opts = {}) {
867
875
  const taskId = id.trim();
868
876
  const deadline = opts.block ? blockDeadline(opts.timeoutMs, Date.now()) : undefined;
@@ -1301,6 +1309,10 @@ export function createTaskOutputTool(opts) {
1301
1309
  ...toolFace("task-output"),
1302
1310
  description: composeTaskOutputDescription(caps),
1303
1311
  parameters: composeTaskOutputParams(caps),
1312
+ boundaryLegsFor: (rawArgs, at) => {
1313
+ const id = resolveTaskIdArg(rawArgs?.task_id);
1314
+ return id ? opts.registry.boundaryLegsOf(id, { owner: opts.owner, scope: opts.scope, ...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}) }, "output", at.tree) : { legs: [] };
1315
+ },
1304
1316
  execute: async (rawArgs, ctx) => {
1305
1317
  const args = rawArgs;
1306
1318
  const id = resolveTaskIdArg(args.task_id);
@@ -1350,6 +1362,11 @@ export function createTaskStopTool(opts) {
1350
1362
  ...toolFace("task-stop"),
1351
1363
  description: composeTaskStopDescription(REGISTRY_TASK_TOOL_CAPS),
1352
1364
  parameters: TASK_STOP_PARAMS,
1365
+ boundaryLegsFor: (rawArgs, at) => {
1366
+ const a = rawArgs;
1367
+ const id = resolveTaskIdArg(a?.task_id, a?.shell_id);
1368
+ return id ? opts.registry.boundaryLegsOf(id, { owner: opts.owner, scope: opts.scope, ...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}) }, "stop", at.tree) : { legs: [] };
1369
+ },
1353
1370
  execute: async (rawArgs, ctx) => {
1354
1371
  const args = rawArgs;
1355
1372
  const id = resolveTaskIdArg(args.task_id, args.shell_id);
@@ -762,7 +762,7 @@ export interface TaskResult {
762
762
  finalVerifyInjected?: true;
763
763
  /** oracle-grounding gate (opus LOW 复审, additive): HOW MANY final-verification injections
764
764
  * fired this run — 1 = the R9 one-shot only, 2 = R9 + the grounding re-entry (hard cap).
765
- * Present iff `finalVerifyInjected` is (the boolean stays for compatibility). */
765
+ * Present iff `finalVerifyInjected` is (the boolean stays for compatibility, RL-1336-finalverify-bool). */
766
766
  finalVerifyInjections?: number;
767
767
  /** design/133: number of attachment blocks injected this run — turn-boundary bundles
768
768
  * (todo/task reminders, changed-files notes, plan-mode re-reminders, listing drift frames;
@@ -301,13 +301,13 @@ export interface TaskSpec {
301
301
  systemPrompt?: string;
302
302
  /** The role base `core/role.base` selects: `"general"` (absent), `"code"`, `"coordinator"`, `"cowork"` (rendered from the
303
303
  * mounted tools and the working directory); `systemPrompt` replaces whichever is selected. Not inherited (a child gets
304
- * the sub-agent persona). A value outside the closed set is refused at prepare. */
305
- promptRole?: "general" | "code" | "coordinator" | "cowork";
304
+ * the sub-agent persona). A value outside the closed set (`HOST_ROLES`, tested by `isHostRole`) is refused at prepare. */
305
+ promptRole?: import("../prompt-assembly/catalog/schema.js").HostRole;
306
306
  /** `"minimal"` leaves the core-behavior band out; the locked sections (mode sections in play among them) and
307
307
  * `appendSystemPrompt` stay. Absent ⇒ standard. Rides the parent spec seat to every engine-built child (a clamp-table
308
308
  * row: no child authors the standard form back). A value outside the closed set is refused at prepare. */
309
309
  promptForm?: "minimal";
310
- /** The deployment's center binding (a scenario name): selects the adopted center artifact's `scope.binding` overlays; rides the parent spec seat; out of grammar (non-blank, ≤ 128) ⇒ `config.prompt_selector_invalid`. */
310
+ /** The deployment's binding (a scenario name): a prompt selector value, read by every overlay's `when.binding` (center, deploy and task alike); rides the parent spec seat; out of grammar (non-blank, ≤ 128 Unicode code points — `PROMPT_BINDING_MAX_CHARS`, counted `[...s].length`, not UTF-16 units) ⇒ `config.prompt_selector_invalid`. */
311
311
  promptBinding?: string;
312
312
  /**
313
313
  * APPEND a caller block to the assembled system prompt, keeping the engine's base + harness/safety blocks
@@ -1312,10 +1312,10 @@ export interface TaskSpec {
1312
1312
  * `attachWorkingFiles` (LONGRUN-2; **default ON since 2026-07-03** — CC 198 hard-codes its
1313
1313
  * post-compact file restore, and LONGRUN-2 measured ≈2.3 extra read round-trips per compaction
1314
1314
  * without it): after each compaction, re-read the task's most recently READ files (CC
1315
- * readFileState semantics; falls back to modified-by-recency when read tracking is absent) and
1316
- * append their current contents to the summary (bounded: `maxFiles` default 3,
1317
- * `maxCharsPerFile` default 16k chars ≈ 4k tokens, plus a total cap that self-scales to ~15% of
1318
- * the model window). The `compacted` wire event carries the attached list as `attachedFiles`
1315
+ * readFileState semantics; else modified-by-recency) the summary leaves out of view, with the Read tool's
1316
+ * read, into attachment frames after the summary (`maxFiles` default 3, `maxCharsPerFile` default 16k chars
1317
+ * of the file's own text, a total cap ~15% of the window in the file contents the frames send, line numbers
1318
+ * included; a file over either is named, never sent in part). The `compacted` wire event lists the files whose contents were delivered as `attachedFiles`
1319
1319
  * (shell renders CC's post-compact Read cards). `false` opts out; an object customizes caps;
1320
1320
  * no-op when the task has no execution env (no hands, nothing to re-read).
1321
1321
  */
@@ -12,10 +12,12 @@ export declare class TaskSpecTightenError extends Error {
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
14
  * - **every row of the clamp table** (`CLAMP_SET`: `handsReadOnly`, `interactiveTools`, `approvalReach`, the two
15
- * containment faces, `readDenyPatterns`, `oneShot`, `memoryPersistenceCapable`, `fileHistory`, `promptForm`) — 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.
15
+ * containment faces, `readDenyPatterns`, `oneShot`, `memoryPersistenceCapable`, `fileHistory`, `promptForm`, and
16
+ * `memory.enabled`) — a base carrying the row's restricting value meeting an override that states a different one ⇒
17
+ * **throw** (loosening); otherwise the restricting value wins if either side carries it, an add-only list unions, and an
18
+ * override writing `undefined` erases nothing. `memory` is an object seat with ONE clamped member (`enabled: false`):
19
+ * the later declaration stands whole (its own scopes, write scope and knobs — never mixed with the base's) with
20
+ * `enabled: false` stated on it when either side carries that.
19
21
  * - **`shellGate`** — one value (`"always"`, the operator floor). Either side set ⇒ the floor stands; it cannot
20
22
  * be loosened because absence is the only other state and an override cannot unset a base value.
21
23
  * - **`onAsk`** — no safe automatic merge (the approver and the policy must be coordinated). Both set to
@@ -1,6 +1,6 @@
1
1
  import { combinePolicies } from "./tool-policy.js";
2
2
  import { clampPermissionMode } from "./ask-origin.js";
3
- import { CLAMP_SEATS, composeClampSeat, loosensClampSeat } from "./child-clamp-seats.js";
3
+ import { CLAMP_SEATS, clampCellOf, clampCellPath, composeClampSeat, loosensClampSeat } from "./child-clamp-seats.js";
4
4
  export class TaskSpecTightenError extends Error {
5
5
  constructor(message) {
6
6
  super(message);
@@ -40,7 +40,7 @@ export function tightenTaskSpec(base, overrides) {
40
40
  const b = base[seat];
41
41
  const o = overrides[seat];
42
42
  if (loosensClampSeat(seat, b, o)) {
43
- throw new TaskSpecTightenError(`tightenTaskSpec: cannot loosen ${seat} from ${JSON.stringify(b)} to ${JSON.stringify(o)} (override may only tighten)`);
43
+ throw new TaskSpecTightenError(`tightenTaskSpec: cannot loosen ${clampCellPath(seat)} from ${JSON.stringify(clampCellOf(seat, b))} to ${JSON.stringify(clampCellOf(seat, o))} (override may only tighten)`);
44
44
  }
45
45
  if (b !== undefined || o !== undefined)
46
46
  Object.assign(merged, { [seat]: composeClampSeat(seat, b, o) });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The STATIC TOOL CATALOG (design/388 §2.1 "静态目录"): one row per built-in / synthetic tool DEFINITION
3
3
  * core owns — its wire name, the conditions core (or a host scenario factory) mounts it under, the
4
- * `details.type` cards it mints, and its declared FACE (family / axes / contract / path target / rule
4
+ * `details.type` cards it mints, and its declared FACE (family / boundary legs / axes / contract / path target / rule
5
5
  * vocabulary / render hints / policy bits).
6
6
  *
7
7
  * SINGLE SOURCE, TWO READERS. Each definition module spreads its row's `face` into the `ToolSpec` it
@@ -16,7 +16,7 @@ export const TOOL_CATALOG_ENTRIES = [
16
16
  id: "read", name: "Read", source: "builtin", definedIn: "src/tools/fs/fs-read.ts", mountedBy: ["hands"],
17
17
  cards: ["text", "notebook", "file_unchanged", "image", "document"],
18
18
  face: {
19
- family: "file-read", effect: "read", contract: c("core.read@1"),
19
+ family: "file-read", boundaryLegs: ["env"], effect: "read", contract: c("core.read@1"),
20
20
  pathTarget: { param: "file_path", aliases: ["path"], access: "read", base: "cwd", absent: "none" },
21
21
  ruleFace: { params: ["file_path", "path", "offset", "limit", "pages"] },
22
22
  renderHints: { summaryParams: ["file_path"] },
@@ -27,7 +27,7 @@ export const TOOL_CATALOG_ENTRIES = [
27
27
  id: "edit", name: "Edit", source: "builtin", definedIn: "src/tools/fs/fs-write.ts", mountedBy: ["hands"],
28
28
  cards: ["edit"],
29
29
  face: {
30
- family: "file-write", effect: "write", contract: c("core.edit@1"),
30
+ family: "file-write", boundaryLegs: ["env"], effect: "write", contract: c("core.edit@1"),
31
31
  pathTarget: { param: "file_path", aliases: ["path"], access: "edit", skillScopeEligible: true, base: "cwd", absent: "none" },
32
32
  ruleFace: { params: ["file_path", "path", "old_string", "new_string", "replace_all"] },
33
33
  renderHints: { summaryParams: ["file_path"], approvalCard: "file-edit" },
@@ -38,7 +38,7 @@ export const TOOL_CATALOG_ENTRIES = [
38
38
  id: "write", name: "Write", source: "builtin", definedIn: "src/tools/fs/fs-write.ts", mountedBy: ["hands"],
39
39
  cards: ["create", "update"],
40
40
  face: {
41
- family: "file-write", effect: "write", contract: c("core.write@1"),
41
+ family: "file-write", boundaryLegs: ["env"], effect: "write", contract: c("core.write@1"),
42
42
  pathTarget: { param: "file_path", aliases: ["path"], access: "create", skillScopeEligible: true, base: "cwd", absent: "none" },
43
43
  ruleFace: { params: ["file_path", "path", "content"] },
44
44
  renderHints: { summaryParams: ["file_path"], approvalCard: "file-write" },
@@ -49,7 +49,7 @@ export const TOOL_CATALOG_ENTRIES = [
49
49
  id: "notebook-edit", name: "NotebookEdit", source: "builtin", definedIn: "src/tools/fs/fs-write.ts", mountedBy: ["hands"],
50
50
  cards: ["notebook-edit"],
51
51
  face: {
52
- family: "file-write", effect: "write", contract: c("core.notebook_edit@1"),
52
+ family: "file-write", boundaryLegs: ["env"], effect: "write", contract: c("core.notebook_edit@1"),
53
53
  pathTarget: { param: "notebook_path", aliases: ["path"], access: "edit", base: "cwd", absent: "none" },
54
54
  ruleFace: { params: ["notebook_path", "path", "cell_id", "cell_type", "edit_mode", "new_source"] },
55
55
  renderHints: { userFacingName: "Edit Notebook", summaryParams: ["notebook_path"], approvalCard: "notebook-edit" },
@@ -59,7 +59,7 @@ export const TOOL_CATALOG_ENTRIES = [
59
59
  id: "bash", name: "Bash", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"],
60
60
  cards: ["bash", "readonly_out_of_root", "read_unestablished", "bash_invalid_timeout"],
61
61
  face: {
62
- family: "shell", effect: "write", contentOrigin: "execution", contract: c(FULL_SHELL_CONTRACT_ID),
62
+ family: "shell", boundaryLegs: ["env"], effect: "write", contentOrigin: "execution", contract: c(FULL_SHELL_CONTRACT_ID),
63
63
  ruleFace: SHELL_RULE_FACE,
64
64
  renderHints: { summaryParams: ["command"], approvalCard: "shell" },
65
65
  offloadThresholdChars: 30_000, compactable: true,
@@ -69,7 +69,7 @@ export const TOOL_CATALOG_ENTRIES = [
69
69
  id: "bash-readonly", name: "Bash", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"],
70
70
  cards: ["bash", "readonly_out_of_root", "read_unestablished", "bash_invalid_timeout"],
71
71
  face: {
72
- family: "shell", effect: "read", contract: c(READONLY_SHELL_CONTRACT_ID),
72
+ family: "shell", boundaryLegs: ["env"], effect: "read", contract: c(READONLY_SHELL_CONTRACT_ID),
73
73
  ruleFace: SHELL_RULE_FACE,
74
74
  renderHints: { summaryParams: ["command"], approvalCard: "shell" },
75
75
  offloadThresholdChars: 30_000, compactable: true,
@@ -79,7 +79,7 @@ export const TOOL_CATALOG_ENTRIES = [
79
79
  id: "grep", name: "Grep", source: "builtin", definedIn: "src/tools/fs/fs-search-tools.ts", mountedBy: ["hands"],
80
80
  cards: ["grep"],
81
81
  face: {
82
- family: "search", effect: "read", contract: c("core.grep@1"),
82
+ family: "search", boundaryLegs: ["env"], effect: "read", contract: c("core.grep@1"),
83
83
  pathTarget: { param: "path", access: "read", base: "root", absent: "base" },
84
84
  ruleFace: { params: ["pattern", "path", "glob", "type", "output_mode", "head_limit", "offset", "multiline", "context", "ignore_case", "-i", "-n", "-o", "-A", "-B", "-C"] },
85
85
  renderHints: { userFacingName: "Search", summaryParams: ["pattern", "path"] },
@@ -90,7 +90,7 @@ export const TOOL_CATALOG_ENTRIES = [
90
90
  id: "glob", name: "Glob", source: "builtin", definedIn: "src/tools/fs/fs-search-tools.ts", mountedBy: ["hands"],
91
91
  cards: ["glob"],
92
92
  face: {
93
- family: "search", effect: "read", contract: c("core.glob@1"),
93
+ family: "search", boundaryLegs: ["env"], effect: "read", contract: c("core.glob@1"),
94
94
  pathTarget: { param: "path", access: "read", base: "root", absent: "base", patternParam: "pattern" },
95
95
  ruleFace: { params: ["pattern", "path", "max_results"] },
96
96
  renderHints: { userFacingName: "Search", summaryParams: ["pattern", "path"] },
@@ -100,7 +100,7 @@ export const TOOL_CATALOG_ENTRIES = [
100
100
  {
101
101
  id: "repo-map", name: "RepoMap", source: "builtin", definedIn: "src/tools/fs/repo-map.ts", mountedBy: ["hands"], cards: ["repo-map"],
102
102
  face: {
103
- family: "search", effect: "read", contract: c("core.repo_map@1"),
103
+ family: "search", boundaryLegs: ["env"], effect: "read", contract: c("core.repo_map@1"),
104
104
  pathTarget: { param: "path", access: "read", base: "root", absent: "base" },
105
105
  ruleFace: { params: ["path", "max_chars", "max_files"] },
106
106
  },
@@ -108,62 +108,62 @@ export const TOOL_CATALOG_ENTRIES = [
108
108
  {
109
109
  id: "lsp", name: "LSP", source: "builtin", definedIn: "src/core/lsp.ts", mountedBy: ["hands"], cards: [],
110
110
  face: {
111
- family: "search", effect: "read", contract: c("core.lsp@1"),
111
+ family: "search", boundaryLegs: ["env"], effect: "read", contract: c("core.lsp@1"),
112
112
  pathTarget: { param: "filePath", access: "read", base: "cwd", absent: "none" },
113
113
  ruleFace: { params: ["operation", "filePath", "line", "character", "query"] },
114
114
  },
115
115
  },
116
116
  {
117
117
  id: "artifact", name: ARTIFACT_TOOL_NAME, source: "builtin", definedIn: "src/tools/artifact/artifact-tool.ts", mountedBy: ["optional"], cards: ["artifact"],
118
- face: { family: "other", effect: "write", contract: c("core.artifact@1"), ruleFace: { primaryParams: ["action"] }, renderHints: { summaryParams: ["action", "file_path", "url"] } },
118
+ face: { family: "other", boundaryLegs: "per_call", effect: "write", contract: c("core.artifact@1"), ruleFace: { primaryParams: ["action"] }, renderHints: { summaryParams: ["action", "file_path", "url"] } },
119
119
  },
120
- { id: "enter-worktree", name: "EnterWorktree", source: "builtin", definedIn: "src/tools/worktree.ts", mountedBy: ["hands"], cards: ["worktree"], face: { family: "other", effect: "write", contract: c("core.enter_worktree@1") } },
121
- { id: "exit-worktree", name: "ExitWorktree", source: "builtin", definedIn: "src/tools/worktree.ts", mountedBy: ["hands"], cards: ["worktree"], face: { family: "other", effect: "write", contract: c("core.exit_worktree@1") } },
122
- { id: "task-output-hands", name: "TaskOutput", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
123
- { id: "task-stop-hands", name: "TaskStop", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
124
- { id: "task-output", name: "TaskOutput", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-output"], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
125
- { id: "task-stop", name: "TaskStop", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-stop"], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
126
- { id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root", "read_unestablished"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), ruleFace: shellRuleFace(["command", "description", "timeout_ms", "persistent"]), offloadThresholdChars: 10_000 } },
127
- { id: "send-message", name: "SendMessage", protocol: ["teammate"], source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", effect: "write", contract: c("core.send_message@1") } },
128
- { id: "agent-transcript", name: "AgentTranscript", source: "builtin", definedIn: "src/agents/agent-transcript-tool.ts", mountedBy: ["delegation"], cards: ["agent-transcript"], face: { family: "delegate", effect: "read", contract: c("core.agent_transcript@1") } },
129
- { id: "list-agents", name: "ListAgents", source: "builtin", definedIn: "src/agents/list-agents-tool.ts", mountedBy: ["delegation", "peerLane"], cards: ["list-agents"], face: { family: "protocol", effect: "read", aliases: ["ListPeers"], contract: c("core.list_agents@1") } },
130
- { id: "read-inbox", name: "ReadInbox", source: "builtin", definedIn: "src/agents/read-inbox-tool.ts", mountedBy: ["peerLane"], cards: ["read-inbox"], face: { family: "protocol", effect: "read", contract: c("core.read_inbox@1"), offload: false, budgetExempt: true } },
131
- { id: "workflow", name: "Workflow", source: "builtin", definedIn: "src/orchestration/run-workflow-tool.ts", mountedBy: ["workflow"], cards: ["workflow-run"], face: { family: "delegate", effect: "write", contract: c("core.workflow@1") } },
132
- { id: "report-blocked", name: "ReportBlocked", protocol: ["delegated"], source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.report_blocked@1") } },
133
- { id: "report-findings", name: "ReportFindings", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: ["report-findings"], face: { family: "scaffold", effect: "read", contract: c("core.report_findings@1") } },
134
- { id: "structured-output", name: "StructuredOutput", protocol: ["structured"], source: "synthetic", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.structured_output@1") } },
135
- { id: "skill", name: "Skill", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.skill@1") } },
136
- { id: "read-tool-result", name: "ReadToolResult", source: "builtin", definedIn: "src/core/tool-result-store.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.read_tool_result@1"), budgetExempt: true } },
137
- { id: "tool-search", name: "ToolSearch", source: "builtin", definedIn: "src/core/runner/tool-disclosure.ts", mountedBy: ["optional"], cards: ["tool-search"], face: { family: "scaffold", effect: "read", contract: c("core.tool_search@1") } },
138
- { id: "ask-user-question", name: "AskUserQuestion", source: "builtin", definedIn: "src/core/ask-question.ts", mountedBy: ["optional"], cards: ["ask-question"], face: { family: "other", effect: "read", contract: c("core.ask_user_question@1"), renderHints: { approvalCard: "question" } } },
139
- { id: "exit-plan-mode", name: "ExitPlanMode", source: "builtin", definedIn: "src/core/present-plan-tool.ts", mountedBy: ["optional"], cards: ["exit-plan-mode"], face: { family: "plan", effect: "read", contract: c("core.exit_plan_mode@1"), renderHints: { userFacingName: "", approvalCard: "plan" } } },
140
- { id: "enter-plan-mode", name: "EnterPlanMode", source: "builtin", definedIn: "src/core/present-plan-tool.ts", mountedBy: ["optional"], cards: ["enter-plan-mode"], face: { family: "plan", effect: "read", contract: c("core.enter_plan_mode@1") } },
141
- { id: "refresh-mcp-tools", name: REFRESH_MCP_TOOLS_TOOL_NAME, source: "builtin", definedIn: "src/core/runner/prepare-protocol-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "protocol", effect: "read" } },
142
- { id: "memory-search", name: "memory_search", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contentOrigin: "local", contract: c("core.memory_search@1", "7"), offload: false } },
143
- { id: "memory-get", name: "memory_get", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contentOrigin: "local", contract: c("core.memory_get@1", "7"), offload: false } },
144
- { id: "memory-index", name: "memory_index", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contentOrigin: "local", contract: c("core.memory_index@1", "2"), offload: false } },
145
- { id: "memory-list", name: "memory_list", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contract: c("core.memory_list@1"), offload: false } },
146
- { id: "memory-read", name: "memory_read", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contract: c("core.memory_read@1"), offload: false } },
147
- { id: "cron-create", name: "CronCreate", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-create"], face: { family: "other", effect: "write", contract: c("core.cron_create@1") } },
148
- { id: "cron-delete", name: "CronDelete", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-delete"], face: { family: "other", effect: "idempotent", contract: c("core.cron_delete@1") } },
149
- { id: "cron-list", name: "CronList", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-list"], face: { family: "other", effect: "read", contract: c("core.cron_list@1") } },
150
- { id: "schedule-wakeup", name: "ScheduleWakeup", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["schedule-wakeup"], face: { family: "other", effect: "write", contract: c("core.schedule_wakeup@1") } },
120
+ { id: "enter-worktree", name: "EnterWorktree", source: "builtin", definedIn: "src/tools/worktree.ts", mountedBy: ["hands"], cards: ["worktree"], face: { family: "other", boundaryLegs: ["env"], effect: "write", contract: c("core.enter_worktree@1") } },
121
+ { id: "exit-worktree", name: "ExitWorktree", source: "builtin", definedIn: "src/tools/worktree.ts", mountedBy: ["hands"], cards: ["worktree"], face: { family: "other", boundaryLegs: ["env"], effect: "write", contract: c("core.exit_worktree@1") } },
122
+ { id: "task-output-hands", name: "TaskOutput", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", boundaryLegs: ["env"], effect: "read", contract: TASK_OUTPUT_CONTRACT } },
123
+ { id: "task-stop-hands", name: "TaskStop", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", boundaryLegs: ["env"], effect: "write", contract: TASK_STOP_CONTRACT } },
124
+ { id: "task-output", name: "TaskOutput", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-output"], face: { family: "delegate", boundaryLegs: "per_call", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
125
+ { id: "task-stop", name: "TaskStop", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-stop"], face: { family: "delegate", boundaryLegs: "per_call", effect: "write", contract: TASK_STOP_CONTRACT } },
126
+ { id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root", "read_unestablished"], face: { family: "other", boundaryLegs: ["env"], effect: "write", contract: c("core.monitor@1"), ruleFace: shellRuleFace(["command", "description", "timeout_ms", "persistent"]), offloadThresholdChars: 10_000 } },
127
+ { id: "send-message", name: "SendMessage", protocol: ["teammate"], source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", boundaryLegs: ["outside_write"], effect: "write", contract: c("core.send_message@1") } },
128
+ { id: "agent-transcript", name: "AgentTranscript", source: "builtin", definedIn: "src/agents/agent-transcript-tool.ts", mountedBy: ["delegation"], cards: ["agent-transcript"], face: { family: "delegate", boundaryLegs: [], effect: "read", contract: c("core.agent_transcript@1") } },
129
+ { id: "list-agents", name: "ListAgents", source: "builtin", definedIn: "src/agents/list-agents-tool.ts", mountedBy: ["delegation", "peerLane"], cards: ["list-agents"], face: { family: "protocol", boundaryLegs: [], effect: "read", aliases: ["ListPeers"], contract: c("core.list_agents@1") } },
130
+ { id: "read-inbox", name: "ReadInbox", source: "builtin", definedIn: "src/agents/read-inbox-tool.ts", mountedBy: ["peerLane"], cards: ["read-inbox"], face: { family: "protocol", boundaryLegs: [], effect: "read", contract: c("core.read_inbox@1"), offload: false, budgetExempt: true } },
131
+ { id: "workflow", name: "Workflow", source: "builtin", definedIn: "src/orchestration/run-workflow-tool.ts", mountedBy: ["workflow"], cards: ["workflow-run"], face: { family: "delegate", boundaryLegs: [], effect: "write", contract: c("core.workflow@1") } },
132
+ { id: "report-blocked", name: "ReportBlocked", protocol: ["delegated"], source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: [], face: { family: "scaffold", boundaryLegs: [], effect: "read", contract: c("core.report_blocked@1") } },
133
+ { id: "report-findings", name: "ReportFindings", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: ["report-findings"], face: { family: "scaffold", boundaryLegs: [], effect: "read", contract: c("core.report_findings@1") } },
134
+ { id: "structured-output", name: "StructuredOutput", protocol: ["structured"], source: "synthetic", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", boundaryLegs: [], effect: "read", contract: c("core.structured_output@1") } },
135
+ { id: "skill", name: "Skill", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", boundaryLegs: [], effect: "read", contract: c("core.skill@1") } },
136
+ { id: "read-tool-result", name: "ReadToolResult", source: "builtin", definedIn: "src/core/tool-result-store.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", boundaryLegs: [], effect: "read", contract: c("core.read_tool_result@1"), budgetExempt: true } },
137
+ { id: "tool-search", name: "ToolSearch", source: "builtin", definedIn: "src/core/runner/tool-disclosure.ts", mountedBy: ["optional"], cards: ["tool-search"], face: { family: "scaffold", boundaryLegs: [], effect: "read", contract: c("core.tool_search@1") } },
138
+ { id: "ask-user-question", name: "AskUserQuestion", source: "builtin", definedIn: "src/core/ask-question.ts", mountedBy: ["optional"], cards: ["ask-question"], face: { family: "other", boundaryLegs: [], effect: "read", contract: c("core.ask_user_question@1"), renderHints: { approvalCard: "question" } } },
139
+ { id: "exit-plan-mode", name: "ExitPlanMode", source: "builtin", definedIn: "src/core/present-plan-tool.ts", mountedBy: ["optional"], cards: ["exit-plan-mode"], face: { family: "plan", boundaryLegs: [], effect: "read", contract: c("core.exit_plan_mode@1"), renderHints: { userFacingName: "", approvalCard: "plan" } } },
140
+ { id: "enter-plan-mode", name: "EnterPlanMode", source: "builtin", definedIn: "src/core/present-plan-tool.ts", mountedBy: ["optional"], cards: ["enter-plan-mode"], face: { family: "plan", boundaryLegs: [], effect: "read", contract: c("core.enter_plan_mode@1") } },
141
+ { id: "refresh-mcp-tools", name: REFRESH_MCP_TOOLS_TOOL_NAME, source: "builtin", definedIn: "src/core/runner/prepare-protocol-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "protocol", boundaryLegs: ["outside_read"], effect: "read" } },
142
+ { id: "memory-search", name: "memory_search", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", boundaryLegs: [], effect: "read", contentOrigin: "local", contract: c("core.memory_search@1", "7"), offload: false } },
143
+ { id: "memory-get", name: "memory_get", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", boundaryLegs: [], effect: "read", contentOrigin: "local", contract: c("core.memory_get@1", "7"), offload: false } },
144
+ { id: "memory-index", name: "memory_index", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", boundaryLegs: [], effect: "read", contentOrigin: "local", contract: c("core.memory_index@1", "2"), offload: false } },
145
+ { id: "memory-list", name: "memory_list", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", boundaryLegs: [], effect: "read", contract: c("core.memory_list@1"), offload: false } },
146
+ { id: "memory-read", name: "memory_read", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", boundaryLegs: [], effect: "read", contract: c("core.memory_read@1"), offload: false } },
147
+ { id: "cron-create", name: "CronCreate", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-create"], face: { family: "other", boundaryLegs: ["env"], effect: "write", contract: c("core.cron_create@1") } },
148
+ { id: "cron-delete", name: "CronDelete", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-delete"], face: { family: "other", boundaryLegs: ["env"], effect: "idempotent", contract: c("core.cron_delete@1") } },
149
+ { id: "cron-list", name: "CronList", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-list"], face: { family: "other", boundaryLegs: ["env"], effect: "read", contract: c("core.cron_list@1") } },
150
+ { id: "schedule-wakeup", name: "ScheduleWakeup", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["schedule-wakeup"], face: { family: "other", boundaryLegs: ["env"], effect: "write", contract: c("core.schedule_wakeup@1") } },
151
151
  {
152
152
  id: "web-fetch", name: "WebFetch", source: "builtin", definedIn: "src/tools/web.ts", mountedBy: ["scenario"], cards: ["web-fetch"],
153
- face: { family: "web", effect: "read", contentOrigin: "external", contract: c("core.web_fetch@1"), ruleFace: { primaryParams: ["url"] }, renderHints: { summaryParams: ["url"], approvalCard: "fetch" }, compactable: true },
153
+ face: { family: "web", boundaryLegs: ["outside_read"], effect: "read", contentOrigin: "external", contract: c("core.web_fetch@1"), ruleFace: { primaryParams: ["url"] }, renderHints: { summaryParams: ["url"], approvalCard: "fetch" }, compactable: true },
154
154
  },
155
155
  {
156
156
  id: "web-search", name: "WebSearch", source: "builtin", definedIn: "src/tools/web.ts", mountedBy: ["scenario"], cards: ["web-search"],
157
- face: { family: "web", effect: "read", contentOrigin: "external", contract: c("core.web_search@1"), ruleFace: { primaryParams: ["query"] }, renderHints: { summaryParams: ["query"] }, compactable: true },
157
+ face: { family: "web", boundaryLegs: ["outside_read"], effect: "read", contentOrigin: "external", contract: c("core.web_search@1"), ruleFace: { primaryParams: ["query"] }, renderHints: { summaryParams: ["query"] }, compactable: true },
158
158
  },
159
- { id: "todo-write", name: TODO_WRITE_TOOL_NAME, source: "builtin", definedIn: "src/tools/todo.ts", mountedBy: ["scenario"], cards: ["todo"], modelGate: "task-scaffold", face: { family: "plan", effect: "idempotent", contract: c("core.todo_write@1") } },
160
- { id: "task-create", name: TASK_CREATE_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "write", contract: c("core.task_create@1") } },
161
- { id: "task-get", name: TASK_GET_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_get@1") } },
162
- { id: "task-update", name: TASK_UPDATE_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "idempotent", contract: c("core.task_update@1") } },
163
- { id: "task-list", name: TASK_LIST_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task-list"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_list@1") } },
164
- { id: "agent", name: "Agent", source: "builtin", definedIn: "src/agents/subagent.ts", mountedBy: ["scenario"], cards: ["agent", "fork"], face: { family: "delegate", contentOrigin: "local", contract: c("core.agent@1") } },
165
- { id: "observer-report", name: "ObserverReport", protocol: ["observer"], source: "builtin", definedIn: "src/agents/observer.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "write", contract: c("core.observer_report@1") } },
166
- { id: "declare-done", name: "DeclareDone", protocol: ["goal"], source: "builtin", definedIn: "src/orchestration/goal.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "read" } },
159
+ { id: "todo-write", name: TODO_WRITE_TOOL_NAME, source: "builtin", definedIn: "src/tools/todo.ts", mountedBy: ["scenario"], cards: ["todo"], modelGate: "task-scaffold", face: { family: "plan", boundaryLegs: [], effect: "idempotent", contract: c("core.todo_write@1") } },
160
+ { id: "task-create", name: TASK_CREATE_TOOL_NAME, protocol: ["teammate"], source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", boundaryLegs: [], effect: "write", contract: c("core.task_create@1") } },
161
+ { id: "task-get", name: TASK_GET_TOOL_NAME, protocol: ["teammate"], source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", boundaryLegs: [], effect: "read", contract: c("core.task_get@1") } },
162
+ { id: "task-update", name: TASK_UPDATE_TOOL_NAME, protocol: ["teammate"], source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", boundaryLegs: [], effect: "idempotent", contract: c("core.task_update@1") } },
163
+ { id: "task-list", name: TASK_LIST_TOOL_NAME, protocol: ["teammate"], source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task-list"], modelGate: "task-scaffold", face: { family: "plan", boundaryLegs: [], effect: "read", contract: c("core.task_list@1") } },
164
+ { id: "agent", name: "Agent", source: "builtin", definedIn: "src/agents/subagent.ts", mountedBy: ["scenario"], cards: ["agent", "fork"], face: { family: "delegate", boundaryLegs: [], contentOrigin: "local", contract: c("core.agent@1") } },
165
+ { id: "observer-report", name: "ObserverReport", protocol: ["observer"], source: "builtin", definedIn: "src/agents/observer.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", boundaryLegs: [], effect: "write", contract: c("core.observer_report@1") } },
166
+ { id: "declare-done", name: "DeclareDone", protocol: ["goal"], source: "builtin", definedIn: "src/orchestration/goal.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", boundaryLegs: [], effect: "read" } },
167
167
  ];
168
168
  export const ENGINE_CARDS = Object.freeze({
169
169
  mcp: "src/core/mcp.ts",