@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
@@ -11,7 +11,8 @@
11
11
  * source by hand cannot drop a row;
12
12
  * · the SCAN — the gate reads this table literally, requires each registered constructor to go through the carrier,
13
13
  * requires the published carriers (`ToolExecuteContext`, `ParentSpecSeat`) to declare exactly the rows they carry,
14
- * and names any hand-written restricting projection outside the carriers.
14
+ * and names any hand-written restricting projection outside the carriers (a field row's: the clamped member at its
15
+ * restricting value on an object, `memory: { enabled: false }`).
15
16
  *
16
17
  * "Tighten-only" here: the value that travels is the RESTRICTING one and only that one, so a parent that clamped nothing
17
18
  * changes nothing about its children, and nothing a child can author lifts a clamp its parent ran under.
@@ -136,6 +137,13 @@ export declare const CLAMP_SET: Readonly<{
136
137
  readonly script: false;
137
138
  readonly carrier: "specSeat";
138
139
  }>;
140
+ memory: Readonly<{
141
+ readonly law: "restricting";
142
+ readonly field: "enabled";
143
+ readonly value: false;
144
+ readonly script: false;
145
+ readonly carrier: "specSeat";
146
+ }>;
139
147
  }>;
140
148
  /** One row's seat name. */
141
149
  export type ClampSeat = keyof typeof CLAMP_SET;
@@ -155,11 +163,19 @@ export type ClampSource = {
155
163
  };
156
164
  /** The clamp source a construction site hands the carrier: EVERY clamp-carrier row as a PRESENT key. */
157
165
  export type ChildClampSource = Pick<ClampSource, ChildClampSeat>;
166
+ /** What one row hands on: its restricting value; an add-only row's list; a field row's restricting MEMBER as a partial
167
+ * declaration of the seat (`{ enabled: false }`) — never the whole declaration, which is the caller's own. */
168
+ type SeatTravels<K extends ClampSeat> = (typeof CLAMP_SET)[K] extends {
169
+ readonly field: infer F extends string;
170
+ readonly value: infer V;
171
+ } ? {
172
+ readonly [P in F]: V;
173
+ } : (typeof CLAMP_SET)[K] extends {
174
+ readonly value: infer V;
175
+ } ? V : NonNullable<TaskSpec[K]>;
158
176
  /** Only the restricting values (add-only rows: a non-empty list) — the shape a waker's clamp seat and a projection take. */
159
177
  export type ClampValues = {
160
- readonly [K in ClampSeat]?: (typeof CLAMP_SET)[K] extends {
161
- readonly value: infer V;
162
- } ? V : NonNullable<TaskSpec[K]>;
178
+ readonly [K in ClampSeat]?: SeatTravels<K>;
163
179
  };
164
180
  /** Every row's seat name, in table order. */
165
181
  export declare const CLAMP_SEATS: readonly ClampSeat[];
