@sema-agent/core 7.54.1 → 7.55.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 (399) hide show
  1. package/CHANGELOG.md +437 -0
  2. package/dist/agents/agent-transcript-tool.js +2 -1
  3. package/dist/agents/builtin-agents.d.ts +12 -14
  4. package/dist/agents/cc-peer-directory.d.ts +36 -0
  5. package/dist/agents/cc-peer-directory.js +138 -0
  6. package/dist/agents/cc-peer-wire.d.ts +105 -0
  7. package/dist/agents/cc-peer-wire.js +98 -0
  8. package/dist/agents/cc-uds-peer-channel.d.ts +40 -0
  9. package/dist/agents/cc-uds-peer-channel.js +457 -0
  10. package/dist/agents/cross-session-envelope.d.ts +4 -1
  11. package/dist/agents/cross-session-envelope.js +8 -1
  12. package/dist/agents/cross-session-judge.d.ts +2 -2
  13. package/dist/agents/list-agents-tool.js +2 -1
  14. package/dist/agents/observer.d.ts +4 -5
  15. package/dist/agents/observer.js +2 -1
  16. package/dist/agents/peer-admission.d.ts +6 -0
  17. package/dist/agents/peer-admission.js +4 -1
  18. package/dist/agents/peer-directory.d.ts +43 -9
  19. package/dist/agents/peer-directory.js +66 -23
  20. package/dist/agents/peer-notices.d.ts +1 -1
  21. package/dist/agents/peer-session-drain.d.ts +3 -1
  22. package/dist/agents/peer-session-drain.js +1 -0
  23. package/dist/agents/read-inbox-tool.js +2 -1
  24. package/dist/agents/retain-ledger.d.ts +1 -1
  25. package/dist/agents/roster-store.d.ts +1 -1
  26. package/dist/agents/send-message-tool.d.ts +10 -0
  27. package/dist/agents/send-message-tool.js +34 -5
  28. package/dist/agents/subagent.d.ts +34 -79
  29. package/dist/agents/subagent.js +45 -46
  30. package/dist/agents/tool-filter.d.ts +29 -46
  31. package/dist/agents/tool-filter.js +8 -31
  32. package/dist/agents/verify.d.ts +3 -3
  33. package/dist/brain/anthropic.js +14 -9
  34. package/dist/brain/errors.d.ts +15 -0
  35. package/dist/brain/errors.js +10 -0
  36. package/dist/brain/model-presets.d.ts +4 -4
  37. package/dist/brain/open-responses.js +15 -10
  38. package/dist/brain/openai.js +17 -12
  39. package/dist/brain/provider-dialect.d.ts +39 -3
  40. package/dist/brain/provider-dialect.js +28 -0
  41. package/dist/brain/reasoning.d.ts +1 -1
  42. package/dist/brain/request-params.d.ts +55 -5
  43. package/dist/brain/request-params.js +35 -3
  44. package/dist/brain/retry.d.ts +1 -1
  45. package/dist/brain/route-adjudicator.d.ts +15 -0
  46. package/dist/brain/route-adjudicator.js +15 -0
  47. package/dist/brain/stream-engine.js +6 -5
  48. package/dist/brain/stream-shared.d.ts +1 -1
  49. package/dist/core/advisor-verdict.d.ts +120 -0
  50. package/dist/core/advisor-verdict.js +92 -0
  51. package/dist/core/agent-definition.d.ts +7 -8
  52. package/dist/core/allow-entry.d.ts +49 -23
  53. package/dist/core/allow-entry.js +57 -32
  54. package/dist/core/allow-rule-shadow.d.ts +7 -0
  55. package/dist/core/approval-object.d.ts +2 -1
  56. package/dist/core/approval-object.js +6 -6
  57. package/dist/core/ask-origin.d.ts +63 -11
  58. package/dist/core/ask-origin.js +27 -5
  59. package/dist/core/ask-question.js +10 -4
  60. package/dist/core/auto-compaction.d.ts +11 -8
  61. package/dist/core/auto-compaction.js +4 -2
  62. package/dist/core/auto-mode-arming.d.ts +2 -2
  63. package/dist/core/auto-mode-defaults.d.ts +8 -8
  64. package/dist/core/auto-mode-prompt-assets.d.ts +3 -3
  65. package/dist/core/auto-mode-prompt.d.ts +1 -1
  66. package/dist/core/auto-mode.d.ts +17 -9
  67. package/dist/core/auto-mode.js +9 -6
  68. package/dist/core/background-agent-store.d.ts +3 -3
  69. package/dist/core/canonical-json.d.ts +1 -1
  70. package/dist/core/canonical-json.js +2 -2
  71. package/dist/core/checkpoint-requirements.d.ts +6 -3
  72. package/dist/core/checkpoint-requirements.js +4 -14
  73. package/dist/core/checkpoint-store.d.ts +80 -51
  74. package/dist/core/checkpoint-store.js +56 -74
  75. package/dist/core/child-clamp-seats.d.ts +6 -0
  76. package/dist/core/child-clamp-seats.js +3 -16
  77. package/dist/core/compliance.d.ts +2 -2
  78. package/dist/core/context-edit.d.ts +6 -7
  79. package/dist/core/cross-boundary-taint.d.ts +9 -3
  80. package/dist/core/cross-boundary-taint.js +5 -3
  81. package/dist/core/decision-receipt.js +2 -2
  82. package/dist/core/effective-path-target.d.ts +1 -1
  83. package/dist/core/engine-notice.d.ts +19 -5
  84. package/dist/core/exec-output-tail.d.ts +1 -1
  85. package/dist/core/file-history-retention.d.ts +1 -1
  86. package/dist/core/file-history-store.d.ts +59 -9
  87. package/dist/core/file-history-store.js +35 -17
  88. package/dist/core/gate-fold.js +3 -2
  89. package/dist/core/gate-lanes.js +12 -8
  90. package/dist/core/gate-outcome.d.ts +17 -4
  91. package/dist/core/gate-outcome.js +30 -0
  92. package/dist/core/governance-codes.d.ts +13 -3
  93. package/dist/core/governance-codes.js +10 -0
  94. package/dist/core/hands-band.d.ts +1 -1
  95. package/dist/core/hooks.d.ts +4 -4
  96. package/dist/core/host-target.d.ts +49 -0
  97. package/dist/core/host-target.js +59 -0
  98. package/dist/core/image-downsample.d.ts +1 -1
  99. package/dist/core/lsp-diagnostics.d.ts +4 -4
  100. package/dist/core/lsp.d.ts +3 -3
  101. package/dist/core/mailbox-store.d.ts +4 -0
  102. package/dist/core/mcp-connection-pool.d.ts +18 -2
  103. package/dist/core/mcp-connection-pool.js +53 -6
  104. package/dist/core/mcp-failure.d.ts +23 -4
  105. package/dist/core/mcp-failure.js +26 -11
  106. package/dist/core/mcp-listing-drift.d.ts +10 -0
  107. package/dist/core/mcp-listing-drift.js +6 -0
  108. package/dist/core/mcp-server-link.d.ts +173 -0
  109. package/dist/core/mcp-server-link.js +127 -0
  110. package/dist/core/mcp-server-spec.d.ts +4 -2
  111. package/dist/core/mcp-session-end.d.ts +26 -0
  112. package/dist/core/mcp-session-end.js +29 -0
  113. package/dist/core/mcp-startup-request.d.ts +17 -0
  114. package/dist/core/mcp-startup-request.js +17 -0
  115. package/dist/core/mcp.d.ts +13 -13
  116. package/dist/core/mcp.js +187 -162
  117. package/dist/core/media-byte-cap.d.ts +1 -1
  118. package/dist/core/memory-engine/engine.d.ts +9 -9
  119. package/dist/core/memory-engine/provenance-wording.d.ts +1 -1
  120. package/dist/core/memory-engine/tools.js +4 -1
  121. package/dist/core/mode-policy.d.ts +1 -1
  122. package/dist/core/namespaced-rule-name.d.ts +33 -7
  123. package/dist/core/namespaced-rule-name.js +39 -25
  124. package/dist/core/output-style.d.ts +1 -1
  125. package/dist/core/param-rule-reach.d.ts +15 -0
  126. package/dist/core/param-rule-reach.js +30 -0
  127. package/dist/core/parent-spec-seat.d.ts +8 -1
  128. package/dist/core/parent-spec-seat.js +4 -0
  129. package/dist/core/pause-registry.d.ts +16 -5
  130. package/dist/core/pause-registry.js +6 -2
  131. package/dist/core/peer-channel.d.ts +70 -0
  132. package/dist/core/peer-channel.js +1 -0
  133. package/dist/core/permission-rule-consent.d.ts +11 -5
  134. package/dist/core/permission-rule-consent.js +37 -30
  135. package/dist/core/permission-rule-model.d.ts +233 -64
  136. package/dist/core/permission-rule-model.js +384 -85
  137. package/dist/core/permission-rule-provider.d.ts +2 -1
  138. package/dist/core/permission-rule-store.d.ts +20 -25
  139. package/dist/core/permission-rule-store.js +1 -4
  140. package/dist/core/permission-rule-sync.d.ts +11 -2
  141. package/dist/core/permission-rule-sync.js +8 -1
  142. package/dist/core/permission-rules.d.ts +49 -11
  143. package/dist/core/permission-rules.js +123 -89
  144. package/dist/core/persisted-rule-arms.d.ts +4 -2
  145. package/dist/core/present-plan-tool.js +3 -1
  146. package/dist/core/protocol-naming.d.ts +2 -2
  147. package/dist/core/read-only-shell-table.d.ts +1 -1
  148. package/dist/core/read-only-shell.d.ts +1 -1
  149. package/dist/core/roles.d.ts +6 -2
  150. package/dist/core/roles.js +45 -44
  151. package/dist/core/rule-offer-read.d.ts +53 -0
  152. package/dist/core/rule-offer-read.js +146 -0
  153. package/dist/core/rule-owner.d.ts +46 -0
  154. package/dist/core/rule-owner.js +28 -0
  155. package/dist/core/runner/advisor-instructions.d.ts +70 -0
  156. package/dist/core/runner/advisor-instructions.js +61 -0
  157. package/dist/core/runner/advisor-tool.d.ts +33 -0
  158. package/dist/core/runner/advisor-tool.js +112 -0
  159. package/dist/core/runner/announce-once-ledger.d.ts +4 -26
  160. package/dist/core/runner/announce-once-ledger.js +14 -46
  161. package/dist/core/runner/approval-feedback.js +6 -3
  162. package/dist/core/runner/assemble-result.d.ts +1 -1
  163. package/dist/core/runner/caller-roster.d.ts +7 -0
  164. package/dist/core/runner/caller-roster.js +11 -0
  165. package/dist/core/runner/classifier-ladder.d.ts +27 -5
  166. package/dist/core/runner/classifier-ladder.js +12 -11
  167. package/dist/core/runner/compaction-restate.d.ts +10 -6
  168. package/dist/core/runner/compaction-restate.js +9 -3
  169. package/dist/core/runner/contracts.d.ts +13 -9
  170. package/dist/core/runner/conversation-segment.d.ts +28 -0
  171. package/dist/core/runner/conversation-segment.js +42 -0
  172. package/dist/core/runner/cross-boundary-lane.d.ts +6 -5
  173. package/dist/core/runner/cross-boundary-lane.js +26 -15
  174. package/dist/core/runner/decide-continuation.d.ts +7 -4
  175. package/dist/core/runner/decide-continuation.js +2 -2
  176. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  177. package/dist/core/runner/derived-route-fallback.d.ts +24 -24
  178. package/dist/core/runner/derived-route-fallback.js +9 -7
  179. package/dist/core/runner/gate-exit.d.ts +6 -0
  180. package/dist/core/runner/gate-exit.js +7 -2
  181. package/dist/core/runner/initial-run-state.js +1 -1
  182. package/dist/core/runner/permission-rule-lanes.d.ts +2 -2
  183. package/dist/core/runner/permission-rule-lanes.js +14 -7
  184. package/dist/core/runner/person-path-rows.js +1 -1
  185. package/dist/core/runner/prepare-advisor.d.ts +63 -0
  186. package/dist/core/runner/prepare-advisor.js +77 -0
  187. package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
  188. package/dist/core/runner/prepare-config-doors.d.ts +6 -4
  189. package/dist/core/runner/prepare-config-doors.js +18 -12
  190. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
  191. package/dist/core/runner/prepare-delegation-surface.js +1 -1
  192. package/dist/core/runner/prepare-file-history.js +39 -39
  193. package/dist/core/runner/prepare-gate-stations.d.ts +1 -4
  194. package/dist/core/runner/prepare-gate-stations.js +2 -7
  195. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -4
  196. package/dist/core/runner/prepare-inherited-gate.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.js +7 -7
  198. package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
  199. package/dist/core/runner/prepare-policy-chain.js +4 -10
  200. package/dist/core/runner/prepare-project-context.d.ts +5 -2
  201. package/dist/core/runner/prepare-project-context.js +6 -3
  202. package/dist/core/runner/prepare-prompt-inputs.js +1 -1
  203. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -2
  204. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  205. package/dist/core/runner/prepare-safety-scan.d.ts +9 -12
  206. package/dist/core/runner/prepare-safety-scan.js +8 -6
  207. package/dist/core/runner/prepare-task.js +15 -18
  208. package/dist/core/runner/prepare-tool-disclosure-mount.js +6 -4
  209. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -3
  210. package/dist/core/runner/prepare-wiring-manifest.js +2 -2
  211. package/dist/core/runner/resume-policy-outcome.js +1 -1
  212. package/dist/core/runner/retired-runner-deps.js +1 -1
  213. package/dist/core/runner/run-attachment-seats.js +0 -1
  214. package/dist/core/runner/run-compaction-machinery.d.ts +1 -1
  215. package/dist/core/runner/run-compaction-machinery.js +2 -12
  216. package/dist/core/runner/run-git-lane.d.ts +1 -0
  217. package/dist/core/runner/run-harness-handlers.js +2 -3
  218. package/dist/core/runner/run-leg.js +13 -0
  219. package/dist/core/runner/run-settle-and-teardown.js +1 -1
  220. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  221. package/dist/core/runner/run-turn-boundary.d.ts +1 -0
  222. package/dist/core/runner/run-turn-boundary.js +14 -12
  223. package/dist/core/runner/session-rule-policy.d.ts +22 -21
  224. package/dist/core/runner/session-rule-policy.js +13 -28
  225. package/dist/core/runner/side-call-account.d.ts +32 -8
  226. package/dist/core/runner/side-call-account.js +23 -1
  227. package/dist/core/runner/strict-output-schema.d.ts +1 -1
  228. package/dist/core/runner/sub-agent-deny-lists.js +4 -3
  229. package/dist/core/runner/synthetic-tools.d.ts +10 -13
  230. package/dist/core/runner/synthetic-tools.js +4 -2
  231. package/dist/core/runner/tool-disclosure.js +2 -1
  232. package/dist/core/runner/tool-face-overlay.d.ts +2 -1
  233. package/dist/core/runner/tool-face-overlay.js +17 -1
  234. package/dist/core/runner/tool-progress-lane.d.ts +2 -2
  235. package/dist/core/runner/tool-turn-repair-seat.d.ts +3 -2
  236. package/dist/core/runner/turn-attachments.d.ts +60 -67
  237. package/dist/core/runner-deps.d.ts +37 -37
  238. package/dist/core/runtime-caps.d.ts +4 -4
  239. package/dist/core/safety-merge-corpus.js +17 -1
  240. package/dist/core/same-plain-value.d.ts +40 -0
  241. package/dist/core/same-plain-value.js +49 -0
  242. package/dist/core/scheduler.d.ts +1 -1
  243. package/dist/core/sensitive-path-policy.d.ts +1 -1
  244. package/dist/core/sensitive-path-policy.js +1 -1
  245. package/dist/core/session-policy-store.d.ts +77 -22
  246. package/dist/core/session-policy-store.js +56 -14
  247. package/dist/core/session-reconcile.d.ts +2 -2
  248. package/dist/core/session-store.d.ts +80 -30
  249. package/dist/core/session-store.js +118 -76
  250. package/dist/core/session.d.ts +24 -46
  251. package/dist/core/shared-memory/tools.js +3 -1
  252. package/dist/core/shared-memory/types.d.ts +1 -1
  253. package/dist/core/side-query.d.ts +10 -0
  254. package/dist/core/side-query.js +5 -0
  255. package/dist/core/store-contracts/checkpoint-store-contract.js +29 -2
  256. package/dist/core/store-contracts/file-history-store-contract.js +43 -3
  257. package/dist/core/store-contracts/session-policy-store-contract.d.ts +7 -0
  258. package/dist/core/store-contracts/session-policy-store-contract.js +130 -0
  259. package/dist/core/store-contracts/session-repo-contract.js +125 -0
  260. package/dist/core/store-contracts/session-store-contract.d.ts +4 -7
  261. package/dist/core/store-contracts/session-store-contract.js +215 -41
  262. package/dist/core/store-contracts/workflow-journal-store-contract.js +26 -1
  263. package/dist/core/surrogate-safe-slice.d.ts +3 -3
  264. package/dist/core/task-event.d.ts +10 -4
  265. package/dist/core/task-notification.d.ts +3 -3
  266. package/dist/core/task-registry-agent.d.ts +4 -4
  267. package/dist/core/task-registry-shared.d.ts +4 -4
  268. package/dist/core/task-registry-shared.js +1 -0
  269. package/dist/core/task-registry.d.ts +4 -4
  270. package/dist/core/task-registry.js +3 -1
  271. package/dist/core/task-result.d.ts +3 -3
  272. package/dist/core/task-spec.d.ts +52 -52
  273. package/dist/core/task-stream.d.ts +6 -6
  274. package/dist/core/task-tool-shape.d.ts +4 -4
  275. package/dist/core/tighten-task-spec.d.ts +1 -1
  276. package/dist/core/tool-catalog-entries.d.ts +19 -2
  277. package/dist/core/tool-catalog-entries.js +57 -42
  278. package/dist/core/tool-conformance.js +1 -0
  279. package/dist/core/tool-errors.d.ts +2 -2
  280. package/dist/core/tool-face-reader.js +5 -3
  281. package/dist/core/tool-face.d.ts +21 -2
  282. package/dist/core/tool-face.js +1 -1
  283. package/dist/core/tool-model-gate.d.ts +3 -3
  284. package/dist/core/tool-name-aliases.d.ts +29 -8
  285. package/dist/core/tool-name-aliases.js +27 -3
  286. package/dist/core/tool-name-readings.d.ts +5 -8
  287. package/dist/core/tool-name-readings.js +1 -2
  288. package/dist/core/tool-policy.d.ts +24 -5
  289. package/dist/core/tool-policy.js +34 -10
  290. package/dist/core/tool-registry.d.ts +14 -7
  291. package/dist/core/tool-registry.js +17 -7
  292. package/dist/core/tool-result-store.js +2 -1
  293. package/dist/core/tool-roster.d.ts +47 -18
  294. package/dist/core/tool-roster.js +53 -32
  295. package/dist/core/tool-spec-members.js +1 -1
  296. package/dist/core/tool-spec.d.ts +35 -21
  297. package/dist/core/tools.js +3 -2
  298. package/dist/core/trace.d.ts +4 -0
  299. package/dist/core/untrusted-egress.d.ts +1 -1
  300. package/dist/core/untrusted-text.d.ts +1 -1
  301. package/dist/core/warm-resume.d.ts +1 -1
  302. package/dist/core/wiring-manifest.d.ts +49 -0
  303. package/dist/core/wiring-manifest.js +13 -5
  304. package/dist/core/workflow-journal-store.d.ts +62 -0
  305. package/dist/core/workflow-journal-store.js +77 -1
  306. package/dist/core/workflow-run-store.d.ts +6 -0
  307. package/dist/core/workflow-run-store.js +2 -0
  308. package/dist/engine/compaction/compaction.d.ts +9 -9
  309. package/dist/engine/execution-env/node-execution-env.d.ts +19 -5
  310. package/dist/engine/execution-env/node-execution-env.js +43 -22
  311. package/dist/engine/harness/agent-harness.d.ts +14 -8
  312. package/dist/engine/harness/agent-harness.js +33 -26
  313. package/dist/engine/harness/types.d.ts +29 -7
  314. package/dist/engine/harness/types.js +12 -1
  315. package/dist/engine/llm/types.d.ts +9 -9
  316. package/dist/engine/llm/validation.d.ts +4 -1
  317. package/dist/engine/llm/validation.js +13 -1
  318. package/dist/engine/loop/agent-loop.d.ts +1 -1
  319. package/dist/engine/loop/agent-loop.js +10 -8
  320. package/dist/engine/loop/types.d.ts +15 -13
  321. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  322. package/dist/engine/session/memory-repo.d.ts +10 -11
  323. package/dist/engine/session/memory-repo.js +13 -8
  324. package/dist/engine/session/parent-chain-guard.d.ts +3 -2
  325. package/dist/engine/session/parent-chain-guard.js +1 -1
  326. package/dist/engine/session/repo-utils.d.ts +31 -3
  327. package/dist/engine/session/repo-utils.js +42 -5
  328. package/dist/index.d.ts +27 -15
  329. package/dist/index.js +21 -11
  330. package/dist/internal/harness-types.d.ts +1 -1
  331. package/dist/internal/harness.d.ts +3 -3
  332. package/dist/internal/harness.js +2 -2
  333. package/dist/internal/llm.d.ts +1 -1
  334. package/dist/internal/llm.js +1 -1
  335. package/dist/orchestration/goal.js +2 -2
  336. package/dist/orchestration/run-workflow-tool.d.ts +2 -2
  337. package/dist/orchestration/run-workflow-tool.js +5 -3
  338. package/dist/orchestration/workflow-size-guideline.d.ts +4 -4
  339. package/dist/orchestration/workflow-types.d.ts +18 -0
  340. package/dist/orchestration/workflow-types.js +6 -0
  341. package/dist/orchestration/workflow.d.ts +2 -13
  342. package/dist/orchestration/workflow.js +21 -31
  343. package/dist/prompt-assembly/event-registry.js +1 -0
  344. package/dist/prompt-assembly/tool-catalog.d.ts +3 -0
  345. package/dist/prompt-assembly/tool-catalog.js +3 -0
  346. package/dist/prompts/default.d.ts +14 -14
  347. package/dist/scenarios/full-body.d.ts +4 -4
  348. package/dist/stores/file/adoption/adopt.d.ts +4 -3
  349. package/dist/stores/file/adoption/adopt.js +24 -31
  350. package/dist/stores/file/adoption/marker.d.ts +6 -13
  351. package/dist/stores/file/adoption/marker.js +6 -15
  352. package/dist/stores/file/checkpoint-store.d.ts +3 -0
  353. package/dist/stores/file/checkpoint-store.js +9 -3
  354. package/dist/stores/file/file-history-store.d.ts +20 -19
  355. package/dist/stores/file/file-history-store.js +152 -135
  356. package/dist/stores/file/fs-atomic.d.ts +15 -0
  357. package/dist/stores/file/fs-atomic.js +89 -14
  358. package/dist/stores/file/index.d.ts +1 -1
  359. package/dist/stores/file/mailbox-store.d.ts +3 -0
  360. package/dist/stores/file/mailbox-store.js +3 -1
  361. package/dist/stores/file/permission-rule-adopt.d.ts +1 -1
  362. package/dist/stores/file/permission-rule-adopt.js +15 -16
  363. package/dist/stores/file/permission-rule-store.d.ts +18 -2
  364. package/dist/stores/file/permission-rule-store.js +21 -14
  365. package/dist/stores/file/session-policy-store.d.ts +3 -3
  366. package/dist/stores/file/session-store.d.ts +26 -11
  367. package/dist/stores/file/session-store.js +77 -40
  368. package/dist/stores/file/workflow-journal-store.d.ts +10 -4
  369. package/dist/stores/file/workflow-journal-store.js +28 -13
  370. package/dist/tools/artifact/artifact-text.d.ts +1 -1
  371. package/dist/tools/artifact/artifact-tool.js +2 -1
  372. package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -3
  373. package/dist/tools/fs/fs-bash.d.ts +2 -2
  374. package/dist/tools/fs/fs-bash.js +3 -1
  375. package/dist/tools/fs/fs-shared.d.ts +8 -8
  376. package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
  377. package/dist/tools/fs/notebook.d.ts +2 -2
  378. package/dist/tools/fs/read-deny.d.ts +29 -7
  379. package/dist/tools/fs/read-deny.js +63 -22
  380. package/dist/tools/fs/removal-targets.d.ts +1 -1
  381. package/dist/tools/fs/safety.d.ts +9 -9
  382. package/dist/tools/fs/search.d.ts +6 -6
  383. package/dist/tools/fs/shell-read-check.js +6 -1
  384. package/dist/tools/fs/shell-read-judge.d.ts +4 -0
  385. package/dist/tools/fs/shell-read-judge.js +5 -2
  386. package/dist/tools/loop-tick.d.ts +2 -2
  387. package/dist/tools/scheduler-tools.js +5 -1
  388. package/dist/tools/task-list.d.ts +7 -7
  389. package/dist/tools/task-list.js +5 -1
  390. package/dist/tools/todo.js +2 -1
  391. package/dist/tools/web.d.ts +1 -1
  392. package/dist/tools/web.js +40 -37
  393. package/dist/tools/worktree.d.ts +3 -3
  394. package/dist/tools/worktree.js +3 -1
  395. package/package.json +1 -1
  396. package/test/export-surface.snapshot.json +5756 -5638
  397. package/wire-changes/core@7.55.0.json +3318 -0
  398. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +0 -39
  399. package/dist/core/store-contracts/session-store-incarnation-contract.js +0 -95
