@sema-agent/core 7.51.1 → 7.52.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 (372) hide show
  1. package/CHANGELOG.md +550 -2
  2. package/dist/agents/agent-transcript-tool.d.ts +1 -1
  3. package/dist/agents/cascade.js +4 -0
  4. package/dist/agents/cumulative-stats.d.ts +3 -0
  5. package/dist/agents/cumulative-stats.js +3 -0
  6. package/dist/agents/list-agents-tool.d.ts +1 -1
  7. package/dist/agents/read-inbox-tool.d.ts +1 -1
  8. package/dist/agents/repair-loop.js +4 -0
  9. package/dist/agents/send-message-tool.d.ts +11 -16
  10. package/dist/agents/send-message-tool.js +11 -13
  11. package/dist/agents/subagent.d.ts +5 -0
  12. package/dist/agents/subagent.js +7 -4
  13. package/dist/agents/tool-filter.d.ts +40 -13
  14. package/dist/agents/tool-filter.js +28 -14
  15. package/dist/bench/metrics.d.ts +1 -0
  16. package/dist/brain/anthropic.js +16 -12
  17. package/dist/brain/model-presets.d.ts +4 -5
  18. package/dist/brain/model-presets.js +4 -4
  19. package/dist/brain/open-responses.js +16 -12
  20. package/dist/brain/openai.js +16 -12
  21. package/dist/brain/request-params.d.ts +12 -33
  22. package/dist/brain/request-params.js +5 -2
  23. package/dist/brain/request-route.d.ts +9 -8
  24. package/dist/brain/request-route.js +1 -0
  25. package/dist/brain/route-adjudicator.d.ts +117 -47
  26. package/dist/brain/route-adjudicator.js +171 -42
  27. package/dist/brain/route-conformance.js +2 -2
  28. package/dist/brain/status-sink.d.ts +18 -0
  29. package/dist/brain/status-sink.js +7 -0
  30. package/dist/core/allow-entry.d.ts +42 -8
  31. package/dist/core/allow-entry.js +24 -17
  32. package/dist/core/auto-compaction.d.ts +23 -20
  33. package/dist/core/auto-compaction.js +37 -16
  34. package/dist/core/auto-promote.d.ts +2 -1
  35. package/dist/core/auto-promote.js +2 -2
  36. package/dist/core/caller-tool-name.d.ts +9 -0
  37. package/dist/core/caller-tool-name.js +13 -0
  38. package/dist/core/checkpoint-requirements.d.ts +7 -1
  39. package/dist/core/checkpoint-requirements.js +2 -0
  40. package/dist/core/checkpoint-store.d.ts +36 -12
  41. package/dist/core/checkpoint-store.js +1 -1
  42. package/dist/core/child-clamp-seats.d.ts +6 -0
  43. package/dist/core/child-clamp-seats.js +2 -1
  44. package/dist/core/credential-vocabulary.d.ts +33 -8
  45. package/dist/core/credential-vocabulary.js +32 -7
  46. package/dist/core/decision-receipt.d.ts +2 -2
  47. package/dist/core/delegation-frames.d.ts +9 -2
  48. package/dist/core/delegation-name.d.ts +18 -0
  49. package/dist/core/delegation-name.js +47 -0
  50. package/dist/core/engine-capabilities.d.ts +8 -1
  51. package/dist/core/engine-capabilities.js +1 -1
  52. package/dist/core/engine-notice.d.ts +36 -19
  53. package/dist/core/frontmatter-fence.d.ts +10 -0
  54. package/dist/core/frontmatter-fence.js +6 -0
  55. package/dist/core/gate-fold.js +5 -23
  56. package/dist/core/gate-lanes.js +356 -270
  57. package/dist/core/governance-codes.d.ts +9 -4
  58. package/dist/core/governance-codes.js +9 -3
  59. package/dist/core/hooks.d.ts +276 -734
  60. package/dist/core/hooks.js +97 -192
  61. package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
  62. package/dist/{tools/fs → core}/image-admission.js +24 -6
  63. package/dist/core/locked-config.d.ts +26 -5
  64. package/dist/core/locked-config.js +31 -6
  65. package/dist/core/mcp-connection-pool.d.ts +4 -1
  66. package/dist/core/mcp-connection-pool.js +7 -2
  67. package/dist/core/mcp-probe.d.ts +12 -9
  68. package/dist/core/mcp-probe.js +2 -2
  69. package/dist/core/mcp-server-spec.d.ts +53 -7
  70. package/dist/core/mcp-server-spec.js +84 -1
  71. package/dist/core/mcp.d.ts +11 -7
  72. package/dist/core/mcp.js +27 -9
  73. package/dist/core/memory-engine/data-plane.js +5 -1
  74. package/dist/core/memory-engine/distiller.js +16 -2
  75. package/dist/core/memory-engine/engine.d.ts +8 -11
  76. package/dist/core/memory-engine/engine.js +9 -5
  77. package/dist/core/memory-engine/frontmatter.d.ts +6 -0
  78. package/dist/core/memory-engine/frontmatter.js +19 -16
  79. package/dist/core/memory.d.ts +2 -2
  80. package/dist/core/mode-policy.d.ts +4 -0
  81. package/dist/core/mode-policy.js +7 -5
  82. package/dist/core/model-seat.d.ts +4 -1
  83. package/dist/core/mods/dollar.d.ts +109 -0
  84. package/dist/core/mods/dollar.js +154 -0
  85. package/dist/core/mods/host.d.ts +23 -0
  86. package/dist/core/mods/host.js +21 -0
  87. package/dist/core/mods/kernel/budget.d.ts +43 -0
  88. package/dist/core/mods/kernel/budget.js +83 -0
  89. package/dist/core/mods/kernel/dispatch.d.ts +100 -0
  90. package/dist/core/mods/kernel/dispatch.js +701 -0
  91. package/dist/core/mods/kernel/events.d.ts +242 -0
  92. package/dist/core/mods/kernel/events.js +210 -0
  93. package/dist/core/mods/kernel/index.d.ts +6 -0
  94. package/dist/core/mods/kernel/index.js +5 -0
  95. package/dist/core/mods/kernel/receive.d.ts +65 -0
  96. package/dist/core/mods/kernel/receive.js +123 -0
  97. package/dist/core/mods/kernel/registration.d.ts +131 -0
  98. package/dist/core/mods/kernel/registration.js +102 -0
  99. package/dist/core/mods/kernel/site-rules.d.ts +84 -0
  100. package/dist/core/mods/kernel/site-rules.js +104 -0
  101. package/dist/core/mods/stations.d.ts +350 -0
  102. package/dist/core/mods/stations.js +444 -0
  103. package/dist/core/own-property-receipt.js +11 -23
  104. package/dist/core/parent-spec-seat.d.ts +18 -7
  105. package/dist/core/parent-spec-seat.js +6 -2
  106. package/dist/core/path-identity.js +2 -1
  107. package/dist/core/permission-rule-syntax.d.ts +15 -0
  108. package/dist/core/permission-rule-syntax.js +24 -16
  109. package/dist/core/permission-rules.d.ts +7 -1
  110. package/dist/core/permission-rules.js +26 -15
  111. package/dist/core/prompt-section-seat.d.ts +15 -27
  112. package/dist/core/prompt-section-seat.js +73 -40
  113. package/dist/core/protocol-http-auth.d.ts +3 -1
  114. package/dist/core/protocol-http-auth.js +2 -0
  115. package/dist/core/reminder-mint.d.ts +2 -2
  116. package/dist/core/remote-env.d.ts +24 -12
  117. package/dist/core/remote-env.js +1 -0
  118. package/dist/core/resume-startup-cause.d.ts +16 -1
  119. package/dist/core/resume-startup-cause.js +4 -1
  120. package/dist/core/retired-keys.d.ts +4 -0
  121. package/dist/core/retired-keys.js +3 -0
  122. package/dist/core/runner/announce-once-ledger.d.ts +31 -1
  123. package/dist/core/runner/announce-once-ledger.js +22 -0
  124. package/dist/core/runner/assemble-result.d.ts +9 -0
  125. package/dist/core/runner/assemble-result.js +3 -1
  126. package/dist/core/runner/call-hook-stations.d.ts +23 -27
  127. package/dist/core/runner/call-hook-stations.js +58 -42
  128. package/dist/core/runner/caller-stop-gate.d.ts +25 -0
  129. package/dist/core/runner/caller-stop-gate.js +7 -0
  130. package/dist/core/runner/compaction-seams.d.ts +8 -22
  131. package/dist/core/runner/compaction-seams.js +28 -53
  132. package/dist/core/runner/construction-doors.js +11 -1
  133. package/dist/core/runner/contracts.d.ts +85 -83
  134. package/dist/core/runner/credential-origin-seat.d.ts +2 -0
  135. package/dist/core/runner/credential-origin-seat.js +19 -0
  136. package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
  137. package/dist/core/runner/gate-exit.d.ts +47 -36
  138. package/dist/core/runner/gate-exit.js +8 -2
  139. package/dist/core/runner/image.js +1 -2
  140. package/dist/core/runner/inline-image.js +1 -1
  141. package/dist/core/runner/notice-seat-input.d.ts +11 -0
  142. package/dist/core/runner/notice-seat-input.js +1 -0
  143. package/dist/core/runner/person-path-rows.js +1 -0
  144. package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
  145. package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
  146. package/dist/core/runner/prepare-ask-lane.js +6 -15
  147. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  148. package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
  149. package/dist/core/runner/prepare-config-doors.d.ts +5 -0
  150. package/dist/core/runner/prepare-config-doors.js +9 -0
  151. package/dist/core/runner/prepare-defer-classify.js +3 -4
  152. package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
  153. package/dist/core/runner/prepare-gate-stations.js +11 -10
  154. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
  155. package/dist/core/runner/prepare-inherited-gate.js +8 -21
  156. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  157. package/dist/core/runner/prepare-park-ask.js +10 -14
  158. package/dist/core/runner/prepare-policy-chain.d.ts +18 -16
  159. package/dist/core/runner/prepare-policy-chain.js +98 -101
  160. package/dist/core/runner/prepare-project-context.js +7 -10
  161. package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
  162. package/dist/core/runner/prepare-prompt-assembly.js +134 -101
  163. package/dist/core/runner/prepare-prompt-inputs.d.ts +18 -19
  164. package/dist/core/runner/prepare-prompt-inputs.js +5 -7
  165. package/dist/core/runner/prepare-protocol-tools.d.ts +6 -4
  166. package/dist/core/runner/prepare-protocol-tools.js +2 -2
  167. package/dist/core/runner/prepare-safety-scan.js +6 -10
  168. package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
  169. package/dist/core/runner/prepare-suspend-saga.js +7 -5
  170. package/dist/core/runner/prepare-task.js +33 -15
  171. package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
  172. package/dist/core/runner/prepare-turn-wiring.js +29 -27
  173. package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -6
  174. package/dist/core/runner/prepare-wiring-manifest.js +7 -7
  175. package/dist/core/runner/resume-apply.d.ts +5 -2
  176. package/dist/core/runner/resume-apply.js +24 -42
  177. package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
  178. package/dist/core/runner/resume-checkpoint-screen.js +9 -1
  179. package/dist/core/runner/resume-preflight.js +38 -16
  180. package/dist/core/runner/retired-runner-deps.js +3 -1
  181. package/dist/core/runner/run-brain-sinks.d.ts +3 -2
  182. package/dist/core/runner/run-brain-sinks.js +5 -1
  183. package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
  184. package/dist/core/runner/run-compaction-machinery.js +7 -33
  185. package/dist/core/runner/run-harness-handlers.js +4 -2
  186. package/dist/core/runner/run-identity-wiring.d.ts +3 -1
  187. package/dist/core/runner/run-identity-wiring.js +3 -2
  188. package/dist/core/runner/run-leg.js +62 -84
  189. package/dist/core/runner/run-notification-lane.js +1 -0
  190. package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
  191. package/dist/core/runner/run-recovery-lanes.js +11 -10
  192. package/dist/core/runner/run-settle-and-teardown.js +9 -28
  193. package/dist/core/runner/run-stop-and-final-verify.js +18 -25
  194. package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
  195. package/dist/core/runner/run-terminal-adoption.js +33 -22
  196. package/dist/core/runner/run-tool-mount-facts.js +5 -3
  197. package/dist/core/runner/run-turn-boundary.js +23 -34
  198. package/dist/core/runner/runtask.d.ts +6 -7
  199. package/dist/core/runner/runtask.js +14 -72
  200. package/dist/core/runner/session-rule-policy.d.ts +6 -4
  201. package/dist/core/runner/session-rule-policy.js +17 -10
  202. package/dist/core/runner/side-call-account.d.ts +68 -0
  203. package/dist/core/runner/side-call-account.js +61 -0
  204. package/dist/core/runner/stream-halt-verbs.js +12 -17
  205. package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
  206. package/dist/core/runner/stream-steer-verb.d.ts +1 -1
  207. package/dist/core/runner/stream-steer-verb.js +20 -38
  208. package/dist/core/runner/sub-agent-deny-lists.d.ts +121 -22
  209. package/dist/core/runner/sub-agent-deny-lists.js +228 -71
  210. package/dist/core/runner/tool-end-body.d.ts +5 -6
  211. package/dist/core/runner/tool-end-body.js +6 -4
  212. package/dist/core/runner/tool-not-executed.d.ts +15 -0
  213. package/dist/core/runner/tool-not-executed.js +30 -0
  214. package/dist/core/runner/tool-occurrence.d.ts +8 -7
  215. package/dist/core/runner/tool-occurrence.js +5 -5
  216. package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
  217. package/dist/core/runner-deps.d.ts +20 -28
  218. package/dist/core/safety-merge-corpus.js +18 -2
  219. package/dist/core/secret-env.d.ts +93 -20
  220. package/dist/core/secret-env.js +192 -38
  221. package/dist/core/session-policy-store.d.ts +93 -17
  222. package/dist/core/session-policy-store.js +178 -25
  223. package/dist/core/session-reconcile.d.ts +4 -0
  224. package/dist/core/session-reconcile.js +2 -1
  225. package/dist/core/shell-read-silence.js +3 -32
  226. package/dist/core/side-query.d.ts +5 -3
  227. package/dist/core/skill-grant.d.ts +3 -2
  228. package/dist/core/skill-grant.js +17 -9
  229. package/dist/core/skills-directory.d.ts +31 -11
  230. package/dist/core/skills-directory.js +655 -104
  231. package/dist/core/task-event.d.ts +13 -3
  232. package/dist/core/task-result.d.ts +8 -0
  233. package/dist/core/task-spec.d.ts +20 -19
  234. package/dist/core/task-stream.d.ts +14 -4
  235. package/dist/core/tighten-task-spec.d.ts +4 -3
  236. package/dist/core/tighten-task-spec.js +2 -4
  237. package/dist/core/tool-check-reading.d.ts +74 -0
  238. package/dist/core/tool-check-reading.js +80 -0
  239. package/dist/core/tool-policy.d.ts +59 -18
  240. package/dist/core/tool-policy.js +77 -12
  241. package/dist/core/tool-registry.d.ts +20 -0
  242. package/dist/core/tool-registry.js +4 -0
  243. package/dist/core/tool-spec.d.ts +10 -6
  244. package/dist/core/tools.js +2 -0
  245. package/dist/core/trace.d.ts +51 -32
  246. package/dist/core/types.d.ts +2 -1
  247. package/dist/core/types.js +1 -0
  248. package/dist/core/unc-spelling.d.ts +10 -0
  249. package/dist/core/unc-spelling.js +3 -0
  250. package/dist/core/untrusted-egress.d.ts +8 -0
  251. package/dist/core/untrusted-egress.js +4 -1
  252. package/dist/core/user-attachment.d.ts +12 -2
  253. package/dist/core/user-attachment.js +17 -2
  254. package/dist/core/wiring-manifest.d.ts +14 -13
  255. package/dist/core/wiring-manifest.js +2 -2
  256. package/dist/engine/compaction/utils.js +1 -2
  257. package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
  258. package/dist/engine/execution-env/symlink-chain.js +1 -1
  259. package/dist/engine/harness/agent-harness.d.ts +1 -1
  260. package/dist/engine/harness/agent-harness.js +3 -3
  261. package/dist/engine/harness/types.d.ts +17 -16
  262. package/dist/engine/harness/types.js +7 -1
  263. package/dist/engine/llm/types.d.ts +12 -7
  264. package/dist/engine/loop/agent-loop.js +1 -1
  265. package/dist/engine/loop/types.d.ts +1 -1
  266. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  267. package/dist/engine/session/import-validate.js +3 -10
  268. package/dist/engine/session/session.d.ts +4 -4
  269. package/dist/engine/session/session.js +4 -4
  270. package/dist/index.d.ts +24 -17
  271. package/dist/index.js +15 -12
  272. package/dist/internal/harness.d.ts +1 -1
  273. package/dist/internal/harness.js +1 -1
  274. package/dist/orchestration/run-spec.d.ts +4 -6
  275. package/dist/orchestration/run-spec.js +39 -63
  276. package/dist/orchestration/run-workflow-tool.js +1 -2
  277. package/dist/orchestration/workflow.d.ts +11 -3
  278. package/dist/orchestration/workflow.js +11 -2
  279. package/dist/prompt-assembly/artifact-store.d.ts +37 -56
  280. package/dist/prompt-assembly/artifact-store.js +31 -49
  281. package/dist/prompt-assembly/artifact.d.ts +89 -76
  282. package/dist/prompt-assembly/artifact.js +167 -146
  283. package/dist/prompt-assembly/assemble.d.ts +34 -29
  284. package/dist/prompt-assembly/assemble.js +33 -220
  285. package/dist/prompt-assembly/bands.d.ts +20 -0
  286. package/dist/prompt-assembly/bands.js +18 -0
  287. package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
  288. package/dist/prompt-assembly/catalog/constitution.js +69 -0
  289. package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
  290. package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
  291. package/dist/prompt-assembly/catalog/index.d.ts +11 -0
  292. package/dist/prompt-assembly/catalog/index.js +59 -0
  293. package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
  294. package/dist/prompt-assembly/catalog/modes.js +137 -0
  295. package/dist/prompt-assembly/catalog/role.d.ts +10 -0
  296. package/dist/prompt-assembly/catalog/role.js +157 -0
  297. package/dist/prompt-assembly/catalog/schema.d.ts +124 -0
  298. package/dist/prompt-assembly/catalog/schema.js +25 -0
  299. package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
  300. package/dist/prompt-assembly/catalog/sections.js +98 -0
  301. package/dist/prompt-assembly/catalog/team.d.ts +4 -0
  302. package/dist/prompt-assembly/catalog/team.js +258 -0
  303. package/dist/prompt-assembly/composer.d.ts +24 -28
  304. package/dist/prompt-assembly/composer.js +124 -46
  305. package/dist/prompt-assembly/epoch.d.ts +89 -79
  306. package/dist/prompt-assembly/epoch.js +115 -142
  307. package/dist/prompt-assembly/explain.d.ts +58 -34
  308. package/dist/prompt-assembly/explain.js +21 -24
  309. package/dist/prompt-assembly/overlay.d.ts +127 -0
  310. package/dist/prompt-assembly/overlay.js +301 -0
  311. package/dist/prompt-assembly/renderers.d.ts +21 -0
  312. package/dist/prompt-assembly/renderers.js +39 -0
  313. package/dist/prompt-assembly/section-usage.d.ts +3 -3
  314. package/dist/prompt-assembly/section-usage.js +1 -1
  315. package/dist/prompt-assembly/select.d.ts +56 -0
  316. package/dist/prompt-assembly/select.js +69 -0
  317. package/dist/prompt-assembly/sugar.d.ts +31 -0
  318. package/dist/prompt-assembly/sugar.js +20 -0
  319. package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
  320. package/dist/prompt-assembly/types.d.ts +45 -148
  321. package/dist/prompts/coordinator.d.ts +2 -21
  322. package/dist/prompts/coordinator.js +3 -255
  323. package/dist/prompts/cowork.d.ts +27 -16
  324. package/dist/prompts/cowork.js +2 -23
  325. package/dist/prompts/default.d.ts +38 -500
  326. package/dist/prompts/default.js +20 -285
  327. package/dist/prompts/supervisor.d.ts +6 -71
  328. package/dist/prompts/supervisor.js +6 -117
  329. package/dist/stores/file/session-policy-store.d.ts +3 -3
  330. package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
  331. package/dist/tools/fs/dir-entries.d.ts +4 -2
  332. package/dist/tools/fs/dir-entries.js +16 -4
  333. package/dist/tools/fs/fs-bash.d.ts +1 -1
  334. package/dist/tools/fs/fs-bash.js +22 -11
  335. package/dist/tools/fs/fs-pdf.js +17 -13
  336. package/dist/tools/fs/fs-read.js +4 -3
  337. package/dist/tools/fs/fs-shared.d.ts +1 -10
  338. package/dist/tools/fs/fs-shared.js +3 -4
  339. package/dist/tools/fs/fs-write.js +2 -1
  340. package/dist/tools/fs/notebook.js +1 -1
  341. package/dist/tools/fs/pdf.d.ts +20 -10
  342. package/dist/tools/fs/pdf.js +19 -10
  343. package/dist/tools/fs/program-read-model.d.ts +68 -8
  344. package/dist/tools/fs/program-read-model.js +112 -33
  345. package/dist/tools/fs/program-read-model.rows.js +30 -10
  346. package/dist/tools/fs/read-deny-home.d.ts +25 -9
  347. package/dist/tools/fs/read-deny-home.js +28 -27
  348. package/dist/tools/fs/read-deny.d.ts +41 -11
  349. package/dist/tools/fs/read-deny.js +51 -5
  350. package/dist/tools/fs/safety.d.ts +4 -26
  351. package/dist/tools/fs/safety.js +10 -32
  352. package/dist/tools/fs/search.js +15 -10
  353. package/dist/tools/fs/shell-read-argv.d.ts +3 -2
  354. package/dist/tools/fs/shell-read-argv.js +118 -38
  355. package/dist/tools/fs/shell-read-establish.d.ts +5 -5
  356. package/dist/tools/fs/shell-read-establish.js +11 -61
  357. package/dist/tools/fs/shell-read-identity.js +6 -3
  358. package/dist/tools/fs/shell-read-judge.d.ts +13 -15
  359. package/dist/tools/fs/shell-read-judge.js +18 -4
  360. package/dist/tools/fs/shell-read-plan.d.ts +2 -2
  361. package/dist/tools/web.js +1 -1
  362. package/package.json +13 -1
  363. package/test/export-surface.snapshot.json +256 -99
  364. package/wire-changes/core@7.51.1.json +5 -5
  365. package/wire-changes/core@7.51.2.json +293 -0
  366. package/wire-changes/core@7.52.0.json +5859 -0
  367. package/dist/core/hook-seats.d.ts +0 -66
  368. package/dist/core/hook-seats.js +0 -37
  369. package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
  370. package/dist/prompt-assembly/packs/sema-default.js +0 -147
  371. package/dist/prompts/simple-sections.d.ts +0 -58
  372. package/dist/prompts/simple-sections.js +0 -43