@@ -170,7 +186,9 @@ export type ScriptSeat = {
170
186
  /** {@link ScriptSeat}, in table order — the script whitelist's tighten-only half. */
171
187
  export declare const SCRIPT_SEATS: readonly ScriptSeat[];
172
188
  /** Two declarations of one seat under its row's law (`b` is the later one): the restricting value if either carries it,
173
- * else what was authored (`b` over `a`); an add-only list is the union of both, in order, by the row's identity. */
189
+ * else what was authored (`b` over `a`); an add-only list is the union of both, in order, by the row's identity. A field
190
+ * row's seat is a declaration the later author writes whole — what is composed is the one member the row clamps: the
191
+ * restricting value stands on the authored declaration if either side carries it, every other member is the author's. */
174
192
  export declare function composeClampSeat(seat: ClampSeat, a: unknown, b: unknown): unknown;
175
193
  /**
176
194
  * THE union of an add-only seat's lists — the ONE merge every add-only row reads, wherever two of its lists meet: two
@@ -188,8 +206,13 @@ export declare function unionAddOnly<S extends AddOnlySeat>(seat: S, ...lists: R
188
206
  * list is folded or compiled, so a task-layer value is judged as the caller wrote it. Absent ⇒ nothing to judge.
189
207
  */
190
208
  export declare function assertAddOnlySeats(spec: object): void;
209
+ /** The name a refusal gives a seat's clamped cell: the seat, or (field row) `seat.member`. */
210
+ export declare function clampCellPath(seat: ClampSeat): string;
211
+ /** What a declaration of the seat states for the cell its row clamps (the value itself for a whole-value row). */
212
+ export declare function clampCellOf(seat: ClampSeat, declaration: unknown): unknown;
191
213
  /** Would `override` LOOSEN `base` on this seat? Only a restricting row can be loosened: a base carrying the restricting
192
- * value meeting an override that states a different one. */
214
+ * value meeting an override that states a different one (a field row reads the one member it clamps, so an override that
215
+ * declares the seat without stating that member loosens nothing). */
193
216
  export declare function loosensClampSeat(seat: ClampSeat, base: unknown, override: unknown): boolean;
194
217
  /** Read every row (`carrier`: that carrier's rows only) off a record that carries the seats under their own names (a spec, a ctx, a seat). */
195
218
  export declare function clampSourceOf(record: object | undefined): ClampSource;
@@ -201,6 +224,22 @@ export declare function foldClampSources(...records: ReadonlyArray<object | unde
201
224
  /** The restricting values of every row (`carrier` narrows it to one carrier's rows): a row with nothing restricting adds
202
225
  * NO KEY, so the result spreads onto a spec without ever writing a loosening value. */
203
226
  export declare function clampSeatsOf(source: Partial<Record<ClampSeat, unknown>>, carrier?: ClampCarrier): ClampValues;
227
+ /** The field rows' seats (object-valued seats, one member of each clamped). */
228
+ export type FieldSeat = {
229
+ [K in ClampSeat]: (typeof CLAMP_SET)[K] extends {
230
+ readonly field: string;
231
+ } ? K : never;
232
+ }[ClampSeat];
233
+ /** {@link clampSeatsOf} for the FIELD rows alone: what a field row hands on is a partial declaration, so it is written onto a
234
+ * spec by {@link applyClampSeats}, never spread over it. */
235
+ export declare function clampFieldSeatsOf(source: Partial<Record<ClampSeat, unknown>>): Pick<ClampValues, FieldSeat>;
236
+ /**
237
+ * Write clamp values onto a spec — the ONE way a projection lands on a record. A whole-value row lands as the value; a field
238
+ * row REWRITES the one member it clamps on the spec's own declaration and keeps every other member (a declaration that was
239
+ * deleted instead would leave the next mint nothing to read, and the clamp would end one level down). Returns a new object;
240
+ * `spec` is not mutated; a seat the values do not carry is left as the spec has it.
241
+ */
242
+ export declare function applyClampSeats<S extends object>(spec: S, values: ClampValues): S;
204
243
  /** The coded refusal of a clamp record a caller handed in (`config.invalid_argument`). */
205
244
  export declare function clampRecordError(message: string): Error & {
206
245
  code: "config.invalid_argument";
@@ -102,14 +102,31 @@ export const CLAMP_SET = Object.freeze({
102
102
  memoryPersistenceCapable: Object.freeze({ law: "restricting", value: false, script: true, carrier: "specSeat" }),
103
103
  fileHistory: Object.freeze({ law: "restricting", value: "disabled", script: false, carrier: "specSeat" }),
104
104
  promptForm: Object.freeze({ law: "restricting", value: "minimal", script: false, carrier: "specSeat" }),
105
+ memory: Object.freeze({ law: "restricting", field: "enabled", value: false, script: false, carrier: "specSeat" }),
105
106
  });
107
+ function fieldOf(row) {
108
+ return "field" in row ? row.field : undefined;
109
+ }
110
+ function cellOf(row, declaration) {
111
+ const field = fieldOf(row);
112
+ if (field === undefined)
113
+ return declaration;
114
+ return typeof declaration === "object" && declaration !== null && !Array.isArray(declaration) ? declaration[field] : undefined;
115
+ }
116
+ function withCell(row, declaration, cell) {
117
+ const field = fieldOf(row);
118
+ if (field === undefined)
119
+ return cell;
120
+ const own = typeof declaration === "object" && declaration !== null && !Array.isArray(declaration) ? declaration : {};
121
+ return { ...own, [field]: cell };
122
+ }
106
123
  const ROWS = Object.freeze(Object.entries(CLAMP_SET));
107
124
  export const CLAMP_SEATS = Object.freeze(ROWS.map(([seat]) => seat));
108
125
  export const SCRIPT_SEATS = Object.freeze(ROWS.filter(([, row]) => row.script !== false).map(([seat]) => seat));
109
126
  export function composeClampSeat(seat, a, b) {
110
127
  const row = CLAMP_SET[seat];
111
128
  if (row.law === "restricting")
112
- return a === row.value || b === row.value ? row.value : (b ?? a);
129
+ return cellOf(row, a) === row.value || cellOf(row, b) === row.value ? withCell(row, b ?? a, row.value) : (b ?? a);
113
130
  const lists = [a, b];
114
131
  return unionAddOnly(seat, ...lists);
115
132
  }
@@ -156,14 +173,28 @@ export function assertAddOnlySeats(spec) {
156
173
  }
157
174
  }
158
175
  }
176
+ export function clampCellPath(seat) {
177
+ const field = fieldOf(CLAMP_SET[seat]);
178
+ return field === undefined ? seat : `${seat}.${field}`;
179
+ }
180
+ export function clampCellOf(seat, declaration) {
181
+ return cellOf(CLAMP_SET[seat], declaration);
182
+ }
159
183
  export function loosensClampSeat(seat, base, override) {
160
184
  const row = CLAMP_SET[seat];
161
- return row.law === "restricting" && base === row.value && override !== undefined && override !== row.value;
185
+ if (row.law !== "restricting")
186
+ return false;
187
+ const stated = cellOf(row, override);
188
+ return cellOf(row, base) === row.value && stated !== undefined && stated !== row.value;
162
189
  }
163
190
  function projected(seat, v) {
164
191
  const row = CLAMP_SET[seat];
165
- if (row.law === "restricting")
166
- return v === row.value ? row.value : undefined;
192
+ if (row.law === "restricting") {
193
+ if (cellOf(row, v) !== row.value)
194
+ return undefined;
195
+ const travels = withCell(row, undefined, row.value);
196
+ return fieldOf(row) === undefined ? travels : Object.freeze(travels);
197
+ }
167
198
  return Array.isArray(v) && v.length > 0 ? Object.freeze(v.map((e) => (typeof e === "object" && e !== null ? { ...e } : e))) : undefined;
168
199
  }
169
200
  export function clampSourceOf(record, carrier) {
@@ -184,6 +215,27 @@ export function clampSeatsOf(source, carrier) {
184
215
  }
185
216
  return out;
186
217
  }
218
+ export function clampFieldSeatsOf(source) {
219
+ const out = {};
220
+ for (const [seat, row] of ROWS) {
221
+ if (fieldOf(row) === undefined)
222
+ continue;
223
+ const v = projected(seat, source[seat]);
224
+ if (v !== undefined)
225
+ out[seat] = v;
226
+ }
227
+ return out;
228
+ }
229
+ export function applyClampSeats(spec, values) {
230
+ const out = { ...spec };
231
+ for (const [seat, row] of ROWS) {
232
+ const v = values[seat];
233
+ if (v === undefined)
234
+ continue;
235
+ out[seat] = fieldOf(row) === undefined ? v : withCell(row, out[seat], cellOf(row, v));
236
+ }
237
+ return out;
238
+ }
187
239
  export function clampRecordError(message) {
188
240
  const e = new Error(message);
189
241
  e.code = "config.invalid_argument";
@@ -1,3 +1,4 @@
1
+ import { describeRejectedValue } from "./tool-search-setting.js";
1
2
  export const COMPLIANCE_CAPABILITIES = Object.freeze(["mcp_servers", "workflows", "web_fetch", "org_memory_mount"]);
2
3
  export const BUILTIN_COMPLIANCE_DENIES = {
3
4
  hipaa: ["web_fetch", "org_memory_mount"],
@@ -26,7 +27,8 @@ export function resolveComplianceDenies(posture) {
26
27
  export function complianceCapabilityProblem(cap, owner) {
27
28
  if (COMPLIANCE_CAPABILITIES.some((member) => member === cap))
28
29
  return undefined;
29
- return (`${owner} contains "${String(cap)}", not a member of the closed capability set ` +
30
+ const shown = (typeof cap === "object" && cap !== null) || typeof cap === "function" ? describeRejectedValue(cap) : `"${String(cap)}"`;
31
+ return (`${owner} contains ${shown}, not a member of the closed capability set ` +
30
32
  `(${COMPLIANCE_CAPABILITIES.join(", ")}) — a misspelled deny must never silently not-enforce.`);
31
33
  }
32
34
  export const WEB_FETCH_TOOL_NAME = "WebFetch";
@@ -86,7 +86,7 @@ export declare function consolidateScope(scope: string, deps: ConsolidateScopeDe
86
86
  * plain max) would lose `id > cursor` and be exiled from consolidation forever. Pass the pass's
87
87
  * `stats.consolidatedIds` (processed), `stats.addedIds`, and `stats.failedIds`.
88
88
  *
89
- * No-op when the store lacks the cursor pair (back-compat: an old store keeps the pure-inline behavior).
89
+ * No-op when the store lacks the cursor pair (back-compat, RL-1654-consolidate-scope: an old store keeps the pure-inline behavior).
90
90
  * Never throws — a cursor-advance failure routes to `onWarn` (the inline notes are already durable; the
91
91
  * worst case is the next periodic pass re-sees them, which `runMemoryConsolidation` handles idempotently).
92
92
  */
@@ -0,0 +1,213 @@
1
+ /**
2
+ * The device lane's CROSS-BOUNDARY vocabulary: the run tree's two taint bits and its posture fact, the legs one call
3
+ * travels, the one table those answer a call's obligation from, and the session record that carries a tree's state from
4
+ * one leg of a session to the next. Pure data and pure functions — no I/O, no clock, no engine state.
5
+ *
6
+ * THE RULE. A call's cross-boundary obligation is decided by three things and nothing else: the legs THIS call travels
7
+ * (declared by the code that dispatches it — a constant on most definitions, given per call by the two that route by
8
+ * their arguments), whether the environment a leg reaches declares itself the target device right now, and the tree's
9
+ * three bits. Two outcomes, independent of each other: `mandate` — a person confirms this call; `record` — once this
10
+ * call is allowed, the tree's off-target-read bit is set. {@link crossBoundaryObligation} is the one function that
11
+ * answers it; every place that asks calls it and none restates a cell.
12
+ */
13
+ import type { AssertAllKeysHandled } from "./ask-origin.js";
14
+ import type { EngineNotice } from "./engine-notice.js";
15
+ import type { HostDecider } from "./gate-outcome.js";
16
+ /** The run tree's two TAINT bits: the tree executes on a target device (`targetExecutionArmed`), and an off-target read
17
+ * was allowed into it since (`offTargetReadObserved`). Presence-coded on every carrier: `true` or absent, never `false`. */
18
+ export declare const CROSS_BOUNDARY_TAINT_BITS: readonly ["targetExecutionArmed", "offTargetReadObserved"];
19
+ export type CrossBoundaryTaintBit = (typeof CROSS_BOUNDARY_TAINT_BITS)[number];
20
+ /**
21
+ * The tree's state: the two taint bits and `governed` — the tree has run under the `multi_tenant` posture since it was
22
+ * first armed. `governed` is a posture fact, not a taint bit; it rides the same carriers and joins the same way. Each
23
+ * member is `true` or absent. The state only grows ({@link joinCrossBoundaryTree}).
24
+ */
25
+ export interface CrossBoundaryTree {
26
+ readonly targetExecutionArmed?: true;
27
+ readonly offTargetReadObserved?: true;
28
+ readonly governed?: true;
29
+ }
30
+ /** The deployment's posture words (`RunnerDeps.crossBoundaryPosture`): who is hurt when a tree that executes on a device
31
+ * reaches past it — the device's own user (`single_user`), or an organization's other tenants (`multi_tenant`). */
32
+ export declare const CROSS_BOUNDARY_POSTURES: readonly ["single_user", "multi_tenant"];
33
+ export type CrossBoundaryPosture = (typeof CROSS_BOUNDARY_POSTURES)[number];
34
+ /**
35
+ * The legs one call can travel: `env` — into or out of an execution environment (the call's arguments reach it, or its
36
+ * content comes back from it); `outside_read` — content fetched from beyond the boundary; `outside_write` — an effect
37
+ * produced beyond the boundary. An empty set is a call whose effect stays inside the engine (its process or its session
38
+ * store).
39
+ */
40
+ export declare const BOUNDARY_LEGS: readonly ["env", "outside_read", "outside_write"];
41
+ export type BoundaryLeg = (typeof BOUNDARY_LEGS)[number];
42
+ /** A definition's declaration of its legs: the legs every call travels, or `"per_call"` — the code that dispatches the call
43
+ * gives them per call (the destination is chosen by the call's own arguments). */
44
+ export type BoundaryLegsDeclaration = BoundaryLegList | "per_call";
45
+ /** A list of legs, in any order, each at most once on a roster row. */
46
+ export type BoundaryLegList = readonly BoundaryLeg[];
47
+ /** What one call owes: a person's confirmation (`mandate`), the off-target-read bit once allowed (`record`), both, or neither. */
48
+ export interface CrossBoundaryObligation {
49
+ readonly mandate: boolean;
50
+ readonly record: boolean;
51
+ }
52
+ /** What a `"per_call"` definition's dispatch gives for one call: the legs it travels and, when its `env` leg reaches an
53
+ * environment other than the calling leg's own (a background shell's), that environment — its declaration is read as the call
54
+ * is judged. */
55
+ export interface BoundaryLegsGiven {
56
+ readonly legs: readonly BoundaryLeg[];
57
+ readonly env?: {
58
+ readonly externalContentTarget?: boolean;
59
+ };
60
+ }
61
+ declare const CROSS_BOUNDARY_TREE_TOKEN: unique symbol;
62
+ /** A run tree's cross-boundary IDENTITY — opaque and frozen, minted by the engine alone: a tool is handed it so a delegation or
63
+ * workflow lane can join its children to the same tree and a registry handle can name the tree that spawned it, compared by
64
+ * identity. Nothing reads or writes the tree's state through it; the state stays the engine's. */
65
+ export interface CrossBoundaryTreeToken {
66
+ readonly [CROSS_BOUNDARY_TREE_TOKEN]: true;
67
+ }
68
+ /** A `"per_call"` definition's resolver, written beside the code that dispatches the call: handed the call's arguments as the
69
+ * tool reads them and the calling tree's {@link CrossBoundaryTreeToken} (a handle that recorded one compares it by identity). No I/O. */
70
+ export type BoundaryLegsFor = (args: unknown, at: {
71
+ readonly tree: CrossBoundaryTreeToken;
72
+ }) => BoundaryLegsGiven;
73
+ /** Why an exit refused a call its decision allowed: the call needs a person's confirmation under the tree's state NOW and the
74
+ * decision was not a person's (`unconfirmed`), or the session record the call owes could not be written before it ran
75
+ * (`unrecorded`). */
76
+ export interface CrossBoundaryExitRefusal {
77
+ readonly kind: "unconfirmed" | "unrecorded";
78
+ readonly message: string;
79
+ }
80
+ /** One call as the cross-boundary seat reads it: its wire name, its arguments as they would run, and its id (the trace names it). */
81
+ export interface CrossBoundaryCall {
82
+ readonly toolName: string;
83
+ readonly args: unknown;
84
+ readonly toolCallId?: string;
85
+ }
86
+ /** The gate's cross-boundary seat — ENGINE-BUILT per leg (`runner/cross-boundary-lane.ts`), never a deployment callback. */
87
+ export interface CrossBoundaryCheck {
88
+ /** This call's obligation under the tree's state at this moment (the station reads it on the final arguments). */
89
+ obligation(call: CrossBoundaryCall): CrossBoundaryObligation;
90
+ /** The allow exit's rule, on the bytes that run and the party that decided: re-reads the obligation; a mandate the decider cannot
91
+ * answer refuses; otherwise the off-target-read bit the call owes is joined and the session record is written before it runs. */
92
+ exit(call: CrossBoundaryCall, decidedBy: HostDecider): Promise<CrossBoundaryExitRefusal | undefined>;
93
+ }
94
+ /** The gate input's seat for {@link CrossBoundaryCheck}: present on a leg whose station is ENGAGED (a posture is declared, the leg runs
95
+ * on a target, or its tree is governed); absent ⇒ the decision path is byte-identical. */
96
+ export interface CrossBoundaryGateSeat {
97
+ crossBoundaryCheck?: CrossBoundaryCheck;
98
+ }
99
+ /** The table's rows. An `env` leg is judged by where it lands: a target environment is its own row, any other environment
100
+ * reads as both outside rows ({@link ENV_LEG_ROWS}). */
101
+ type LegRow = "env_to_target" | "outside_read" | "outside_write";
102
+ /** THE table. The host-lane column is empty, so a tree that never armed decides exactly as it would without this table. */
103
+ declare const OBLIGATION_TABLE: {
104
+ readonly env_to_target: {
105
+ readonly host_lane: "none";
106
+ readonly armed: "none";
107
+ readonly armed_observed: "mandate_if_governed";
108
+ };
109
+ readonly outside_read: {
110
+ readonly host_lane: "none";
111
+ readonly armed: "record";
112
+ readonly armed_observed: "none";
113
+ };
114
+ readonly outside_write: {
115
+ readonly host_lane: "none";
116
+ readonly armed: "mandate_if_governed";
117
+ readonly armed_observed: "mandate_if_governed";
118
+ };
119
+ };
120
+ export type ObligationTableCoversEveryRow = AssertAllKeysHandled<Exclude<LegRow, keyof typeof OBLIGATION_TABLE>>;
121
+ /** The obligation of one call — the union of its legs' cells under the tree's current state. `legs` are the legs this call
122
+ * travels (a definition's constant, or what its dispatch gave for this call); `reachedEnvIsTarget` — the environment its
123
+ * `env` leg reaches declares itself the target right now (read off that environment, never off the tree's history; unread
124
+ * when the call has no `env` leg). A leg outside {@link BOUNDARY_LEGS}, a non-boolean target fact or a malformed tree throws. */
125
+ export declare function crossBoundaryObligation(legs: readonly BoundaryLeg[], reachedEnvIsTarget: boolean, tree: CrossBoundaryTree): CrossBoundaryObligation;
126
+ /** The tree is GOVERNED — what the table's `mandate_if_governed` cells read, and so what makes the session record a carrier some
127
+ * judgment reads (a record that cannot be written refuses its call on a governed tree only). */
128
+ export declare function crossBoundaryTreeGoverned(tree: CrossBoundaryTree): boolean;
129
+ /** The join of two tree states: every bit either holds. Monotone — it never clears a bit — and returns a fresh frozen
130
+ * state, never one of its operands. */
131
+ export declare function joinCrossBoundaryTree(a: CrossBoundaryTree, b: CrossBoundaryTree): CrossBoundaryTree;
132
+ /** Whether `covering` holds every bit `tree` holds — a record that covers a tree's state need not be written again. */
133
+ export declare function crossBoundaryTreeCovers(covering: CrossBoundaryTree, tree: CrossBoundaryTree): boolean;
134
+ /** The strictest state: what a record that cannot be read stands for. */
135
+ export declare const STRICTEST_CROSS_BOUNDARY_TREE: CrossBoundaryTree;
136
+ /** The `customType` of the session entry that records a tree's state: a custom entry, persisted with the transcript and
137
+ * never replayed into model context — the state lives where the content it describes lives. */
138
+ export declare const CROSS_BOUNDARY_RECORD_TYPE = "engine:cross_boundary";
139
+ /** The record's `data`: written only for an armed tree, so `targetExecutionArmed` is always present. */
140
+ export interface CrossBoundaryRecordData {
141
+ readonly targetExecutionArmed: true;
142
+ readonly offTargetReadObserved?: true;
143
+ readonly governed?: true;
144
+ }
145
+ /** The one spelling of a record's `data` for a tree state. A tree that is not armed has no record: refused. */
146
+ export declare function crossBoundaryRecordData(tree: CrossBoundaryTree): CrossBoundaryRecordData;
147
+ /** What a branch says about its tree: no record on it; the join of its records (`entryId` = the newest one's); or a record — or an
148
+ * entry — that cannot be read, which reads as the strictest state, with the sentence a disclosure names. A record covers the content
149
+ * before it: `uncoveredContent` — conversation content (the session layer's `hasConversationContent`: a message, a custom message, a
150
+ * compaction) follows the newest record, or with no record is on the branch at all — is content produced under a state no record says. */
151
+ export type CrossBoundaryRecordRead = {
152
+ readonly kind: "absent";
153
+ readonly uncoveredContent: boolean;
154
+ } | {
155
+ readonly kind: "recorded";
156
+ readonly entryId?: string;
157
+ readonly tree: CrossBoundaryTree;
158
+ readonly uncoveredContent: boolean;
159
+ } | {
160
+ readonly kind: "malformed";
161
+ readonly entryId?: string;
162
+ readonly tree: CrossBoundaryTree;
163
+ readonly why: string;
164
+ };
165
+ /** The members a session entry is read by. */
166
+ export interface CrossBoundaryRecordEntry {
167
+ readonly type: string;
168
+ readonly id?: string;
169
+ readonly customType?: unknown;
170
+ readonly data?: unknown;
171
+ }
172
+ /**
173
+ * Read a branch's tree state: the JOIN of every cross-boundary record on `branch` (entries in path order) — the state only grows, so
174
+ * a later record holding fewer bits never lowers it. Any record whose shape is not a record's, and any entry that cannot be read
175
+ * (one of its members throws), makes the read {@link STRICTEST_CROSS_BOUNDARY_TREE}, never a looser state. No record reads `absent`.
176
+ */
177
+ export declare function readCrossBoundaryRecord(branch: readonly CrossBoundaryRecordEntry[]): CrossBoundaryRecordRead;
178
+ /** The one mint of `config.cross_boundary_posture_undeclared`: an armed tree on a deployment that configured no posture, so
179
+ * the permissive `single_user` reading is in force. `detail.taint` lists the tree's taint bits. */
180
+ export declare function crossBoundaryPostureUndeclaredNotice(at: {
181
+ readonly sessionId: string;
182
+ readonly runId: string;
183
+ readonly tree: CrossBoundaryTree;
184
+ }): EngineNotice;
185
+ /** Where a session record's write was owed: a leg's prepare, an exit before its call runs, a suspend before its row, a settle. */
186
+ export type CrossBoundaryRecordPhase = "prepare" | "exit" | "suspend" | "settle"; /** The one mint of `config.cross_boundary_record_unwritten`: a session record that could not be written and refused nothing — on a tree
187
+ * that is not governed (no judgment reads the record), or at a governed leg's prepare / settle (no call to refuse; the next leg then
188
+ * reads the content after the newest record as off-target). A governed exit refuses its call and a governed suspend its row instead. */
189
+ export declare function crossBoundaryRecordUnwrittenNotice(at: {
190
+ readonly sessionId: string;
191
+ readonly runId: string;
192
+ readonly phase: CrossBoundaryRecordPhase;
193
+ readonly governed: boolean;
194
+ readonly cause: string;
195
+ }): EngineNotice;
196
+ /** The face a protocol tool (an MCP tool, an A2A skill) carries for its JUDGED `toolAxes` entry (`readToolAxes`' copy): the empty leg
197
+ * set when the entry declares `boundaryFlow: "engine"`, nothing otherwise (its roster row then states the legs its effect implies).
198
+ * The one translation of that declaration onto a mounted instance; the MCP intake and the A2A peer mint both call it. */
199
+ export declare function boundaryFlowFace(entry: {
200
+ readonly boundaryFlow?: "engine";
201
+ } | undefined): {
202
+ boundaryLegs?: readonly BoundaryLeg[];
203
+ };
204
+ /**
205
+ * The legs a ROSTER ROW states, from where the instance came from, what it declares and its resolved effect. The engine's
206
+ * own definitions (`builtin` / `synthetic` rows) and the protocol tools the engine materialized (`mcp` / `a2a`) are read by
207
+ * their declaration; any other row — and a row of those sources that declares nothing — by its effect: `read` reaches beyond
208
+ * the boundary to read, anything else to write. A declaration that is neither `"per_call"` nor a list of legs is refused on EVERY
209
+ * row, whoever declared it (`undefined` back, the caller refuses the row loudly): a row that does not read a declaration still does
210
+ * not let a bad one pass silently. The returned list is a fresh array in {@link BOUNDARY_LEGS} order.
211
+ */
212
+ export declare function rosterBoundaryLegs(source: string, declared: unknown, effect: "read" | "write" | "idempotent"): BoundaryLegsDeclaration | undefined;
213
+ export {};
@@ -0,0 +1,196 @@
1
+ import { hasConversationContent } from "./session.js";
2
+ export const CROSS_BOUNDARY_TAINT_BITS = Object.freeze(["targetExecutionArmed", "offTargetReadObserved"]);
3
+ export const CROSS_BOUNDARY_POSTURES = Object.freeze(["single_user", "multi_tenant"]);
4
+ const postureSeatIsTheSet = true;
5
+ void postureSeatIsTheSet;
6
+ export const BOUNDARY_LEGS = Object.freeze(["env", "outside_read", "outside_write"]);
7
+ const LEG_SET = new Set(BOUNDARY_LEGS);
8
+ const OBLIGATION_TABLE = {
9
+ env_to_target: { host_lane: "none", armed: "none", armed_observed: "mandate_if_governed" },
10
+ outside_read: { host_lane: "none", armed: "record", armed_observed: "none" },
11
+ outside_write: { host_lane: "none", armed: "mandate_if_governed", armed_observed: "mandate_if_governed" },
12
+ };
13
+ const ENV_LEG_ROWS = { target: ["env_to_target"], off_target: ["outside_read", "outside_write"] };
14
+ const TREE_MEMBERS = [...CROSS_BOUNDARY_TAINT_BITS, "governed"];
15
+ function assertTree(tree, at) {
16
+ if (typeof tree !== "object" || tree === null || Array.isArray(tree))
17
+ throw new TypeError(`${at}: a cross-boundary tree is a record of presence bits (got ${tree === null ? "null" : Array.isArray(tree) ? "an array" : typeof tree})`);
18
+ for (const member of TREE_MEMBERS) {
19
+ const v = tree[member];
20
+ if (v !== undefined && v !== true)
21
+ throw new TypeError(`${at}: tree member "${member}" is presence-coded (true or absent), got ${typeof v}`);
22
+ }
23
+ }
24
+ export function crossBoundaryObligation(legs, reachedEnvIsTarget, tree) {
25
+ if (!Array.isArray(legs))
26
+ throw new TypeError(`crossBoundaryObligation: legs is a list of ${BOUNDARY_LEGS.join(" | ")}`);
27
+ if (typeof reachedEnvIsTarget !== "boolean")
28
+ throw new TypeError("crossBoundaryObligation: reachedEnvIsTarget is a boolean");
29
+ assertTree(tree, "crossBoundaryObligation");
30
+ const column = tree.targetExecutionArmed !== true ? "host_lane" : tree.offTargetReadObserved === true ? "armed_observed" : "armed";
31
+ let mandate = false;
32
+ let record = false;
33
+ for (const leg of legs) {
34
+ if (!LEG_SET.has(leg))
35
+ throw new TypeError(`crossBoundaryObligation: ${JSON.stringify(leg)} is not a boundary leg (${BOUNDARY_LEGS.join(", ")})`);
36
+ const rows = leg === "env" ? ENV_LEG_ROWS[reachedEnvIsTarget ? "target" : "off_target"] : [leg];
37
+ for (const row of rows) {
38
+ const cell = OBLIGATION_TABLE[row][column];
39
+ if (cell === "record")
40
+ record = true;
41
+ else if (cell === "mandate_if_governed" && crossBoundaryTreeGoverned(tree))
42
+ mandate = true;
43
+ }
44
+ }
45
+ return Object.freeze({ mandate, record });
46
+ }
47
+ export function crossBoundaryTreeGoverned(tree) {
48
+ return tree.governed === true;
49
+ }
50
+ export function joinCrossBoundaryTree(a, b) {
51
+ assertTree(a, "joinCrossBoundaryTree");
52
+ assertTree(b, "joinCrossBoundaryTree");
53
+ const out = {};
54
+ if (a.targetExecutionArmed === true || b.targetExecutionArmed === true)
55
+ out.targetExecutionArmed = true;
56
+ if (a.offTargetReadObserved === true || b.offTargetReadObserved === true)
57
+ out.offTargetReadObserved = true;
58
+ if (a.governed === true || b.governed === true)
59
+ out.governed = true;
60
+ return Object.freeze(out);
61
+ }
62
+ export function crossBoundaryTreeCovers(covering, tree) {
63
+ assertTree(covering, "crossBoundaryTreeCovers");
64
+ assertTree(tree, "crossBoundaryTreeCovers");
65
+ return TREE_MEMBERS.every((m) => tree[m] !== true || covering[m] === true);
66
+ }
67
+ export const STRICTEST_CROSS_BOUNDARY_TREE = Object.freeze({ targetExecutionArmed: true, offTargetReadObserved: true, governed: true });
68
+ export const CROSS_BOUNDARY_RECORD_TYPE = "engine:cross_boundary";
69
+ export function crossBoundaryRecordData(tree) {
70
+ assertTree(tree, "crossBoundaryRecordData");
71
+ if (tree.targetExecutionArmed !== true)
72
+ throw new TypeError("crossBoundaryRecordData: only an armed tree is recorded");
73
+ return Object.freeze({ targetExecutionArmed: true, ...(tree.offTargetReadObserved === true ? { offTargetReadObserved: true } : {}), ...(tree.governed === true ? { governed: true } : {}) });
74
+ }
75
+ const RECORD_MEMBERS = TREE_MEMBERS;
76
+ function ownedRecordTree(data) {
77
+ if (typeof data !== "object" || data === null || Array.isArray(data) || Object.getPrototypeOf(data) !== Object.prototype)
78
+ return { why: `its data is ${data === null ? "null" : Array.isArray(data) ? "an array" : typeof data === "object" ? "not a plain record" : typeof data}` };
79
+ const owned = new Map();
80
+ for (const key of Reflect.ownKeys(data)) {
81
+ if (typeof key !== "string" || !RECORD_MEMBERS.includes(key))
82
+ return { why: `its data carries ${typeof key === "string" ? JSON.stringify(key.slice(0, 40)) : "a symbol key"}, which is not a member` };
83
+ const d = Object.getOwnPropertyDescriptor(data, key);
84
+ if (d === undefined || !("value" in d))
85
+ return { why: `"${key}" is not a data member` };
86
+ owned.set(key, d.value);
87
+ }
88
+ if (owned.get("targetExecutionArmed") !== true)
89
+ return { why: `"targetExecutionArmed" is not true` };
90
+ for (const m of ["offTargetReadObserved", "governed"])
91
+ if (owned.get(m) !== undefined && owned.get(m) !== true)
92
+ return { why: `"${m}" is neither true nor absent` };
93
+ return { tree: Object.freeze({ targetExecutionArmed: true, ...(owned.get("offTargetReadObserved") === true ? { offTargetReadObserved: true } : {}), ...(owned.get("governed") === true ? { governed: true } : {}) }) };
94
+ }
95
+ function readBranchEntry(branch, i) {
96
+ let id;
97
+ try {
98
+ const e = branch[i];
99
+ if (typeof e !== "object" || e === null)
100
+ return { kind: "other", content: false };
101
+ const { type, customType } = e;
102
+ if (type !== "custom" || customType !== CROSS_BOUNDARY_RECORD_TYPE)
103
+ return { kind: "other", content: typeof type === "string" && hasConversationContent([{ type }]) };
104
+ const rawId = e.id;
105
+ id = typeof rawId === "string" ? rawId : undefined;
106
+ const read = ownedRecordTree(e.data);
107
+ return "why" in read ? { kind: "unreadable", ...(id !== undefined ? { id } : {}), why: read.why } : { kind: "record", ...(id !== undefined ? { id } : {}), tree: read.tree };
108
+ }
109
+ catch {
110
+ return { kind: "unreadable", ...(id !== undefined ? { id } : {}), why: "an entry on the branch could not be read" };
111
+ }
112
+ }
113
+ export function readCrossBoundaryRecord(branch) {
114
+ const strictest = (id, why) => Object.freeze({ kind: "malformed", ...(id !== undefined ? { entryId: id } : {}), tree: STRICTEST_CROSS_BOUNDARY_TREE, why: `the cross-boundary record on this branch cannot be read: ${why}` });
115
+ let isList;
116
+ let length;
117
+ try {
118
+ isList = Array.isArray(branch);
119
+ length = isList ? branch.length : undefined;
120
+ }
121
+ catch {
122
+ return strictest(undefined, "the branch could not be read");
123
+ }
124
+ if (!isList)
125
+ throw new TypeError("readCrossBoundaryRecord: the branch is a list of session entries");
126
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0)
127
+ return strictest(undefined, "the branch could not be read");
128
+ let joined;
129
+ let newest;
130
+ let uncoveredContent = false;
131
+ for (let i = 0; i < length; i++) {
132
+ const entry = readBranchEntry(branch, i);
133
+ if (entry.kind === "other") {
134
+ uncoveredContent ||= entry.content;
135
+ continue;
136
+ }
137
+ if (entry.kind === "unreadable")
138
+ return strictest(entry.id, entry.why);
139
+ joined = joinCrossBoundaryTree(joined ?? {}, entry.tree);
140
+ newest = entry.id;
141
+ uncoveredContent = false;
142
+ }
143
+ return joined === undefined ? Object.freeze({ kind: "absent", uncoveredContent }) : Object.freeze({ kind: "recorded", ...(newest !== undefined ? { entryId: newest } : {}), tree: joined, uncoveredContent });
144
+ }
145
+ export function crossBoundaryPostureUndeclaredNotice(at) {
146
+ assertTree(at.tree, "crossBoundaryPostureUndeclaredNotice");
147
+ return {
148
+ code: "config.cross_boundary_posture_undeclared",
149
+ message: 'This run executes on an external-content target (the device lane) and no RunnerDeps.crossBoundaryPosture is configured — the engine reads the permissive "single_user" posture. ' +
150
+ 'On a multi-tenant deployment declare "multi_tenant", so an org-side write from a tainted run is put to a person; on a single-user deployment declare "single_user" to silence this line.',
151
+ detail: { sessionId: at.sessionId, runId: at.runId, taint: CROSS_BOUNDARY_TAINT_BITS.filter((b) => at.tree[b] === true) },
152
+ };
153
+ }
154
+ export function crossBoundaryRecordUnwrittenNotice(at) {
155
+ return {
156
+ code: "config.cross_boundary_record_unwritten",
157
+ message: `The session's cross-boundary record could not be written at ${at.phase} (${at.cause.slice(0, 300)}) — ` +
158
+ (at.governed
159
+ ? "the tree is governed: the next leg on this session reads the content after its newest record as off-target, so its device calls are put to a person."
160
+ : "the tree is not governed: no judgment reads the record, so its failure refused nothing; the tree's growth, or the leg's end, writes next."),
161
+ detail: { sessionId: at.sessionId, runId: at.runId, phase: at.phase, governed: at.governed, cause: at.cause.slice(0, 300) },
162
+ };
163
+ }
164
+ const NO_LEGS = Object.freeze([]);
165
+ export function boundaryFlowFace(entry) {
166
+ return entry?.boundaryFlow === "engine" ? { boundaryLegs: NO_LEGS } : {};
167
+ }
168
+ function ownedLegList(v) {
169
+ try {
170
+ if (!Array.isArray(v))
171
+ return undefined;
172
+ const length = v.length;
173
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0)
174
+ return undefined;
175
+ const held = new Set();
176
+ for (let i = 0; i < length; i++) {
177
+ const leg = v[i];
178
+ if (!LEG_SET.has(leg))
179
+ return undefined;
180
+ held.add(leg);
181
+ }
182
+ return BOUNDARY_LEGS.filter((l) => held.has(l));
183
+ }
184
+ catch {
185
+ return undefined;
186
+ }
187
+ }
188
+ export function rosterBoundaryLegs(source, declared, effect) {
189
+ const list = declared === undefined || declared === "per_call" ? undefined : ownedLegList(declared);
190
+ if (declared !== undefined && declared !== "per_call" && list === undefined)
191
+ return undefined;
192
+ const reads = source === "builtin" || source === "synthetic" || source === "mcp" || source === "a2a";
193
+ if (!reads || declared === undefined)
194
+ return effect === "read" ? ["outside_read"] : ["outside_write"];
195
+ return declared === "per_call" ? "per_call" : list;
196
+ }