@@ -178,11 +178,27 @@ export const SAFETY_MERGE_CONFORMANCE_CORPUS = [
178
178
  {
179
179
  name: "inert-deny-entry-removed-safe",
180
180
  intent: "removing a deny entry the grammar reads that names nothing (a wildcard peer, a `__` name with no protocol prefix) relaxes nothing — the comparison reads the EFFECTIVE entries on both sides",
181
- prior: { toolDeny: ["Bash", "mcp__*", "github__x"] },
181
+ prior: { toolDeny: ["Bash", "mcp__git*", "github__x"] },
182
182
  next: { toolDeny: ["Bash"] },
183
183
  loosens: false,
184
184
  reasons: [],
185
185
  },
186
+ {
187
+ name: "whole-protocol-deny-removed-loosened",
188
+ intent: "`mcp__*` and `mcp__*__*` in a deny list name every tool of every MCP server: removing either lifts a real deny, unlike a wildcard in another peer segment (which names nothing)",
189
+ prior: { toolDeny: ["Bash", "mcp__*"] },
190
+ next: { toolDeny: ["Bash"] },
191
+ loosens: true,
192
+ reasons: ['removed toolDeny "mcp__*"'],
193
+ },
194
+ {
195
+ name: "whole-protocol-deny-both-spellings-removed-loosened",
196
+ intent: "each whole-protocol spelling is its own deny entry: dropping one while keeping the other still removes an entry the base layer wrote",
197
+ prior: { toolDeny: ["mcp__*", "mcp__*__*"] },
198
+ next: { toolDeny: ["mcp__*"] },
199
+ loosens: true,
200
+ reasons: ['removed toolDeny "mcp__*__*"'],
201
+ },
186
202
  {
187
203
  name: "unreadable-deny-prior-loosened",
188
204
  intent: "rules whose toolDeny holds an entry no rule grammar reads (an empty entry) cannot be read and refuse every call — an override that can be read loosens them",
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The conservative value-sameness proof the park mint reads when it decides whether a fidelity projection
3
+ * moved a value, and whether a policy's rewrite names the very value about to be filed. A leaf: no imports.
4
+ */
5
+ /**
6
+ * CONSERVATIVE "these two are provably the same value" for deciding whether a
7
+ * {@link import("./checkpoint-store.js").encodeAtFidelity} projection moved anything, and whether a policy's
8
+ * rewrite names the very value about to be filed.
9
+ *
10
+ * Deliberately NOT the binding hash, which is the wrong instrument for this question in two directions:
11
+ * it folds `-0` onto `0` (a real projection change the policy may care about, invisible to the hash), and
12
+ * it is a digest, so a collision would read as sameness. `Object.is` at the leaves keeps the `-0`
13
+ * distinction; comparing own-key SETS keeps the dropped `undefined`-valued key visible.
14
+ *
15
+ * Conservative on purpose — the answer it must never give wrongly is TRUE, because both callers read
16
+ * TRUE as "nothing moved / the policy named this very value" and act by filing. So sameness has to be
17
+ * PROVED, not merely un-disproved:
18
+ * - the PROTOTYPES must match and be the plain-object or array one. `[object Object]` is not proof of
19
+ * a plain object — a class instance and a null-prototype object both report it, and a tool executing
20
+ * the arguments can observe inherited behaviour. (Neither input to the projection comparison can be
21
+ * such a value: `structuredClone` and `JSON.parse` both produce plain data. A policy's rewrite can.)
22
+ * - own keys are compared with `Reflect.ownKeys` and POSITIONALLY, so a SYMBOL key or a non-enumerable
23
+ * property on one side is a difference rather than an invisible extra, and so is a different order
24
+ * (which `Object.keys`, `JSON.stringify` and any tool iterating its arguments can see).
25
+ * - properties are compared through their DESCRIPTORS — every flag, so a frozen object is not the
26
+ * mutable value a JSON decode produces — and an accessor on either side answers FALSE without being
27
+ * read: an approval decision must not be made by running deployment code, and what a getter returns
28
+ * need not be the same twice.
29
+ * - reference TOPOLOGY counts: two properties that share one object are not two properties holding
30
+ * equal objects, and a JSON round trip is precisely what turns the first into the second.
31
+ * - what it CANNOT prove on its own is that the value it inspected answers ordinary reads the way its
32
+ * descriptors did — a Proxy reports the target's descriptors and can return anything from a `get`.
33
+ * A caller comparing a value it did not construct must first establish that it is an inert data
34
+ * graph (the capture clone does exactly that, and refuses a Proxy outright).
35
+ * - any brand outside plain object/array that is not already reference-identical answers FALSE (a Date
36
+ * is never the string a JSON codec leaves behind, a Map is never the `{}`), and so does any
37
+ * inspection that throws.
38
+ * An uncertain answer therefore costs a policy consultation or a park, never a silently filed value.
39
+ */
40
+ export declare function samePlainValue(a: unknown, b: unknown): boolean;
@@ -0,0 +1,49 @@
1
+ const MAX_SAME_VALUE_DEPTH = 256;
2
+ export function samePlainValue(a, b) {
3
+ return sameValueAt(a, b, 0, new Map(), new Map());
4
+ }
5
+ function sameValueAt(a, b, depth, aToB, bToA) {
6
+ if (Object.is(a, b))
7
+ return true;
8
+ if (depth >= MAX_SAME_VALUE_DEPTH)
9
+ return false;
10
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object")
11
+ return false;
12
+ try {
13
+ const pairedWithA = aToB.get(a);
14
+ const pairedWithB = bToA.get(b);
15
+ if (pairedWithA !== undefined || pairedWithB !== undefined)
16
+ return pairedWithA === b && pairedWithB === a;
17
+ aToB.set(a, b);
18
+ bToA.set(b, a);
19
+ const proto = Object.getPrototypeOf(a);
20
+ if (proto !== Object.getPrototypeOf(b))
21
+ return false;
22
+ if (proto !== Object.prototype && proto !== Array.prototype)
23
+ return false;
24
+ if (Object.isExtensible(a) !== Object.isExtensible(b))
25
+ return false;
26
+ const ka = Reflect.ownKeys(a);
27
+ const kb = Reflect.ownKeys(b);
28
+ if (ka.length !== kb.length)
29
+ return false;
30
+ for (const [i, k] of ka.entries()) {
31
+ if (k !== kb[i])
32
+ return false;
33
+ const da = Object.getOwnPropertyDescriptor(a, k);
34
+ const db = Object.getOwnPropertyDescriptor(b, k);
35
+ if (da === undefined || db === undefined)
36
+ return false;
37
+ if (!("value" in da) || !("value" in db))
38
+ return false;
39
+ if (da.enumerable !== db.enumerable || da.writable !== db.writable || da.configurable !== db.configurable)
40
+ return false;
41
+ if (!sameValueAt(da.value, db.value, depth + 1, aToB, bToA))
42
+ return false;
43
+ }
44
+ return true;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
@@ -111,7 +111,7 @@ export interface ScheduledTaskSummary {
111
111
  /** 下次触发的 epoch ms(cron/at 可算;daemon 提供)。 */
112
112
  nextRunMs?: number;
113
113
  /** 执行形态回显({@link ScheduledIntent.mode})。ScheduleWakeup 的 stop:true 靠它定位要取消的 pending
114
- * wakeup(cron/task 意图不受 stop 影响——CC 206 D9 语义)。老 backend 不回 = undefined(工具壳以
114
+ * wakeup(cron/task 意图不受 stop 影响)。老 backend 不回 = undefined(工具壳以
115
115
  * label 兜底过滤)。 */
116
116
  mode?: "task" | "session-wakeup";
117
117
  /** [c209-D2]:生命周期层级回显({@link ScheduledIntent.lifetime})——缺了它,任务创建后就再无
@@ -23,7 +23,7 @@ import type { ToolPolicy } from "./tool-policy.js";
23
23
  export declare function agentConfigDenyPatterns(): readonly string[];
24
24
  /**
25
25
  * RECOMMENDED default pattern set for {@link createSensitivePathPolicy} (additive export).
26
- * NO upstream verbatim anchor exists (cc-anchors/2.1.209 carries no sensitive/dangerous file-pattern
26
+ * NO upstream anchor exists (the anchor set carries no sensitive/dangerous file-pattern
27
27
  * material), so this is an ENGINEERING-CONSENSUS set — each line states its tradeoff. The factory's
28
28
  * default behavior is unchanged (no implicit default list; wiring this set is the caller's opt-in:
29
29
  * `createSensitivePathPolicy({ patterns: RECOMMENDED_SENSITIVE_PATTERNS, … })`).
@@ -134,7 +134,7 @@ export function createSensitivePathPolicy(opts) {
134
134
  const fold = CASE_INSENSITIVE_FS ? "ascii" : "none";
135
135
  const lower = (segs) => (CASE_INSENSITIVE_FS ? segs.map((seg) => seg.replace(/[A-Z]/g, (c) => c.toLowerCase())) : segs);
136
136
  const anchors = [configRoot, ...(env === undefined ? [] : await canonicalDataRootKeys(env, [configRoot], signal))].map((root) => lower(agentConfigDirSegments(root)));
137
- return configRows.map(({ raw, tail }) => ({ ...(compileSegmentPattern(tail, fold) ?? { raw, segments: [], foldAscii: CASE_INSENSITIVE_FS }), raw, anchors, rootedOnly: true }));
137
+ return configRows.map(({ raw, tail }) => ({ ...(compileSegmentPattern(tail, fold) ?? { raw, segments: [], fold }), raw, anchors, rootedOnly: true }));
138
138
  };
139
139
  const rowsOf = async (env, signal) => [...guardRows, ...(await configTwins(env, signal)).map(rowOf)];
140
140
  const unstampedRoots = Object.freeze(dataRootSpellings(undefined));
@@ -8,10 +8,12 @@
8
8
  * must match this InMemory one byte-for-byte — a cross-backend equivalence test is the contract).
9
9
  *
10
10
  * Safety model:
11
- * - **tighten-only by default**: a NORMAL write may only ADD denies / NARROW allowlists / NARROW allowDirs. A
12
- * write that LOOSENS (removes a deny, widens an allowlist/allowDirs) is REFUSED unless it is an OPERATOR
13
- * write. core has NO role system — `operator` is a boolean the caller FREEZES from a verified principal
14
- * (mirrors `steer.trusted`); core never adjudicates operator-ness, it only honors the flag.
11
+ * - **tighten-only below what an operator wrote**: a NORMAL write may not loosen the row's operator part
12
+ * ({@link StoredSessionRules.operatorRules}: what the last operator write set; absent, the whole row) — above it, it
13
+ * may take back what normal writes added (a deny, its own allowlist or allowDirs narrowing). The one `putRules` is
14
+ * the write port and the delete port alike: a rule leaves every gate that reads the row when the row stops holding it.
15
+ * core has NO role system — `operator` is a boolean the caller FREEZES from a verified principal (mirrors
16
+ * `steer.trusted`); core never adjudicates operator-ness, it only honors the flag.
15
17
  * - **CAS rev** (optimistic concurrency): a `putRules` with a stale `expectedRev` is rejected, so two replicas
16
18
  * can't lose an update (mirrors the checkpoint store's `resolve` OCC).
17
19
  * - **one grammar**: the two rule buckets hold rule ENTRIES — the grammar every entry list reads, through its one reader
@@ -60,10 +62,25 @@ export type SessionRuleEntryField = {
60
62
  * `*`, one more name no tool carried, which voided nothing beside it;
61
63
  * - 1, the one entry reader taking its tool name from the lenient rule parser (trimmed before a `(`, any character but a
62
64
  * blank, a parenthesis or a control / format character);
63
- * - 2, the rule syntax's tool-name head (`ruleSpellingOf`): a name is read as written, in the characters names are minted in.
65
+ * - 2, the rule syntax's tool-name head (`ruleSpellingOf`): a name is read as written, in the characters names are minted in;
66
+ * - 3, the whole-protocol names (`mcp__*`, `mcp__*__*`, see `wholeProtocolOf`) read as a deny rule in `toolDeny` — every tool of the
67
+ * protocol — where grammar 2 read them as an entry that names nothing. An engine of grammar 2 given a row written in grammar 3
68
+ * refuses it ({@link grammarOf}) rather than read the deny as nothing;
69
+ * - 4, the delegation tool's type rules (`Agent(<subagent_type>)`) read as a rule on the agent type the call runs — where grammar 3
70
+ * read them as an entry with no rule form (and its write judge refused one) — and a Bash pattern (`Bash(git * main)`, a star
71
+ * anywhere but the two trailing markers) reads as the `pattern` match form — where grammar 3 had no form for it — so an engine of
72
+ * grammar 3 given a row written in grammar 4 refuses it rather than read an `Agent(…)` or `Bash(<pattern>)` deny as nothing.
64
73
  * An entry this grammar reads as no rule in a row an earlier grammar wrote reads as `earlier_grammar` ({@link compileSessionRuleBucket}).
74
+ *
75
+ * The persisted rule store stamps the same number on each bucket it writes (and the rule-sync wire on each state it sends):
76
+ * a stored rule's text is a `Tool(content)` entry read by the same parser, so every revision of what such a text means is a
77
+ * revision counted here. A revision that touches only the entry list (a tool-name head, a protocol name) moves the persisted
78
+ * stamp too, and an engine before it then refuses a bucket it could have read — the refusing side. The persisted store keeps
79
+ * no earlier reading: its rows are read in this grammar whatever stamp at or below it they carry, so a revision that changes
80
+ * what an existing persisted rule text means owes that store a reading of its earlier stamps, as {@link earlierGrammarReads}
81
+ * is for entries. Every stamp is judged by {@link readRuleGrammarStamp}.
65
82
  */
66
- export declare const SESSION_RULES_GRAMMAR = 2;
83
+ export declare const SESSION_RULES_GRAMMAR = 4;
67
84
  /** {@link SessionPermissionRules} plus the monotonic `rev` the store stamps (the OCC key). */
68
85
  export interface StoredSessionRules extends SessionPermissionRules {
69
86
  rev: number;
@@ -75,18 +92,31 @@ export interface StoredSessionRules extends SessionPermissionRules {
75
92
  /** The rule grammar the row's entries are read in ({@link SESSION_RULES_GRAMMAR}), stamped by the write judge. Absent =
76
93
  * a row stored before the stamp: grammar 0. */
77
94
  grammar?: number;
95
+ /** The row's OPERATOR PART: the buckets (and their grammar) the last operator write set — the line a non-operator write
96
+ * may not loosen ({@link judgeSessionRulesWrite}). Minted by the write judge alone, and present only once a non-operator
97
+ * write stamped the row (it carries the part it was judged against; over no row, the part of the value it wrote when that
98
+ * value is a stored row — a rev from 1 — else an empty part). ABSENT = the whole row is the operator's: an operator write, and a row stored before
99
+ * the member, read fail-closed. Never enforced itself — the row's buckets are what every gate reads. */
100
+ operatorRules?: SessionPermissionRules & {
101
+ grammar: number;
102
+ };
78
103
  }
79
- /** A rules value as the shape reading hands it on ({@link readSessionRulesShape}): its buckets, and the grammar its entries
80
- * are read in — resolved, so a store that serves it on beside its own counters serves the row's grammar with it. */
104
+ /** A rules value as the shape reading hands it on ({@link readSessionRulesShape}): its buckets, the grammar its entries
105
+ * are read in — resolved, so a store that serves it on beside its own counters serves the row's grammar with it — and the
106
+ * row's operator part when it carries one ({@link StoredSessionRules.operatorRules}). */
81
107
  export type SessionRulesInGrammar = SessionPermissionRules & {
82
108
  grammar: number;
109
+ operatorRules?: SessionPermissionRules & {
110
+ grammar: number;
111
+ };
83
112
  };
84
113
  /** Options for {@link SessionPolicyStore.putRules}. */
85
114
  export interface PutRulesOptions {
86
115
  /** CAS: reject the write if the stored `rev` is not this (optimistic concurrency). Omit to skip the check. */
87
116
  expectedRev?: number;
88
- /** When true, this write MAY loosen the rules (remove a deny / widen an allowlist or allowDirs). A NORMAL
89
- * (non-operator) write is TIGHTEN-ONLY. core does NOT verify operator-ness — the caller (service) FREEZES
117
+ /** When true, this write MAY loosen the rules (remove a deny / widen an allowlist or allowDirs), and what it writes is
118
+ * the operator's whole. A NORMAL (non-operator) write may not loosen the row's operator part
119
+ * ({@link StoredSessionRules.operatorRules}). core does NOT verify operator-ness — the caller (service) FREEZES
90
120
  * this from a verified, role-checked principal (mirrors `steer.trusted`). */
91
121
  operator?: boolean;
92
122
  }
@@ -105,7 +135,7 @@ export interface SessionRuleEntryRefusal extends AllowEntryInertDetail {
105
135
  reason: AllowEntryInertReason;
106
136
  }
107
137
  /** Typed error from {@link SessionPolicyStore.putRules}. `conflict` = stale `expectedRev`; `loosen_forbidden`
108
- * = a non-operator write tried to relax the rules; `corrupt` = a backend could not read what it stored (a row, or the
138
+ * = a non-operator write tried to relax what an operator wrote; `corrupt` = a backend could not read what it stored (a row, or the
109
139
  * erase-surviving rev mark of {@link SessionPolicyStore.deleteBySession}) and refuses rather than read it as fewer rules or
110
140
  * stamp an unproven rev; `invalid_shape` = the rules value is not an object of buckets, each a list of strings
111
141
  * ({@link readSessionRulesShape}); `inert_entry` = a bucket holds an entry the entry reader reads as nothing. `detail` is
@@ -142,8 +172,9 @@ export interface SessionRulesRecord {
142
172
  export interface SessionPolicyStore {
143
173
  getRules(sessionId: string, principal?: string): Promise<StoredSessionRules | null>;
144
174
  /** Write the rules for a key through the one write judge ({@link judgeSessionRulesWrite}): CAS on `expectedRev`, the
145
- * shape, every entry's reading (operator writes included), then, for a non-operator write over an existing row, the
146
- * tighten-only check — each refusal a {@link SessionPolicyError}, and nothing is written. */
175
+ * shape, every entry's reading (operator writes included), then, for a non-operator write, the check against the row's
176
+ * operator part — each refusal a {@link SessionPolicyError}, and nothing is written. The same call is the delete port: a
177
+ * value without an entry takes it out of every gate that reads the row. */
147
178
  putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
148
179
  /**
149
180
  * 2c session-sync: every (principal, rules) record for `sessionId` across ALL principals — `getRules`
@@ -193,13 +224,33 @@ export interface SessionPolicyStore {
193
224
  * backend should keep `allowDirs` to real directories; a future symlink-aware write-time canonicalize closes it.
194
225
  */
195
226
  declare function lexWithin(root: string, p: string): boolean;
227
+ /**
228
+ * THE ONE JUDGE of a rule grammar stamp, whichever store carries it — a session-rules row ({@link readSessionRulesShape}),
229
+ * the persisted rule store's bucket and the rule-sync wire: the grammar the stamped rules are read in, or the fault that
230
+ * makes them unreadable. `absent` is what a missing stamp reads as (each store says what its unstamped rules were written
231
+ * in). A stamp is a whole number no greater than {@link SESSION_RULES_GRAMMAR}: a later grammar is one this engine cannot
232
+ * read the rules in, and reading them in an older one could grant what their writer refused or drop a refusal it cannot
233
+ * name — so they are refused WHOLE (`later`), never read in part. `rules` names the stamped rules in the sentence, `held`
234
+ * what they hold (`entries`, `rows`).
235
+ */
236
+ export declare function readRuleGrammarStamp(stamp: unknown, absent: number, words: {
237
+ readonly rules: string;
238
+ readonly held: string;
239
+ }): {
240
+ grammar: number;
241
+ } | {
242
+ got: string;
243
+ sentence: string;
244
+ later?: true;
245
+ };
196
246
  /**
197
247
  * THE ONE SHAPE READING of a rules value — what every station that takes one reads it through: the write (incoming rules),
198
248
  * 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
199
249
  * object whose keys are buckets ({@link SessionPermissionRules}); each bucket present is a list of strings (`allowDirs`: of
200
250
  * 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,
201
251
  * in the grammar the value is written in ({@link grammarOf}), handed on as `grammar`. The store's counters (`rev`, `gen`) are
202
- * passed over, and a key holding `undefined` holds nothing. Anything else is a fault, named by key and by TYPE, never by value —
252
+ * passed over, the row's operator part is read as rules of its own and handed on ({@link readOperatorPart}), and a key
253
+ * holding `undefined` holds nothing. Anything else is a fault, named by key and by TYPE, never by value —
203
254
  * a bare string is never one rule and never its characters (`"rm"` spread into `["r","m"]`), and a key outside the buckets is
204
255
  * never dropped (a dropped deny is a deny nobody enforces).
205
256
  */
@@ -277,14 +328,18 @@ export declare function earlierGrammarAnnouncer(sink: ((entries: readonly string
277
328
  * the rules to stamp, or a {@link SessionPolicyError}, in this order — `conflict` (a stale `expectedRev` against `prior`'s rev),
278
329
  * `invalid_shape` ({@link normalizeRules}), `inert_entry` (an entry that reads as nothing in the grammar the rules are written
279
330
  * in; operator writes included — a rule that names nothing guards nothing whoever writes it), then, for a non-operator write
280
- * over an existing row, `loosen_forbidden` ({@link loosenReasons}) — or `corrupt` when that prior cannot be read as rules,
281
- * which the comparison never reads as fewer. The rules to stamp carry `grammar` = {@link SESSION_RULES_GRAMMAR}: what passed
282
- * the judge means the same in this engine's grammar, and THIS is the one place the stamp is minted, so every store that
283
- * stores what the judge returns keeps it. `mountedToolNames` = the store's declaration ({@link SessionPolicyStoreOptions.mountedToolNames}).
331
+ * over a row, `loosen_forbidden` ({@link loosenReasons}) against the row's OPERATOR PART ({@link StoredSessionRules.operatorRules};
332
+ * absent, the whole row) — or `corrupt` when that prior cannot be read as rules, which the comparison never reads as fewer.
333
+ * Over no row there is no rev to compare and no tighten gate, and a judge called with `prior = null` (a host's pre-read of a
334
+ * value's shape and entries) answers only those two content refusals. Below what an operator wrote nothing moves; above it,
335
+ * a non-operator takes back what non-operator writes added. The
336
+ * rules to stamp carry `grammar` = {@link SESSION_RULES_GRAMMAR} — what passed the judge means the same in this engine's
337
+ * grammar — and, from a non-operator write, the operator part it was judged against: over no row, the value's own when it is a
338
+ * stored row handed in ({@link isStoredRow}), else an empty one (an operator write's whole value is the operator's, so it
339
+ * carries none). THIS is the one place both stamps are minted, so every store that stores what the judge
340
+ * returns keeps them. `mountedToolNames` = the store's declaration ({@link SessionPolicyStoreOptions.mountedToolNames}).
284
341
  */
285
- export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null, rules: unknown, opts: PutRulesOptions | undefined, mountedToolNames?: ReadonlySet<string>): SessionPermissionRules & {
286
- grammar: number;
287
- };
342
+ export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null, rules: unknown, opts: PutRulesOptions | undefined, mountedToolNames?: ReadonlySet<string>): SessionRulesInGrammar;
288
343
  /**
289
344
  * Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op), over the EFFECTIVE
290
345
  * entries of each rule bucket — the list as the run-time layer enforces it ({@link effectiveEntries}). Rules whose toolDeny holds
@@ -321,7 +376,7 @@ export declare function continuesLineage(a: {
321
376
  }): boolean;
322
377
  /**
323
378
  * In-process default {@link SessionPolicyStore} (single-replica). A deployment that needs cross-replica
324
- * durability supplies its own backend with the SAME CAS-rev + tighten-only semantics. Single-threaded JS makes
379
+ * durability supplies its own backend with the SAME CAS-rev + write-judge semantics. Single-threaded JS makes
325
380
  * the read-check-write trivially atomic here; a durable backend folds it into a CAS WHERE clause.
326
381
  */
327
382
  export declare class InMemorySessionPolicyStore implements SessionPolicyStore {
@@ -9,7 +9,8 @@ const SESSION_RULE_BUCKETS = { toolAllow: "allow", toolDeny: "deny", allowDirs:
9
9
  const BUCKETS = Object.keys(SESSION_RULE_BUCKETS);
10
10
  const ENTRY_FIELDS = BUCKETS.filter((f) => SESSION_RULE_BUCKETS[f] !== "dirs");
11
11
  const STORE_STAMPS = new Set(["rev", "gen", "grammar"]);
12
- export const SESSION_RULES_GRAMMAR = 2;
12
+ const OPERATOR_PART = "operatorRules";
13
+ export const SESSION_RULES_GRAMMAR = 4;
13
14
  export class SessionPolicyError extends Error {
14
15
  code;
15
16
  detail;
@@ -53,18 +54,26 @@ function lexWithin(root, p) {
53
54
  return true;
54
55
  return np === nr || np.startsWith(nr + "/");
55
56
  }
57
+ function isStoredRow(value) {
58
+ const rev = value.rev;
59
+ return typeof rev === "number" && Number.isInteger(rev) && rev >= 1;
60
+ }
56
61
  function grammarOf(value) {
57
62
  const stamp = value.grammar;
63
+ return readRuleGrammarStamp(stamp, stamp === undefined && isStoredRow(value) ? 0 : SESSION_RULES_GRAMMAR, { rules: "session rules", held: "entries" });
64
+ }
65
+ export function readRuleGrammarStamp(stamp, absent, words) {
58
66
  if (stamp === undefined)
59
- return { grammar: value.rev !== undefined ? 0 : SESSION_RULES_GRAMMAR };
67
+ return { grammar: absent };
60
68
  if (typeof stamp !== "number" || !Number.isInteger(stamp) || stamp < 0) {
61
69
  const got = typeof stamp === "number" ? "a number that is not a whole number of 0 or more" : valueTypeLabel(stamp);
62
- 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})` };
70
+ return { got, sentence: `${words.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})` };
63
71
  }
64
72
  if (stamp > SESSION_RULES_GRAMMAR) {
65
73
  return {
66
74
  got: `${stamp}, a grammar newer than this engine's (${SESSION_RULES_GRAMMAR})`,
67
- 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`,
75
+ sentence: `${words.rules} were written in rule grammar ${stamp}, newer than this engine reads (grammar ${SESSION_RULES_GRAMMAR}) — refusing rather than read their ${words.held} in an older grammar; an engine that reads grammar ${stamp} reads them`,
76
+ later: true,
68
77
  };
69
78
  }
70
79
  return { grammar: stamp };
@@ -82,6 +91,13 @@ export function readSessionRulesShape(value) {
82
91
  for (const [key, v] of Object.entries(value)) {
83
92
  if (STORE_STAMPS.has(key) || v === undefined)
84
93
  continue;
94
+ if (key === OPERATOR_PART) {
95
+ const part = readOperatorPart(v);
96
+ if ("fault" in part)
97
+ return part;
98
+ rules.operatorRules = part.rules;
99
+ continue;
100
+ }
85
101
  if (!Object.hasOwn(SESSION_RULE_BUCKETS, key)) {
86
102
  return fault(key, "not a rule bucket", `session rules hold ${JSON.stringify(key)}, which is not a rule bucket — the buckets are ${BUCKETS.join(", ")}, and a shell command rule is a toolDeny / toolAllow entry written Bash(<command>) (Bash(rm:*) matches a command whose first word is rm)`);
87
103
  }
@@ -100,6 +116,20 @@ export function readSessionRulesShape(value) {
100
116
  }
101
117
  return { rules };
102
118
  }
119
+ function readOperatorPart(value) {
120
+ const read = readSessionRulesShape(value);
121
+ if ("fault" in read) {
122
+ const field = read.fault.field === "" ? OPERATOR_PART : `${OPERATOR_PART}.${read.fault.field}`;
123
+ return { fault: { field, got: read.fault.got }, sentence: `the session rules' operator part: ${read.sentence}` };
124
+ }
125
+ const { operatorRules: nested, ...part } = read.rules;
126
+ const unstamped = value.grammar === undefined;
127
+ if (unstamped || nested !== undefined) {
128
+ const got = unstamped ? "rules without a grammar stamp" : "an operator part of its own";
129
+ return { fault: { field: OPERATOR_PART, got }, sentence: `session rules carry an operator part holding ${got} — the part is the rules the last operator write set, stamped with their grammar by the write judge` };
130
+ }
131
+ return { rules: part };
132
+ }
103
133
  export function normalizeRules(rules) {
104
134
  return canonicalRules(shapeOrRefuse(rules));
105
135
  }
@@ -284,17 +314,29 @@ export function judgeSessionRulesWrite(prior, rules, opts, mountedToolNames = NO
284
314
  const read = shapeOrRefuse(rules);
285
315
  const clean = canonicalRules(read);
286
316
  assertEntriesRead(read, mountedToolNames);
287
- if (prior !== null && opts?.operator !== true) {
288
- const stored = readSessionRulesShape(prior);
289
- if ("fault" in stored) {
290
- throw new SessionPolicyError("corrupt", `the stored session rules cannot be read as rules (${stored.sentence}) — refusing to judge a non-operator write against them as fewer rules; an operator write replaces them`);
291
- }
292
- const loosens = loosenReasons(stored.rules, clean, mountedToolNames);
293
- if (loosens.length > 0) {
294
- throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may only tighten; refused: ${loosens.join("; ")}`);
295
- }
317
+ if (opts?.operator === true)
318
+ return { ...clean, grammar: SESSION_RULES_GRAMMAR };
319
+ if (prior === null) {
320
+ return { ...clean, grammar: SESSION_RULES_GRAMMAR, operatorRules: isStoredRow(rules) ? operatorPartOf(read) : { grammar: SESSION_RULES_GRAMMAR } };
321
+ }
322
+ const operatorRules = operatorPartOf(readStoredOrRefuse(prior));
323
+ const loosens = loosenReasons(operatorRules, clean, mountedToolNames);
324
+ if (loosens.length > 0) {
325
+ throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may not loosen what an operator wrote; refused: ${loosens.join("; ")}`);
326
+ }
327
+ return { ...clean, grammar: SESSION_RULES_GRAMMAR, operatorRules };
328
+ }
329
+ function operatorPartOf(read) {
330
+ const { operatorRules, ...whole } = read;
331
+ const part = operatorRules ?? whole;
332
+ return { ...canonicalRules(part), grammar: part.grammar };
333
+ }
334
+ function readStoredOrRefuse(prior) {
335
+ const stored = readSessionRulesShape(prior);
336
+ if ("fault" in stored) {
337
+ throw new SessionPolicyError("corrupt", `the stored session rules cannot be read as rules (${stored.sentence}) — refusing to judge a non-operator write against them as fewer rules; an operator write replaces them`);
296
338
  }
297
- return { ...clean, grammar: SESSION_RULES_GRAMMAR };
339
+ return stored.rules;
298
340
  }
299
341
  export function loosenReasons(priorValue, nextValue, mountedToolNames = NO_DECLARED_TOOLS) {
300
342
  const shapeOf = (side, value) => {
@@ -92,13 +92,13 @@ export interface ReconcileReport {
92
92
  */
93
93
  export declare function findOrphanToolCalls(messages: AgentMessage[], suspendedBatch?: ReadonlySet<string>): OrphanToolCall[];
94
94
  /**
95
- * backlog #389 伴生 (D-2) — CC 2.1.223's interruption markers, VERBATIM (`$U`/`CR` @ `CC:151120-151121`,
95
+ * backlog #389 伴生 (D-2) — the reference's interruption markers, as is (
96
96
  * minted as a USER message by `Jce` @ `CC:640141-640154`). CC mints one on every abort whose reason is
97
97
  * outside `{"interrupt","refusal-fallback-edit"}` — and the interactive Esc / remote cancel, which is
98
98
  * what `TaskStream.interrupt()` corresponds to, is precisely on the minting side (`CC:1033698-1033733`).
99
99
  * The suppressed reason is the one case where the user's own replacement message is already the context.
100
100
  *
101
- * Taken verbatim rather than reworded: this string is an INPUT to later reasoning in CC (its own
101
+ * Taken as is rather than reworded: this string is an INPUT to later reasoning in CC (its own
102
102
  * "interrupted then immediately retried the same action" rule reads it back), and the constitution's
103
103
  * standing rule is that a question CC has answered is answered in CC's form.
104
104
  */
@@ -1,4 +1,4 @@
1
- import { type SessionPlacementRecord, type SessionRepo } from "../internal/harness.js";
1
+ import { type Session, type SessionHead, type SessionPlacementRecord, type SessionRepo } from "../internal/harness.js";
2
2
  import { type AcquiredSession, type PlacedSessionRow, type SessionPlacement, type SessionStore, type SessionStoreSummary } from "./session.js";
3
3
  import type { StoreDurability } from "./checkpoint-store.js";
4
4
  import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
@@ -117,29 +117,27 @@ export declare class TtlSessionStore implements SessionStore {
117
117
  olderThanMs?: number;
118
118
  scope?: string;
119
119
  }) => Promise<PlacedSessionRow[]>;
120
+ /** The by-id head read ({@link SessionStore.headOf}): the repo's own, verbatim — the row is the repo's, never this cache's, and a read
121
+ * racing a delete answers either generation (it never waits on one). Present only when the backing repo answers it (absence means no
122
+ * by-id head read, and so no ownership pair for an authorizer). */
123
+ readonly headOf?: (sessionId: string) => Promise<SessionHead | undefined>;
124
+ /** The repo's head read as this store's own decisions read the row through it (the cache check, the existence check), held
125
+ * privately so the store's reading of its rows is the repo's whatever a host does with the face. */
126
+ private readonly repoHead?;
120
127
  private repo;
121
128
  private entries;
122
- /** Session-ownership rows (the multi-tenant admission capability face — see SessionStore.ownerOf).
123
- * Deliberately NOT part of `entries`: an evicted-but-durable session (`forget` / evict:"forget")
124
- * still EXISTS, and its ownership row outliving the cache entry is what keeps a live session from
125
- * reading as `undefined` (= re-claimable by anyone). Rows die only when the repo's history does. */
126
- private owners;
127
129
  /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) share one session. */
128
130
  private pending;
129
- /** Subagent transcript persistence — placed ids whose durable deletion FAILED: the
130
- * placed fact for the retry lives HERE, not on a retained cache entry, because the repo may have
131
- * torn down the shared session authority before its removal failed (the File repo closes the
132
- * append fd pre-rmSync) — a retained entry would serve a session whose log is closed while the
133
- * file still exists (append → log_closed, transcript revival broken until restart). The entry is
134
- * dropped instead, so a re-acquire replays a FRESH authority from disk; success clears the row. */
131
+ /** Subagent transcript persistence — placed ids whose durable deletion FAILED. Not a reading of placement: the row is that
132
+ * (see `rowIsPlaced`). It makes a later cold `release` read the row whatever the repo or the store's declaration, and it is never
133
+ * believed in the row's place: a read that faults deletes nothing. No cache entry is retained instead, because the repo may have torn down the shared session
134
+ * authority before its removal failed (the File repo closes the append fd pre-rmSync): a retained entry would serve a closed log. */
135
135
  private pendingPlacedDeletes;
136
- /** Subagent transcript persistence — placed deletions IN FLIGHT: acquire awaits the
137
- * settlement before resolving the id, because the repo's delete tears down the shared session
138
- * authority mid-flight — an acquire racing the await window could capture (or rebuild and then
139
- * lose) an authority the delete is about to close, and the caller would hold a session whose
140
- * first append answers log_closed while the file still exists. The gate is per-id and bounded by
141
- * the delete's own settlement (both outcomes release it). */
142
- private deletingPlaced;
136
+ /** Releases (and idle-sweep deletes) IN FLIGHT that may delete the row: acquire and register — the two that resolve or write the row —
137
+ * await the settlement, because the repo's delete tears down the shared session authority mid-flight — an acquire racing the await window could capture (or rebuild
138
+ * and then lose) an authority the delete is about to close, and the caller would hold a session whose first append answers
139
+ * log_closed while the file still exists. The gate is per-id and bounded by the release's own settlement (both outcomes release it). */
140
+ private releasing;
143
141
  /** Sessions pinned by a design/45 checkpoint — skipped by idle sweep until unpinned (B6/§5). */
144
142
  private pinned;
145
143
  private defaultTtlMs;
@@ -151,6 +149,10 @@ export declare class TtlSessionStore implements SessionStore {
151
149
  requireExisting?: boolean;
152
150
  placement?: SessionPlacement;
153
151
  }): Promise<AcquiredSession>;
152
+ /** Whether a cached view is still the occupant the row holds: the row's head, read in one read, names the same occupant as the cached
153
+ * session (`compareOccupants` — unknown is never the same). Over a repo with no head read there is nothing to read the row with, and
154
+ * the view is served as it is. */
155
+ private cachedViewStands;
154
156
  /** The placement admission ({@link admitSessionPlacement}) for this store, judged on every resolution path. The
155
157
  * partition declaration is a getter over this store's live field, so it is read where the admission judges the
156
158
  * mirror direction (after the request's own members, and only then) — never captured at construction. */
@@ -179,8 +181,34 @@ export declare class TtlSessionStore implements SessionStore {
179
181
  * fork capability mounts (it was INERT before — the durable repo had `fork` but the store never exposed it).
180
182
  */
181
183
  fork(sourceId: string, owner?: string | null): Promise<string | null>;
182
- /** Drop a session from the live set. With a durable repo (`evict: "forget"`) the history is kept. */
184
+ /** Drop a session from the live set. With a durable repo (`evict: "forget"`) the history is kept. A release that rejects leaves the
185
+ * session where it was: the store keeps no view of it and retries nothing on its own, and a placed row is found again by the
186
+ * partition-GC leg's `listPlaced` — except one whose header is damaged, which `listPlaced` skips and every release of it rejects
187
+ * (`corrupt_session`), so only manual cleanup removes it. */
183
188
  release(sessionId: string): Promise<void>;
189
+ /** Run a release that may delete the row while it holds the id: the gate is set before the first await, after any release already in
190
+ * flight for the id, and `acquire` / `register` answer after it settles — no caller captures an authority the delete is about to tear
191
+ * down (the repo closes the shared authority before its removal can fail), or writes the row mid-decision. */
192
+ private holdingId;
193
+ /** The deciding half of {@link release}, under its gate. A delete goes through the cached view only while the row still stands for it
194
+ * (`cachedViewStands`); with no view, or a view of an occupant the row no longer holds (another writer deleted the row, or deleted and
195
+ * created it again), the row decides — its placement read cold ({@link rowIsPlaced}) — and an unplaced row nobody here holds is left
196
+ * as it is. Placement obligation 4: a placed row's release is a REAL deletion whatever the evict policy (the joint reap deletes the
197
+ * agent row first and then calls this to end the transcript; a forget would strand it). A delete runs only on a read that answered
198
+ * — the head read with the view still standing, or the row's own placement: a read that faults deletes nothing, and the release
199
+ * rejects with it. A placed release that fails (a read or the delete) leaves the tombstone, so the retry reads the row again —
200
+ * never a retained cache entry, which could serve a closed log; an unplaced view's release under `evict: "delete"` leaves nothing,
201
+ * so its retry after a failed read resolves and deletes nothing. */
202
+ private releaseRow;
203
+ /** The repo's placement probe, where it has one. */
204
+ private placementProbe;
205
+ /** Whether an uncached id's row is worth reading for its placement: through the probe on a store that declares placements, and by
206
+ * either means for an id whose placed deletion failed here. */
207
+ private rowReadMatters;
208
+ /** Whether the row standing under `sessionId` is placed: the repo's placement probe where it has one, else the row's own metadata,
209
+ * read where {@link rowReadMatters}. Its answer retires the tombstone. A read that FAULTS answers nothing: it rejects (the release
210
+ * deletes nothing and fails with it), and the tombstone stays for the next release to read the row again. */
211
+ private rowIsPlaced;
184
212
  /** Drop only the cached view — the repo keeps the session regardless of `evictPolicy`, so a re-acquire
185
213
  * re-opens the SAME history (B-17: the reconcile retry / prepare-throw paths must never delete). Cost
186
214
  * on the default in-memory repo: ONE retained session object **per forgotten id** until process end
@@ -193,19 +221,41 @@ export declare class TtlSessionStore implements SessionStore {
193
221
  pin(sessionId: string): void;
194
222
  /** Release a design/45 pin (on resume or checkpoint expiry); the session resumes normal idle eviction. */
195
223
  unpin(sessionId: string): void;
196
- /** Multi-tenant admission capability (see the SessionStore contract for the three-state semantics
197
- * and the first-write-wins requirement — both are load-bearing for the deployment's owner gate). */
198
- ownerOf(sessionId: string): Promise<string | null | undefined>;
199
- /** First-write-wins idempotent upsert; a synchronous Map write is atomic per the contract. Makes no
200
- * authorization decision — the authorizer re-reads {@link ownerOf} and judges there. */
224
+ /** The owner write of the ownership pair (see the SessionStore contract): an id with no row gets one — an ordinary row recording
225
+ * `owner` in its creating write, through the repo's create (the repo makes the creation first-write-wins); an id whose row exists is
226
+ * left exactly as it is, whatever this call carries. Makes no authorization decision — the authorizer re-reads {@link headOf}. The
227
+ * row is not cached here: it is resolved like any other row on the next `acquire`. */
201
228
  register(sessionId: string, owner: string | null): Promise<void>;
202
- /** The session row's incarnation cell (see the SessionStore contract), read from the ROW through the
203
- * repo on every call — never from this cache: a cached view outlives a row another store over the
204
- * same repo deleted or recreated, and its copy would name the previous occupant. No row ⇒
205
- * `undefined`; any other repo failure rejects (a fault is not an answer). Never caches, never creates. */
206
- incarnationOf(sessionId: string): Promise<string | undefined>;
229
+ /** Whether the repo holds a row for `sessionId` — its head read where it has one (no replay of the row's history), else an open. A
230
+ * fault rejects; it is not an answer. */
231
+ private hasRow;
207
232
  /** Evict idle-expired sessions from the cache. Deletes durable history only when `evict: "delete"`. */
208
233
  sweep(now?: number): void;
209
234
  get size(): number;
210
235
  dispose(): void;
211
236
  }
237
+ /**
238
+ * design/110 (F4) — pick the fork point so a child does NOT inherit an in-flight (uncommitted) tool call.
239
+ *
240
+ * The agent loop commits the assistant turn (`message_end` → `appendMessage`) BEFORE it executes that turn's tool
241
+ * calls. So while the Fork tool's own `execute` runs, the source session's LEAF is the assistant message that carries
242
+ * the Fork tool-call (and any sibling parallel calls) with NO tool results behind it yet. Forking the whole log would
243
+ * copy that orphan call into the child, and the child's wake-time `reconcileInterruptedSession` would inject an
244
+ * `[INTERRUPTED]` result for it — polluting the inherited context ("I called Fork → it was interrupted").
245
+ *
246
+ * Detection: the latest assistant `message` on the leaf's chain (past the tool results behind it) has a `toolCall` block
247
+ * (the message shape — there is NO `toolCalls` field; tool calls are content blocks) that no tool result behind it answers —
248
+ * the loop's own reading of an open batch (`findUnansweredToolBatch`). When so, fork at the turn's PARENT (`position:"at"`
249
+ * includes the parent, excludes the assistant turn) — the correct fork semantics: "inherit my context up to, but not
250
+ * including, the spawn call". A settled leaf (a text-only turn, or a tool turn every call of which is answered), or an
251
+ * unsettled assistant that is somehow a root (no `parentId`, fail-safe), forks the whole log (`{}`).
252
+ *
253
+ * Exported at the package root: the bundled store's `fork` and any other store's `fork` are the same question and must give the same
254
+ * answer — a backend reads the source session (`SessionRepo.open`), asks this, and hands the answer to `SessionRepo.fork` with the id
255
+ * and owner it mints. Forking the whole log instead copies the in-flight turn. The walk is one of the parent-chain walks, so it takes the
256
+ * shared guard: a leaf on a loop of tool-result messages is refused `invalid_session` (the sentence of `getPathToRoot`) instead of followed.
257
+ */
258
+ export declare function settledForkOptions(source: Session): Promise<{
259
+ entryId?: string;
260
+ position?: "at";
261
+ }>;