@@ -18,10 +18,15 @@
18
18
  * (`readRuleEntry`, allow-entry.ts): a tool name, a protocol namespace, a shell command rule (`Bash(git push:*)`), a
19
19
  * path rule, a name or host rule. Entries are stored as written; every station that validates, compares, matches or
20
20
  * names one reads that reader's answer ({@link judgeSessionRulesWrite}, {@link loosenReasons}, the run-time layer).
21
+ * - **a row keeps its grammar**: the write judge stamps the grammar it read the entries in ({@link SESSION_RULES_GRAMMAR}), and
22
+ * every station reads a row's entries in the grammar the row carries ({@link readSessionRulesShape}) — a row stored before
23
+ * the stamp keeps the reading it was written under, so an upgrade never turns a stored entry into a grant.
24
+ * - **rules are read as rules or not at all**: rules whose shape cannot be read, or whose toolDeny holds an entry no grammar
25
+ * reads ({@link unreadableDenyEntries}), refuse every call until an operator writes them afresh — never read as fewer.
21
26
  * - **NOT A SANDBOX**: a shell command rule reads the command line the call carries (every program run in its connector
22
27
  * chain); the real isolation boundary is the executionEnv, never these rules.
23
28
  */
24
- import { type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
29
+ import { compileAllowList, type AllowEntryInert, type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
25
30
  /** The per-session rules. All optional; an absent bucket imposes no constraint of that kind. */
26
31
  export interface SessionPermissionRules {
27
32
  /** If set, ONLY the calls these entries admit are allowed — a narrowing allowlist: a call no entry admits is refused.
@@ -45,6 +50,19 @@ declare const SESSION_RULE_BUCKETS: {
45
50
  export type SessionRuleEntryField = {
46
51
  [K in keyof typeof SESSION_RULE_BUCKETS]: (typeof SESSION_RULE_BUCKETS)[K] extends "dirs" ? never : K;
47
52
  }[keyof typeof SESSION_RULE_BUCKETS];
53
+ /**
54
+ * The rule grammar this engine reads a session's rule entries in: the `grammar` the write judge stamps on every rules value it
55
+ * judges ({@link judgeSessionRulesWrite}), so every store that writes through it keeps the stamp on the row. It counts
56
+ * revisions of what an entry MEANS and moves only when that changes:
57
+ * - 0, the reading before the one entry reader: a bucket's entries were tool names compared as written, so an allow entry the
58
+ * reader now reads as speaking for another name (`Bash(rm:*)`, `Bash(*)`) named no tool and admitted nothing — and so did
59
+ * `*`, one more name no tool carried, which voided nothing beside it;
60
+ * - 1, the one entry reader taking its tool name from the lenient rule parser (trimmed before a `(`, any character but a
61
+ * blank, a parenthesis or a control / format character);
62
+ * - 2, the rule syntax's tool-name head (`ruleSpellingOf`): a name is read as written, in the characters names are minted in.
63
+ * An entry this grammar reads as no rule in a row an earlier grammar wrote reads as `earlier_grammar` ({@link compileSessionRuleBucket}).
64
+ */
65
+ export declare const SESSION_RULES_GRAMMAR = 2;
48
66
  /** {@link SessionPermissionRules} plus the monotonic `rev` the store stamps (the OCC key). */
49
67
  export interface StoredSessionRules extends SessionPermissionRules {
50
68
  rev: number;
@@ -53,7 +71,15 @@ export interface StoredSessionRules extends SessionPermissionRules {
53
71
  * can tell a recreated row from a continuation of the one it enforces whatever `rev` the new row wears.
54
72
  * Absent = generation 0 (a backend that predates the mark, or a row written before it). */
55
73
  gen?: number;
74
+ /** The rule grammar the row's entries are read in ({@link SESSION_RULES_GRAMMAR}), stamped by the write judge. Absent =
75
+ * a row stored before the stamp: grammar 0. */
76
+ grammar?: number;
56
77
  }
78
+ /** A rules value as the shape reading hands it on ({@link readSessionRulesShape}): its buckets, and the grammar its entries
79
+ * are read in — resolved, so a store that serves it on beside its own counters serves the row's grammar with it. */
80
+ export type SessionRulesInGrammar = SessionPermissionRules & {
81
+ grammar: number;
82
+ };
57
83
  /** Options for {@link SessionPolicyStore.putRules}. */
58
84
  export interface PutRulesOptions {
59
85
  /** CAS: reject the write if the stored `rev` is not this (optimistic concurrency). Omit to skip the check. */
@@ -92,7 +118,8 @@ export declare class SessionPolicyError extends Error {
92
118
  export interface SessionPolicyStoreOptions {
93
119
  /** The names this deployment mounts its OWN tools under — the roster the store reads a rule entry's tool name against
94
120
  * (`readRuleEntry`): a retired name declared here is an ordinary name to the write and to the loosening check alike.
95
- * Absent = none. A name carrying `__` is refused, as it is for a deployment tool. */
121
+ * Absent = none. Each name keeps the caller tool name contract a deployment tool's name keeps at the mount
122
+ * (`callerToolNameProblem`: a name a rule can name, no `__`); one that does not is refused at construction. */
96
123
  mountedToolNames?: Iterable<string>;
97
124
  }
98
125
  /** A store's declared roster from {@link SessionPolicyStoreOptions.mountedToolNames} — the one reader both bundled stores
@@ -169,13 +196,14 @@ declare function lexWithin(root: string, p: string): boolean;
169
196
  * THE ONE SHAPE READING of a rules value — what every station that takes one reads it through: the write (incoming rules),
170
197
  * a store's read-back of its own rows, the tighten comparison's prior, the run-time layer's served row. A rules value is an
171
198
  * object whose keys are buckets ({@link SessionPermissionRules}); each bucket present is a list of strings (`allowDirs`: of
172
- * non-blank strings — a directory is no blank). A bucket's TYPE is all this reads: what an entry means is the entry reader's.
173
- * The store's own stamps (`rev`, `gen`) are passed over, and a key holding `undefined` holds nothing. Anything else is a fault,
174
- * named by key and by TYPE, never by value — a bare string is never one rule and never its characters (`"rm"` spread into
175
- * `["r","m"]`), and a key outside the buckets is never dropped (a dropped deny is a deny nobody enforces).
199
+ * non-blank strings — a directory is no blank). A bucket's TYPE is all this reads: what an entry means is the entry reader's,
200
+ * in the grammar the value is written in ({@link grammarOf}), handed on as `grammar`. The store's counters (`rev`, `gen`) are
201
+ * passed over, and a key holding `undefined` holds nothing. Anything else is a fault, named by key and by TYPE, never by value —
202
+ * a bare string is never one rule and never its characters (`"rm"` spread into `["r","m"]`), and a key outside the buckets is
203
+ * never dropped (a dropped deny is a deny nobody enforces).
176
204
  */
177
205
  export declare function readSessionRulesShape(value: unknown): {
178
- rules: SessionPermissionRules;
206
+ rules: SessionRulesInGrammar;
179
207
  } | {
180
208
  fault: SessionRuleShapeRefusal;
181
209
  sentence: string;
@@ -185,25 +213,73 @@ export declare function readSessionRulesShape(value: unknown): {
185
213
  * stored form is `..`-free and the tighten diff compares the form the runtime resolves. Both bundled stores and a durable twin
186
214
  * stamp exactly this, so a structuredClone and a JSON round-trip of it are byte-identical. */
187
215
  export declare function normalizeRules(rules: unknown): SessionPermissionRules;
216
+ /**
217
+ * THE ONE ANSWER to "can these rules' denies be read?": the `toolDeny` entries the list reading ({@link compileSessionRuleBucket},
218
+ * in the rules' own grammar) reads as no rule at all. Rules whose deny bucket holds one cannot be read, and rules that cannot be
219
+ * read are never read as fewer: they refuse every call (the run-time layer) and a non-operator write over them is a loosening
220
+ * ({@link loosenReasons}) — until an operator writes them afresh, and the write judge refuses such an entry. Unlike a definition
221
+ * file a plugin ships, which its user cannot edit, a session's rules are written by the session's own operator and can be
222
+ * rewritten, so a deny nobody can read costs one write instead of quietly guarding nothing. An entry the grammar reads that names
223
+ * no mounted tool (or `*`) is not here: it names nothing and refuses nothing, the rest of the bucket refusing as written.
224
+ * `roster` as in {@link compileSessionRuleBucket}; it never changes this answer (a spelling is read before a name is judged).
225
+ */
226
+ export declare function unreadableDenyEntries(rules: SessionRulesInGrammar, roster?: ReadonlySet<string>): AllowEntryInert[];
227
+ /**
228
+ * THE LIST READING of one rule bucket of a rules value the shape reading read — what the run-time layer enforces, the tighten
229
+ * comparison compares and the write judge refuses on: `compileAllowList` over the one entry reader, in the bucket's direction,
230
+ * against `roster` when given, in the grammar the value is written in. An entry its grammar reads as nothing
231
+ * ({@link earlierGrammarReads}) is moved to the entries that read as nothing, reason `earlier_grammar`, before the list is
232
+ * compiled: it is one entry that reads as nothing, and the list beside it is read as written. The move is an ENTRY's reading and
233
+ * never the list's: an allow list a `*` voids (in any spelling the voiding test reads as `*`, `* (*)` included) is voided in
234
+ * every grammar from 1 on, so in such a row the list is read whole, as written — only grammar 0, where `*` was one more name, takes
235
+ * it out alone.
236
+ */
237
+ export declare function compileSessionRuleBucket(rules: SessionRulesInGrammar, field: SessionRuleEntryField, roster?: ReadonlySet<string>): ReturnType<typeof compileAllowList>;
238
+ /**
239
+ * The advisory for the entries of one bucket a stored row's earlier grammar reads as nothing ({@link compileSessionRuleBucket},
240
+ * `earlier_grammar`; `grammar` = the row's): the session, the bucket, the entries as written, what they do and the way out. An
241
+ * entry this engine's grammar reads as no rule is named with the reader's word — in `toolAllow` it admits nothing, in `toolDeny`
242
+ * the rules cannot be read and every call is refused ({@link unreadableDenyEntries}); the way out is a rewrite leaving it out
243
+ * (the write judge refuses an entry it cannot read). A grammar-0 allow entry that named no tool there is told as it always was:
244
+ * to make a parenthesized one the rule it now spells, or (`*`, which is no allow rule in any grammar) to leave it out.
245
+ */
246
+ export declare function earlierGrammarAdvisory(sessionId: string, entries: readonly string[], field: SessionRuleEntryField, grammar: number): string;
247
+ /** The clause a run-time refusal carries when the allowlist it judged by holds entries its earlier grammar reads as nothing
248
+ * ({@link compileSessionRuleBucket}; `grammar` = the row's): the grammar the rules are stored in, then which entries name no tool
249
+ * there (grammar 0: `*` by its spelling, the parenthesized ones counted) and how many this engine's grammar reads as no rule —
250
+ * counted, since a stored entry's own text is data and never reaches a sentence the model reads. Empty when none. */
251
+ export declare function earlierGrammarRefusalClause(entries: readonly string[], grammar: number): string;
252
+ /** A layer's delivery of the entries its snapshots' earlier grammar reads as nothing ({@link compileSessionRuleBucket},
253
+ * `earlier_grammar`), one bucket at a time with the grammar its row is written in, to `sink`: once per distinct set per layer
254
+ * (the ledger is this closure's), an empty set is no news, and a throwing sink never changes what the snapshot enforces. */
255
+ export declare function earlierGrammarAnnouncer(sink: ((entries: readonly string[], field: SessionRuleEntryField, grammar: number) => void) | undefined): (entries: readonly string[], field: SessionRuleEntryField, grammar: number) => void;
188
256
  /**
189
257
  * THE ONE WRITE JUDGE every store applies before it stamps a row (both bundled stores, and a durable backend at the same point):
190
258
  * the rules to stamp, or a {@link SessionPolicyError}, in this order — `conflict` (a stale `expectedRev` against `prior`'s rev),
191
- * `invalid_shape` ({@link normalizeRules}), `inert_entry` (an entry the reader reads as nothing; operator writes included — a
192
- * rule that names nothing guards nothing whoever writes it), then, for a non-operator write over an existing row,
193
- * `loosen_forbidden` ({@link loosenReasons}) — or `corrupt` when that prior cannot be read as rules, which the comparison never
194
- * reads as fewer. `mountedToolNames` = the store's declaration ({@link SessionPolicyStoreOptions.mountedToolNames}).
259
+ * `invalid_shape` ({@link normalizeRules}), `inert_entry` (an entry that reads as nothing in the grammar the rules are written
260
+ * in; operator writes included — a rule that names nothing guards nothing whoever writes it), then, for a non-operator write
261
+ * over an existing row, `loosen_forbidden` ({@link loosenReasons}) — or `corrupt` when that prior cannot be read as rules,
262
+ * which the comparison never reads as fewer. The rules to stamp carry `grammar` = {@link SESSION_RULES_GRAMMAR}: what passed
263
+ * the judge means the same in this engine's grammar, and THIS is the one place the stamp is minted, so every store that
264
+ * stores what the judge returns keeps it. `mountedToolNames` = the store's declaration ({@link SessionPolicyStoreOptions.mountedToolNames}).
195
265
  */
196
- export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null, rules: unknown, opts: PutRulesOptions | undefined, mountedToolNames?: ReadonlySet<string>): SessionPermissionRules;
266
+ export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null, rules: unknown, opts: PutRulesOptions | undefined, mountedToolNames?: ReadonlySet<string>): SessionPermissionRules & {
267
+ grammar: number;
268
+ };
197
269
  /**
198
270
  * Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op), over the EFFECTIVE
199
- * entries of each rule bucket — the list as the run-time layer enforces it ({@link effectiveEntries}). An entry that reads as
271
+ * entries of each rule bucket — the list as the run-time layer enforces it ({@link effectiveEntries}). Rules whose toolDeny holds
272
+ * an entry no grammar reads ({@link unreadableDenyEntries}) refuse every call: as `next` they loosen nothing, and as `prior`
273
+ * every readable `next` loosens them (one reason, naming the entries). Any other entry that reads as
200
274
  * nothing is in neither side's effect, so removing one relaxes nothing and adding one widens nothing (the way a session's owner
201
- * takes out an entry that names nothing); an allowlist a `*` voids admits nothing, so dropping the `*` widens. toolDeny: removing an effective entry loosens. toolAllow: removing the list
275
+ * takes out an entry that names nothing); an allowlist a `*` voids (in this engine's grammar; in grammar 0 a `*` is one such entry beside the rest) admits nothing, so dropping the `*` widens. toolDeny: removing an effective entry loosens. toolAllow: removing the list
202
276
  * (→ everything allowed) or adding an effective entry outside the prior set loosens; prior-undefined → any defined next only
203
277
  * narrows. allowDirs: removing confinement, or adding a dir not within some prior dir, loosens (coarse raw containment).
204
- * Entries compare as written. Both sides are read through {@link readSessionRulesShape} first: a side it cannot read (a key that
205
- * is no bucket — a retired one included) throws `invalid_shape` rather than compare without it. `mountedToolNames` = the store's
206
- * declaration ({@link SessionPolicyStoreOptions.mountedToolNames}; empty when it has none) — the write judge's too.
278
+ * Entries compare as written, each side's in the grammar that side is written in (a stored row's entry that its grammar reads as
279
+ * nothing is in no effect, so writing it in this engine's grammar widens). Both sides are read through
280
+ * {@link readSessionRulesShape} first: a side it cannot read (a key that is no bucket — a retired one included) throws
281
+ * `invalid_shape` rather than compare without it. `mountedToolNames` = the store's declaration
282
+ * ({@link SessionPolicyStoreOptions.mountedToolNames}; empty when it has none) — the write judge's too.
207
283
  */
208
284
  export declare function loosenReasons(priorValue: SessionPermissionRules, nextValue: SessionPermissionRules, mountedToolNames?: ReadonlySet<string>): string[];
209
285
  /**
@@ -1,12 +1,14 @@
1
1
  import { posix } from "node:path";
2
2
  import { isWinFormPath } from "../tools/fs/safety.js";
3
- import { NAMESPACED_NAME_SHAPES, carriesNamespaceSeparator } from "./protocol-table.js";
4
- import { compileAllowList, inertEntryPhrase, readRuleEntry, renderListEntry } from "./allow-entry.js";
3
+ import { NAMESPACED_NAME_SHAPES } from "./protocol-table.js";
4
+ import { callerToolNameProblem } from "./caller-tool-name.js";
5
+ import { compileAllowList, compiledEntryName, inertEntryPhrase, readRuleEntry, renderListEntry, rootFacingToolNames } from "./allow-entry.js";
5
6
  import { valueTypeLabel } from "./tool-name-list.js";
6
7
  const SESSION_RULE_BUCKETS = { toolAllow: "allow", toolDeny: "deny", allowDirs: "dirs" };
7
8
  const BUCKETS = Object.keys(SESSION_RULE_BUCKETS);
8
9
  const ENTRY_FIELDS = BUCKETS.filter((f) => SESSION_RULE_BUCKETS[f] !== "dirs");
9
- const STORE_STAMPS = new Set(["rev", "gen"]);
10
+ const STORE_STAMPS = new Set(["rev", "gen", "grammar"]);
11
+ export const SESSION_RULES_GRAMMAR = 2;
10
12
  export class SessionPolicyError extends Error {
11
13
  code;
12
14
  detail;
@@ -28,10 +30,11 @@ export function readMountedToolNames(value) {
28
30
  refuse("must be an iterable of tool names (an array or a Set), not a single value");
29
31
  const names = new Set();
30
32
  for (const n of value) {
31
- if (typeof n !== "string" || n.length === 0)
32
- refuse(`holds ${JSON.stringify(n) ?? String(n)}, which is not a non-empty tool name`);
33
- if (carriesNamespaceSeparator(n))
34
- refuse(`holds ${JSON.stringify(n)}: "__" is reserved for protocol tool names and no deployment tool may carry it`);
33
+ if (typeof n !== "string")
34
+ refuse(`holds ${JSON.stringify(n) ?? String(n)}, which is not a tool name`);
35
+ const problem = callerToolNameProblem(n);
36
+ if (problem !== undefined)
37
+ refuse(`holds ${JSON.stringify(n)}: ${problem}`);
35
38
  names.add(n);
36
39
  }
37
40
  return names;
@@ -49,13 +52,32 @@ function lexWithin(root, p) {
49
52
  return true;
50
53
  return np === nr || np.startsWith(nr + "/");
51
54
  }
55
+ function grammarOf(value) {
56
+ const stamp = value.grammar;
57
+ if (stamp === undefined)
58
+ return { grammar: value.rev !== undefined ? 0 : SESSION_RULES_GRAMMAR };
59
+ if (typeof stamp !== "number" || !Number.isInteger(stamp) || stamp < 0) {
60
+ const got = typeof stamp === "number" ? "a number that is not a whole number of 0 or more" : valueTypeLabel(stamp);
61
+ return { got, sentence: `session rules carry a grammar stamp that is ${got} — the stamp is the whole number of the rule grammar the rules were written in (this engine reads grammar ${SESSION_RULES_GRAMMAR})` };
62
+ }
63
+ if (stamp > SESSION_RULES_GRAMMAR) {
64
+ return {
65
+ got: `${stamp}, a grammar newer than this engine's (${SESSION_RULES_GRAMMAR})`,
66
+ sentence: `session rules were written in rule grammar ${stamp}, newer than this engine reads (grammar ${SESSION_RULES_GRAMMAR}) — refusing rather than read their entries in an older grammar; an engine that reads grammar ${stamp} reads them`,
67
+ };
68
+ }
69
+ return { grammar: stamp };
70
+ }
52
71
  export function readSessionRulesShape(value) {
53
72
  const fault = (field, got, sentence) => ({ fault: { field, got }, sentence });
54
73
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
55
74
  const got = Array.isArray(value) ? "an array" : valueTypeLabel(value);
56
75
  return fault("", got, `session rules must be an object of rule buckets (${BUCKETS.join(", ")}), got ${got}`);
57
76
  }
58
- const rules = {};
77
+ const read = grammarOf(value);
78
+ if ("got" in read)
79
+ return fault("grammar", read.got, read.sentence);
80
+ const rules = { grammar: read.grammar };
59
81
  for (const [key, v] of Object.entries(value)) {
60
82
  if (STORE_STAMPS.has(key) || v === undefined)
61
83
  continue;
@@ -78,36 +100,160 @@ export function readSessionRulesShape(value) {
78
100
  return { rules };
79
101
  }
80
102
  export function normalizeRules(rules) {
103
+ return canonicalRules(shapeOrRefuse(rules));
104
+ }
105
+ function shapeOrRefuse(rules) {
81
106
  const read = readSessionRulesShape(rules);
82
107
  if ("fault" in read)
83
108
  throw new SessionPolicyError("invalid_shape", read.sentence, read.fault);
109
+ return read.rules;
110
+ }
111
+ function canonicalRules(read) {
84
112
  const out = {};
85
113
  for (const field of BUCKETS) {
86
- const list = read.rules[field];
114
+ const list = read[field];
87
115
  if (list === undefined)
88
116
  continue;
89
117
  out[field] = SESSION_RULE_BUCKETS[field] === "dirs" ? list.map((d) => posix.normalize(d).replace(/\/+$/, "") || "/") : [...list];
90
118
  }
91
119
  return out;
92
120
  }
93
- function effectiveEntries(field, list, mountedToolNames) {
94
- return compileAllowList(list ?? [], SESSION_RULE_BUCKETS[field], mountedToolNames).rows.map((r) => r.entry);
121
+ function earlierGrammarReads(grammar, field, entry, reading) {
122
+ if (grammar >= SESSION_RULES_GRAMMAR)
123
+ return false;
124
+ if (reading.kind === "inert" && reading.reason === "grammar")
125
+ return true;
126
+ if (grammar !== 0 || SESSION_RULE_BUCKETS[field] !== "allow")
127
+ return false;
128
+ return reading.kind === "row" ? compiledEntryName(reading.compiled) !== entry : reading.reason === "wildcard_all";
129
+ }
130
+ function readsAsNoRule(entry, field) {
131
+ const reading = readRuleEntry(entry, SESSION_RULE_BUCKETS[field]);
132
+ return reading.kind === "inert" && reading.reason === "grammar";
133
+ }
134
+ const NO_RULE_REASONS = new Set(["grammar", "earlier_grammar"]);
135
+ export function unreadableDenyEntries(rules, roster) {
136
+ return compileSessionRuleBucket(rules, "toolDeny", roster).inert.filter((i) => NO_RULE_REASONS.has(i.reason));
137
+ }
138
+ function readsAsWildcard(entry) {
139
+ return compileAllowList([entry], "allow").inert.some((i) => i.reason === "wildcard_all");
140
+ }
141
+ const STAR_IS_NO_ALLOW_RULE = "it is no allow rule in any grammar and a write holding it is refused";
142
+ function starWayOut(onlyStars, others, left) {
143
+ return onlyStars
144
+ ? `"*" is no allow rule in any grammar (a write holding it is refused); to drop it, write the session's rules afresh without it ${REWRITE_THROUGH}; ${left}`
145
+ : `${others}, leaving "*" out — ${STAR_IS_NO_ALLOW_RULE}; ${left}`;
146
+ }
147
+ export function compileSessionRuleBucket(rules, field, roster) {
148
+ const direction = SESSION_RULE_BUCKETS[field];
149
+ const entries = rules[field] ?? [];
150
+ const earlier = new Set(entries.filter((e) => earlierGrammarReads(rules.grammar, field, e, readRuleEntry(e, direction, roster))));
151
+ const whole = compileAllowList(entries, direction, roster);
152
+ const voided = direction === "allow" && whole.inert.some((i) => i.reason === "wildcard_all");
153
+ if (earlier.size === 0 || (voided && rules.grammar > 0))
154
+ return whole;
155
+ const list = compileAllowList(entries.filter((e) => !earlier.has(e)), direction, roster);
156
+ return { rows: list.rows, inert: [...list.inert, ...entries.filter((e) => earlier.has(e)).map((entry) => ({ entry, reason: "earlier_grammar" }))] };
157
+ }
158
+ const ADVISORY_ENTRIES_SHOWN = 5;
159
+ function shownEntries(entries, render) {
160
+ const rest = entries.length > ADVISORY_ENTRIES_SHOWN ? ` and ${entries.length - ADVISORY_ENTRIES_SHOWN} more` : "";
161
+ return `${entries.slice(0, ADVISORY_ENTRIES_SHOWN).map(render).join(", ")}${rest}`;
162
+ }
163
+ const REWRITE_THROUGH = `through this engine (an operator write of a new value without the stored row's rev; the write stamps grammar ${SESSION_RULES_GRAMMAR})`;
164
+ export function earlierGrammarAdvisory(sessionId, entries, field, grammar) {
165
+ const noRule = entries.filter((e) => readsAsNoRule(e, field));
166
+ const namedNoTool = entries.filter((e) => !readsAsNoRule(e, field));
167
+ const sentences = [...(namedNoTool.length > 0 ? [namedNoToolInGrammar0(namedNoTool)] : []), ...(noRule.length > 0 ? [noRuleInThisGrammar(noRule, field, grammar)] : [])];
168
+ return `session permission rules of session ${JSON.stringify(sessionId)}: ${sentences.join(" ")}`;
169
+ }
170
+ function namedNoToolInGrammar0(entries) {
171
+ const one = entries.length === 1;
172
+ const wildcard = entries.some(readsAsWildcard);
173
+ const rules = entries.some((e) => !readsAsWildcard(e));
174
+ const left = `left as stored, ${one ? "it keeps" : "they keep"} admitting nothing.`;
175
+ const way = !wildcard
176
+ ? `To make ${one ? "it an allow rule" : "them allow rules"}, write the session's rules afresh ${REWRITE_THROUGH}; ${left}`
177
+ : starWayOut(!rules, `To make the parenthesized ones allow rules, write the session's rules afresh ${REWRITE_THROUGH}`, left);
178
+ return (`toolAllow ${shownEntries(entries, renderListEntry)} ${one ? "was" : "were"} stored before the rule grammar stamp, when session rules compared an entry as a tool name as written, so ${wildcard ? 'an entry that is no bare tool name (a parenthesized rule, or "*")' : "a parenthesized entry"} named no tool — ` +
179
+ `${one ? "it admits" : "they admit"} nothing, as when written${wildcard ? "; the rest of the list is read as written, not voided" : ""}. ${way}`);
180
+ }
181
+ function noRuleInThisGrammar(entries, field, grammar) {
182
+ const one = entries.length === 1;
183
+ const stored = `${field} ${shownEntries(entries, (entry) => inertEntryPhrase({ entry, reason: "grammar" }))} ${one ? "was" : "were"} stored in rule grammar ${grammar}${grammar === 0 ? ", before the rule grammar stamp" : ""}, and this engine's rule grammar (${SESSION_RULES_GRAMMAR}) reads ${one ? "it" : "them"} as no rule`;
184
+ const way = `write the session's rules afresh ${REWRITE_THROUGH}, leaving ${one ? "it" : "them"} out or spelling the tool's name as a rule spells one — the write judge refuses an entry it cannot read`;
185
+ if (SESSION_RULE_BUCKETS[field] === "deny") {
186
+ return `${stored}: rules whose toolDeny holds an entry no grammar reads cannot be read, so every call is refused until they are rewritten. To lift that, ${way}, and a non-operator write cannot loosen rules that refuse every call.`;
187
+ }
188
+ const stars = entries.filter(readsAsWildcard).length;
189
+ const left = `left as stored, ${one ? "it keeps" : "they keep"} admitting nothing.`;
190
+ const wayOut = stars === 0
191
+ ? `To allow what ${one ? "it" : "they"} meant, ${way}; ${left}`
192
+ : starWayOut(stars === entries.length, `To allow what the others meant, write the session's rules afresh ${REWRITE_THROUGH}, spelling each tool's name as a rule spells one`, left);
193
+ return `${stored} — ${one ? "it admits" : "they admit"} nothing, and the rest of the list is read as written. ${wayOut}`;
194
+ }
195
+ export function earlierGrammarRefusalClause(entries, grammar) {
196
+ if (entries.length === 0)
197
+ return "";
198
+ const noRule = entries.filter((e) => readsAsNoRule(e, "toolAllow")).length;
199
+ const namedNoTool = entries.filter((e) => !readsAsNoRule(e, "toolAllow"));
200
+ const stars = namedNoTool.filter(readsAsWildcard).length;
201
+ const others = namedNoTool.length - stars;
202
+ const named = [...(stars > 0 ? ['"*"'] : []), ...(others > 0 ? [`${others} parenthesized ${others === 1 ? "entry" : "entries"}`] : [])];
203
+ const clauses = [
204
+ ...(namedNoTool.length > 0 ? [`where ${named.join(" and ")} ${namedNoTool.length === 1 ? "names no tool and admits" : "name no tool and admit"} nothing`] : []),
205
+ ...(noRule > 0 ? [`${namedNoTool.length > 0 ? "and " : "where "}${noRule} ${noRule === 1 ? "entry is" : "entries are"} no rule in this engine's rule grammar (${SESSION_RULES_GRAMMAR}) and ${noRule === 1 ? "admits" : "admit"} nothing`] : []),
206
+ ];
207
+ return `; the session's rules are stored in rule grammar ${grammar}, ${clauses.join(" ")}`;
208
+ }
209
+ export function earlierGrammarAnnouncer(sink) {
210
+ const announced = new Set();
211
+ return (entries, field, grammar) => {
212
+ const key = JSON.stringify([field, grammar, entries]);
213
+ if (sink === undefined || entries.length === 0 || announced.has(key))
214
+ return;
215
+ announced.add(key);
216
+ try {
217
+ sink(entries, field, grammar);
218
+ }
219
+ catch {
220
+ }
221
+ };
222
+ }
223
+ function effectiveEntries(rules, field, mountedToolNames) {
224
+ return compileSessionRuleBucket(rules, field, mountedToolNames).rows.map((r) => r.entry);
95
225
  }
96
226
  function assertEntriesRead(rules, mountedToolNames) {
227
+ const known = rootFacingToolNames(mountedToolNames);
97
228
  for (const field of ENTRY_FIELDS) {
98
229
  for (const entry of rules[field] ?? []) {
99
- const read = readRuleEntry(entry, SESSION_RULE_BUCKETS[field], mountedToolNames);
230
+ const reading = readRuleEntry(entry, SESSION_RULE_BUCKETS[field], known);
231
+ const read = earlierGrammarReads(rules.grammar, field, entry, reading) ? { kind: "inert", reason: "earlier_grammar" } : reading;
100
232
  if (read.kind === "row")
101
233
  continue;
102
234
  const { kind: _kind, ...why } = read;
103
235
  void _kind;
104
- const way = why.verdict === "retired"
105
- ? ` If this deployment mounts its own tool under ${renderListEntry(entry)}, declare it in the store's mountedToolNames.`
106
- : why.currentName === undefined
107
- ? ` An entry is a tool name (Read; a protocol tool is named ${NAMESPACED_NAME_SHAPES}), a protocol namespace (mcp__<server>), or a rule in its tool's own grammar (Bash(<command>), Edit(<path>), Skill(<name>), WebFetch(domain:<host>)).`
108
- : "";
236
+ const way = why.reason === "earlier_grammar"
237
+ ? readsAsNoRule(entry, field)
238
+ ? ` These rules are written in grammar ${rules.grammar} (a stored row), and this engine's grammar (${SESSION_RULES_GRAMMAR}) reads this entry as no rule (grammar) — ` +
239
+ (field === "toolAllow" && readsAsWildcard(entry) ? `leave the entry out: it spells "*", and ${STAR_IS_NO_ALLOW_RULE}.` : "leave the entry out, or spell the tool's name as a rule spells one.")
240
+ : ` These rules are written in grammar ${rules.grammar} (a stored row from before the grammar stamp), in which this entry names no tool.` +
241
+ (readsAsWildcard(entry)
242
+ ? ` It is no allow rule in grammar ${SESSION_RULES_GRAMMAR} either (a write holding it is refused) — leave the entry out.`
243
+ : ` To make it an allow rule, write the rules afresh — a value without the stored row's rev; the write stamps grammar ${SESSION_RULES_GRAMMAR} — or leave the entry out.`)
244
+ : why.verdict === "retired" || why.reason === "near_miss"
245
+ ? ` If this deployment mounts its own tool under ${renderListEntry(entry)}, declare it in the store's mountedToolNames.`
246
+ : why.currentName === undefined
247
+ ? ` An entry is a tool name (Read; a protocol tool is named ${NAMESPACED_NAME_SHAPES}), a protocol namespace (mcp__<server>), or a rule in its tool's own grammar (Bash(<command>), Edit(<path>), Skill(<name>), WebFetch(domain:<host>)).`
248
+ : "";
109
249
  const sentence = why.guidance !== undefined ? ` — ${why.guidance}` : "";
110
- throw new SessionPolicyError("inert_entry", `session rules ${field} holds ${inertEntryPhrase({ entry, ...why })}, which ${SESSION_RULE_BUCKETS[field] === "deny" ? "refuses" : "admits"} nothing as written${sentence}; nothing was written.${way}`, { field, entry, ...why });
250
+ const does = SESSION_RULE_BUCKETS[field] === "allow"
251
+ ? "admits nothing as written"
252
+ : NO_RULE_REASONS.has(why.reason)
253
+ ? "refuses every call as stored (rules whose toolDeny holds an entry no grammar reads cannot be read) — a write holding it is refused"
254
+ : "refuses nothing as written";
255
+ const carriedBy = why.reason === "near_miss" && why.currentName !== undefined && !mountedToolNames.has(why.currentName) ? { carriedBy: "catalog" } : {};
256
+ throw new SessionPolicyError("inert_entry", `session rules ${field} holds ${inertEntryPhrase({ entry, ...why, ...carriedBy })}, which ${does}${sentence}; nothing was written.${way}`, { field, entry, ...why });
111
257
  }
112
258
  }
113
259
  }
@@ -115,8 +261,9 @@ export function judgeSessionRulesWrite(prior, rules, opts, mountedToolNames = NO
115
261
  if (opts?.expectedRev !== undefined && (prior?.rev ?? 0) !== opts.expectedRev) {
116
262
  throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${prior?.rev ?? 0})`);
117
263
  }
118
- const clean = normalizeRules(rules);
119
- assertEntriesRead(clean, mountedToolNames);
264
+ const read = shapeOrRefuse(rules);
265
+ const clean = canonicalRules(read);
266
+ assertEntriesRead(read, mountedToolNames);
120
267
  if (prior !== null && opts?.operator !== true) {
121
268
  const stored = readSessionRulesShape(prior);
122
269
  if ("fault" in stored) {
@@ -127,7 +274,7 @@ export function judgeSessionRulesWrite(prior, rules, opts, mountedToolNames = NO
127
274
  throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may only tighten; refused: ${loosens.join("; ")}`);
128
275
  }
129
276
  }
130
- return clean;
277
+ return { ...clean, grammar: SESSION_RULES_GRAMMAR };
131
278
  }
132
279
  export function loosenReasons(priorValue, nextValue, mountedToolNames = NO_DECLARED_TOOLS) {
133
280
  const shapeOf = (side, value) => {
@@ -138,17 +285,23 @@ export function loosenReasons(priorValue, nextValue, mountedToolNames = NO_DECLA
138
285
  };
139
286
  const prior = shapeOf("prior", priorValue);
140
287
  const next = shapeOf("next", nextValue);
288
+ if (unreadableDenyEntries(next, mountedToolNames).length > 0)
289
+ return [];
290
+ const unreadPrior = unreadableDenyEntries(prior, mountedToolNames);
291
+ if (unreadPrior.length > 0) {
292
+ return [`the prior rules refuse every call — their toolDeny holds ${shownEntries(unreadPrior.map((i) => i.entry), (entry) => inertEntryPhrase({ entry, reason: "grammar" }))}, which no grammar reads`];
293
+ }
141
294
  const reasons = [];
142
- const keptDeny = new Set(effectiveEntries("toolDeny", next.toolDeny, mountedToolNames));
143
- for (const x of effectiveEntries("toolDeny", prior.toolDeny, mountedToolNames))
295
+ const keptDeny = new Set(effectiveEntries(next, "toolDeny", mountedToolNames));
296
+ for (const x of effectiveEntries(prior, "toolDeny", mountedToolNames))
144
297
  if (!keptDeny.has(x))
145
298
  reasons.push(`removed toolDeny ${renderListEntry(x)}`);
146
299
  if (prior.toolAllow !== undefined) {
147
300
  if (next.toolAllow === undefined)
148
301
  reasons.push("removed toolAllow allowlist (all allowed)");
149
302
  else {
150
- const had = new Set(effectiveEntries("toolAllow", prior.toolAllow, mountedToolNames));
151
- for (const x of effectiveEntries("toolAllow", next.toolAllow, mountedToolNames))
303
+ const had = new Set(effectiveEntries(prior, "toolAllow", mountedToolNames));
304
+ for (const x of effectiveEntries(next, "toolAllow", mountedToolNames))
152
305
  if (!had.has(x))
153
306
  reasons.push(`widened toolAllow with ${renderListEntry(x)}`);
154
307
  }
@@ -51,6 +51,9 @@ export interface OrphanToolCall {
51
51
  * message and `errorCode` on the synthetic `tool_end` frame, so a consumer never parses the text. Two-valued
52
52
  * (a third value needs a cross-repo slip, see {@link reconcileInterruptedSession}). */
53
53
  export type ReconciledErrorKind = "interrupted_never_started" | "interrupted_outcome_unknown";
54
+ /** The class that is positive evidence the call never ran: closed with no start observed. The synthetic `tool_end` frame's
55
+ * `notExecuted` (`reconciledEnd`) and the persisted close's `details.notExecuted` both read it — one table, two faces. */
56
+ export declare const closeNeverExecuted: (errorKind: ReconciledErrorKind) => boolean;
54
57
  /** One orphan closed by {@link reconcileInterruptedSession}, with everything the caller needs to mint the
55
58
  * matching stream frame WITHOUT re-reading the session. */
56
59
  export type RecoveredOrphan = OrphanToolCall & {
@@ -153,6 +156,7 @@ export declare function isInterruptedClose(message: {
153
156
  /** The `toolResult` message for one {@link InterruptedResult} — the persisted and the request-view closes are this one shape. */
154
157
  export declare function interruptedResultMessage(result: InterruptedResult, timestamp: number): ToolResultMessage<{
155
158
  errorKind: ReconciledErrorKind;
159
+ notExecuted?: true;
156
160
  }>;
157
161
  /**
158
162
  * Reconcile a resumed session's active branch: close any orphan tool calls with a synthetic
@@ -1,4 +1,5 @@
1
1
  import { readMember } from "./thrown-value.js";
2
+ export const closeNeverExecuted = (errorKind) => errorKind === "interrupted_never_started";
2
3
  const INTERRUPTED_UNKNOWN = "[INTERRUPTED] The previous run ended before this tool call's result was recorded. Its outcome " +
3
4
  "is UNKNOWN — the action may have completed, partially completed, or never run. Do not assume " +
4
5
  "success or failure. Before relying on it: if it is safe to read, query the current state to " +
@@ -119,7 +120,7 @@ export function interruptedResultMessage(result, timestamp) {
119
120
  toolCallId: result.toolCallId,
120
121
  toolName: result.toolName,
121
122
  content: [{ type: "text", text: result.text }],
122
- details: { errorKind: result.errorKind },
123
+ details: { errorKind: result.errorKind, ...(closeNeverExecuted(result.errorKind) ? { notExecuted: true } : {}) },
123
124
  isError: true,
124
125
  timestamp,
125
126
  };
@@ -2,6 +2,7 @@ import { types } from "node:util";
2
2
  import { dispositionsOfRow, SHELL_READ_FINDINGS } from "../tools/fs/shell-read-judge.js";
3
3
  import { receiveContract } from "./decision-receipt.js";
4
4
  import { egressThrownText } from "./untrusted-egress.js";
5
+ import { receive } from "./mods/kernel/receive.js";
5
6
  const DISPOSITIONS = ["refuse", "mandated", "ordinary", "vouch"];
6
7
  const MEMBER_KINDS = {
7
8
  disposition: "word",
@@ -20,37 +21,7 @@ const MEMBER_KINDS = {
20
21
  admit: "admission",
21
22
  };
22
23
  const VERDICT_MEMBERS = Object.keys(MEMBER_KINDS);
23
- const DATA_DEPTH = 8;
24
- function ownedData(value, depth, path) {
25
- if (value === null || value === undefined || typeof value === "string" || typeof value === "number" || typeof value === "boolean")
26
- return { ok: true, value };
27
- if (typeof value !== "object" || depth >= DATA_DEPTH || types.isProxy(value) || path.has(value))
28
- return { ok: false };
29
- const list = Array.isArray(value);
30
- const proto = Object.getPrototypeOf(value);
31
- if (list ? proto !== Array.prototype : proto !== Object.prototype && proto !== null)
32
- return { ok: false };
33
- const own = Object.getOwnPropertyDescriptors(value);
34
- path.add(value);
35
- try {
36
- const out = list ? [] : {};
37
- for (const key of Object.keys(own)) {
38
- if (list && key === "length")
39
- continue;
40
- const d = own[key];
41
- if (!Object.hasOwn(d, "value") || d.enumerable !== true)
42
- return { ok: false };
43
- const inner = ownedData(d.value, depth + 1, path);
44
- if (!inner.ok)
45
- return { ok: false };
46
- Object.defineProperty(out, key, { value: inner.value, enumerable: true, writable: true, configurable: true });
47
- }
48
- return { ok: true, value: out };
49
- }
50
- finally {
51
- path.delete(value);
52
- }
53
- }
24
+ const OWNED_DATA_MODE = Object.freeze({ accessors: "refuse", depth: 8, others: "refuse", isProxy: (v) => types.isProxy(v) });
54
25
  function receivedAnswer(answer, call) {
55
26
  if (typeof answer !== "object" || answer === null)
56
27
  return undefined;
@@ -65,7 +36,7 @@ function receivedAnswer(answer, call) {
65
36
  if (kind === "word" ? typeof v !== "string" : kind === "mark" ? v !== true : kind === "face" ? v !== "write" : kind === "admission" ? typeof v !== "function" || types.isProxy(v) : false)
66
37
  return undefined;
67
38
  if (kind === "record") {
68
- const data = ownedData(v, 0, new Set());
39
+ const data = receive(v, OWNED_DATA_MODE);
69
40
  if (!data.ok || typeof data.value !== "object" || data.value === null || Array.isArray(data.value))
70
41
  return undefined;
71
42
  v = data.value;
@@ -63,9 +63,11 @@ export interface SideQuerySpec {
63
63
  * Per-model auth — MIRRORS {@link TaskSpec.getApiKeyAndHeaders} (same signature, resolved per
64
64
  * call against the RESOLVED model, exactly like the task path's per-call hook). The brain
65
65
  * contract is the route pairing law (route-adjudicator.ts): a per-model credential (this hook,
66
- * or an auth header on `Model.headers`) always rides; the deployment credential rides only to the
67
- * deployment's own root — a model carrying a root of its own is refused
68
- * (`route.credential_mismatch` / `route.credential_missing`), never silently followed.
66
+ * or a credential header on `Model.headers`) rides to the origin it first went out to and is
67
+ * refused anywhere else (`route.credential_origin_mismatch`); an answer handing the key beside
68
+ * the lane's own key header is refused as one verdict (`route.credential_answer_conflict`); the
69
+ * deployment credential rides only to the deployment's own root — a model carrying a root of its
70
+ * own is refused (`route.credential_mismatch` / `route.credential_missing`), never silently followed.
69
71
  * Historically the fallback was unconditional (`options.apiKey ?? config.apiKey`, with a model's
70
72
  * own `baseUrl` outranking the brain's), so a side query routed to a model carrying its own
71
73
  * `baseUrl` + per-model key sent the GATEWAY key to the per-model (possibly external) URL — the
@@ -69,8 +69,9 @@ export declare function skillGrantBindingMembers(call: {
69
69
  * bytes — the row stays pending: deny it, then activate again) and the resumed execution boundary (`site: "execution"`, the
70
70
  * bytes that will run, after every station that can rewrite them — the approval is spent: the call does not run). The
71
71
  * sentence says which side of the digest differs, read off the row's own arguments: the arguments name another skill than
72
- * the card's (`row.args`), or the card's skill no longer declares the lists the card showed (or is not served). Names the
73
- * skills, never a path; says the way out.
72
+ * the card's (`row.args`), or the card's skill's lists no longer bind to the card's digest (or it is not served) — its
73
+ * definition changed, or this engine reads an entry of it differently than the engine that parked the row (an entry that
74
+ * reads as nothing: the digest is the list reader's reading, not the text). Names the skills, never a path; says the way out.
74
75
  */
75
76
  export declare function parkedSkillApprovalRefusal(row: {
76
77
  readonly toolName: string;