@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
@@ -1,4 +1,4 @@
1
- /** Closed compliance profile set (CC 2.1.221 policy-limits profiles). */
1
+ /** Closed compliance profile set (policy-limits profiles). */
2
2
  export type ComplianceProfile = "hipaa" | "zdr";
3
3
  /** Closed capability set the ENGINE can veto end-to-end (see the module header for the entry-point
4
4
  * map — a capability is admitted here only with its full interception story). */
@@ -17,7 +17,7 @@ export interface CompliancePosture {
17
17
  additionalDenies?: readonly ComplianceCapability[];
18
18
  }
19
19
  /**
20
- * Builtin per-profile deny floors, mapped from the CC 2.1.221 policy-limits pair table onto the
20
+ * Builtin per-profile deny floors, mapped from the policy-limits pair table onto the
21
21
  * capabilities this engine manages: hipaa denies `allow_web_fetch` ⇒ `web_fetch`. (CC's
22
22
  * `allow_memory_sync` row maps onto the org-memory plane and joins this table together with the
23
23
  * memory-admission seam; CC has no MCP/workflow rows — those stay reachable via `additionalDenies`.)
@@ -10,7 +10,7 @@ import { type ContextWindowOverlay } from "./context-settings.js";
10
10
  */
11
11
  export declare const EDIT_FRACTION = 0.7;
12
12
  /**
13
- * CC-parity trigger geometry (verified against CC 2.1.207): the reactive autocompact trigger sits a
13
+ * Trigger geometry: the reactive autocompact trigger sits a
14
14
  * FIXED DISTANCE below the window, not at a fixed fraction —
15
15
  * effectiveWindow = autocompactWindow − min(maxOutputTokens, 20000) (CC `Nye`, cap `uMd`)
16
16
  * trigger = effectiveWindow − 13000 (CC `dSo`, buffer `rMd`)
@@ -18,7 +18,7 @@ export declare const EDIT_FRACTION = 0.7;
18
18
  * 167000 (83.5% of nominal), a 1M window at 967000.
19
19
  *
20
20
  * 1M DIVERGENCE, stated as fact rather than as an equivalence (anchoring re-check 2026-08-23): CC
21
- * carries a MODEL-DEFAULT autocompact-window table (2.1.223 `j3u` :242918 — `claude-sonnet-5`
21
+ * carries a MODEL-DEFAULT autocompact-window table (`claude-sonnet-5`
22
22
  * default 967000, and since 223 a surface dimension: `remote_cowork` / `local-agent` 500000), so a
23
23
  * 1M sonnet-5 gets 967000 as its window and 934000 as its trigger WITHOUT anyone configuring
24
24
  * anything. We ship no such table: a model declaring only `contextWindow: 1e6` triggers at 967000
@@ -52,14 +52,14 @@ export declare function contextEditFrontier(window: number): number;
52
52
  /**
53
53
  * The `RunnerDeps.microCompact.machine` vocabulary — ONE clearing machine, switchable:
54
54
  *
55
- * - `"cc"` (default): the CC 2.1.223 `yId` machine — keep 5, a ≥20000-token minimum-savings gate
55
+ * - `"cc"` (default): the reference's machine — keep 5, a ≥20000-token minimum-savings gate
56
56
  * (below it the pass is a byte-identical no-op), ONE deep clear of everything beyond the keep
57
57
  * window, the window base counts already-cleared/offloaded occurrences (they hold their keep
58
58
  * slots), and CC's plain marker bytes.
59
59
  * - `"off"`: no proactive frontier clearing; the machine gets its one shot at the blocking point
60
60
  * of the guard chain instead (trace trigger `"blocking"`).
61
61
  *
62
- * WHOLE-MACHINE CROSS-CHECK against the 2.1.250 corpus (design/374 slice 4 — the "the knob number
62
+ * WHOLE-MACHINE CROSS-CHECK against the reference corpus (design/374 slice 4 — the "the knob number
63
63
  * was copied, the machine was not" disease is checked by naming the machine's parts, not the one
64
64
  * number). CC's four load-bearing parts, each against its 250 coordinate, all verified present here:
65
65
  * ① the ≥20000-token minimum-savings gate — {@link MIN_CLEAR_SAVINGS_TOKENS} vs `Mnn`
@@ -70,7 +70,7 @@ export declare function contextEditFrontier(window: number): number;
70
70
  * re-resolved member-by-member off the 250 corpus, byte-identical (see that constant).
71
71
  * ③ cleared-but-recoverable — CC replaces the content with a POINTER, not a void
72
72
  * (`<persisted-output>Tool result saved to: {path}\n\nUse Read to view</persisted-output>`,
73
- * cli250.js:40044), which is what makes a keep window of 5 survivable at all. Ours is the same
73
+ * cli296.js:219492: "Tool result saved to: "), which is what makes a keep window of 5 survivable at all. Ours is the same
74
74
  * construction in this engine's carrier: the full text is offloaded first and the marker
75
75
  * carries the store ref ({@link ContextEditOptions.offload} → {@link clearOneResult}), read
76
76
  * back through ReadToolResult instead of a filesystem path (registered divergence D-9). The
@@ -92,8 +92,7 @@ export declare const MICRO_COMPACT_MACHINES: readonly ["off", "cc"];
92
92
  export type MicroCompactMachine = (typeof MICRO_COMPACT_MACHINES)[number];
93
93
  /**
94
94
  * How many most-recent CANDIDATE tool results keep their content when the stale-result pass runs:
95
- * 5 (CC 2.1.223 `uAp`; 2.1.250 `var k=5` beside the call site, cli250.js:58373, and 2.1.245 carries
96
- * the same value — zero drift across 245→250; same value again on CC 88's `timeBasedMCConfig`, i.e.
95
+ * 5 (the reference's hard-coded constant beside its call site; the same value in every corpus generation checked, i.e.
97
96
  * stable across all three corpus generations, and it is a hard-coded constant upstream, not a
98
97
  * setting). The #384 adjudication adopted 5 CONDITIONAL on the whole machine coming with it (20k
99
98
  * gate + deep clear + re-ordered backstops); the machine carries that condition (§3.5: the gate
@@ -88,7 +88,8 @@ export interface CrossBoundaryCheck {
88
88
  /** This call's obligation under the tree's state at this moment (the station reads it on the final arguments). */
89
89
  obligation(call: CrossBoundaryCall): CrossBoundaryObligation;
90
90
  /** The allow exit's rule, on the bytes that run and the party that decided: re-reads the obligation; a mandate the decider cannot
91
- * answer refuses; otherwise the off-target-read bit the call owes is joined and the session record is written before it runs. */
91
+ * answer refuses; otherwise the session record carrying the off-target-read bit the call owes is written, and the bit joins the
92
+ * tree, before it runs — a call the exit refuses joins nothing. */
92
93
  exit(call: CrossBoundaryCall, decidedBy: HostDecider): Promise<CrossBoundaryExitRefusal | undefined>;
93
94
  }
94
95
  /** The gate input's seat for {@link CrossBoundaryCheck}: present on a leg whose station is ENGAGED (a posture is declared, the leg runs
@@ -183,14 +184,19 @@ export declare function crossBoundaryPostureUndeclaredNotice(at: {
183
184
  readonly tree: CrossBoundaryTree;
184
185
  }): EngineNotice;
185
186
  /** Where a session record's write was owed: a leg's prepare, an exit before its call runs, a suspend before its row, a settle. */
186
- export type CrossBoundaryRecordPhase = "prepare" | "exit" | "suspend" | "settle"; /** The one mint of `config.cross_boundary_record_unwritten`: a session record that could not be written and refused nothing — on a tree
187
+ export type CrossBoundaryRecordPhase = "prepare" | "exit" | "suspend" | "settle";
188
+ /** The one mint of `config.cross_boundary_record_unwritten`: a session record that could not be written and refused nothing — on a tree
187
189
  * that is not governed (no judgment reads the record), or at a governed leg's prepare / settle (no call to refuse; the next leg then
188
- * reads the content after the newest record as off-target). A governed exit refuses its call and a governed suspend its row instead. */
190
+ * reads the content after the newest record as off-target). A governed exit refuses its call and a governed suspend its row instead.
191
+ * `catchUp`: an earlier record of the same write landed and the one that carries the tree's newer bits did not — on a governed tree
192
+ * that earlier record covers the content before it, so the sentence says the next leg may not route its device calls to a person.
193
+ * Minted once per state a leg could not write. */
189
194
  export declare function crossBoundaryRecordUnwrittenNotice(at: {
190
195
  readonly sessionId: string;
191
196
  readonly runId: string;
192
197
  readonly phase: CrossBoundaryRecordPhase;
193
198
  readonly governed: boolean;
199
+ readonly catchUp: boolean;
194
200
  readonly cause: string;
195
201
  }): EngineNotice;
196
202
  /** The face a protocol tool (an MCP tool, an A2A skill) carries for its JUDGED `toolAxes` entry (`readToolAxes`' copy): the empty leg
@@ -155,9 +155,11 @@ export function crossBoundaryRecordUnwrittenNotice(at) {
155
155
  return {
156
156
  code: "config.cross_boundary_record_unwritten",
157
157
  message: `The session's cross-boundary record could not be written at ${at.phase} (${at.cause.slice(0, 300)}) — ` +
158
- (at.governed
159
- ? "the tree is governed: the next leg on this session reads the content after its newest record as off-target, so its device calls are put to a person."
160
- : "the tree is not governed: no judgment reads the record, so its failure refused nothing; the tree's growth, or the leg's end, writes next."),
158
+ (!at.governed
159
+ ? "the tree is not governed: no judgment reads the record, so its failure refused nothing; the tree's growth, or the leg's end, writes next, and a failure of this same state is not reported again."
160
+ : at.catchUp
161
+ ? "the tree is governed, and an earlier record of this write landed: the catch-up record that carries the tree's newer bits has not been written. Until #2160 lands, the next leg on this session may read the content before that earlier record as covered and may not route its device calls to a person (KNOWN-LIMITS KL-469-c); the terminal shape is fail-closed."
162
+ : "the tree is governed: the next leg on this session reads the content after its newest record as off-target, so its device calls are put to a person."),
161
163
  detail: { sessionId: at.sessionId, runId: at.runId, phase: at.phase, governed: at.governed, cause: at.cause.slice(0, 300) },
162
164
  };
163
165
  }
@@ -1,6 +1,6 @@
1
1
  import { types } from "node:util";
2
2
  import { ASK_CLASSES, ASK_ORIGINS, isAskClass, isAskOrigin, isPersistedRuleMandate, PERSISTED_RULE_MANDATES } from "./ask-origin.js";
3
- import { AUTO_MODE_FALLBACK_CAUSES, isAutoModeFallbackCause } from "./auto-mode.js";
3
+ import { AUTO_MODE_FALLBACK_CAUSES, isAutoModeFallbackCause, isDenialLimitBound } from "./auto-mode.js";
4
4
  import { thrownText } from "./thrown-value.js";
5
5
  const ENGINE_REFUSALS = new WeakSet();
6
6
  export const refusedOutOfContract = (message) => {
@@ -62,7 +62,7 @@ const NESTED_CONTRACTS = [
62
62
  ];
63
63
  const nonNegativeInteger = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0;
64
64
  const NESTED_VALUE_FENCES = {
65
- denialLimitFallback: { consecutive: nonNegativeInteger, total: nonNegativeInteger, limit: (v) => v === "consecutive" || v === "total" },
65
+ denialLimitFallback: { consecutive: nonNegativeInteger, total: nonNegativeInteger, limit: isDenialLimitBound },
66
66
  probeCause: { code: (v) => typeof v === "string" && v.length > 0 },
67
67
  "probeCause.roots": { total: nonNegativeInteger },
68
68
  "probeCause.further": { total: nonNegativeInteger },
@@ -65,7 +65,7 @@ export interface ToolPathBases {
65
65
  */
66
66
  export declare function pathTargetBaseOf(pt: Pick<ToolCallPathTarget, "base"> | undefined, bases: ToolPathBases): string | undefined;
67
67
  /**
68
- * design/116 批④ #8 (CC 2.1.198 RWp, bundle :332987) — decompose an ABSOLUTE glob pattern into a base
68
+ * design/116 批④ #8 — decompose an ABSOLUTE glob pattern into a base
69
69
  * directory + relative pattern: cut at the last separator before the first glob metachar (`* ? [ {`); a
70
70
  * pattern with no metachar splits dirname/basename. `undefined` for a relative pattern (the caller keeps it
71
71
  * as-is) or when no usable base can be derived; a namespace refusal ({@link win32NamespaceScreen}, run
@@ -62,8 +62,8 @@ export interface EngineNotice {
62
62
  * firing rule as its skills sibling below (once per delivered frame, on the carrying message's receipt).
63
63
  * - `"permission.allowed_tool_not_granted"` — an allow list holds entries that grant (or refuse) nothing: a loaded skill's
64
64
  * `allowed-tools` / `disallowed-tools`, the request's `TaskSpec.allowedTools`, the deployment's `RunnerDeps.allowedTools`.
65
- * `*` (it voids an allow list; on a deny list it reads as nothing and the rest still refuses), `mcp__*` (no server
66
- * named), a rule on a tool with no such rule form (an allow by path on a tool other than `Read` / `Edit`, a deny by host),
65
+ * `*` (it voids an allow list; on a deny list it reads as nothing and the rest still refuses), `mcp__*` / `mcp__*__*` (no server
66
+ * named), a rule on a tool with no such rule form (an allow by path on a tool other than `Read` / `Edit`),
67
67
  * a shape the rule grammar refuses, a tool name stale against the run's roster. The run goes on and asks for those tools
68
68
  * as usual. Audience `user`; once per prepared task for the skill and request lists (`detail.sessionId` present), once
69
69
  * per Runner for the deployment's (at construction, no session). `detail: { sessionId?, entries: [{ source, entry,
@@ -231,6 +231,16 @@ export interface EngineNotice {
231
231
  * above's keys MINUS `to` (nothing moved, and a `to` naming a model that was not used is the one lie a
232
232
  * consumer cannot detect); `action` is the discriminator, so a reader never infers the posture from that
233
233
  * absence. Audience `operator`, same entitlement reading as its twin. Dedup key: (code, seat) per run.
234
+ * - `"route.seat_detached"` (same pre-flight, the third POSTURE) — a derived seat no primary model can stand in for
235
+ * (the advisor tool: advising the agent with its own model is no advice) failed the pairing pre-flight, so the seat
236
+ * is taken off THIS run: the advisor tool is not mounted (the leg's manifest says `advisor.reason: "route_refused"`).
237
+ * `detail: { seat, from, cause, fixHint, action: "detached", sessionId?, runId? }` — `route.classifier_unroutable`'s
238
+ * keys, `action` the discriminator. Audience `operator`. Dedup key: (code, seat) per run.
239
+ * - `"advisor.not_attached"` (`src/core/advisor-verdict.ts`) — a task's `advisorModel` did not attach for a reason the
240
+ * person is told: `unknown_model`, `advisor_unranked`, `weakest_tier`, `main_unranked`, `pairing` (the advisor is on a
241
+ * weaker tier than the main model) or `hold_unknown` (whether the conversation switched it off could not be read).
242
+ * `detail: { advisorModel, mainModel, reason, sessionId, runId? }`. Root legs only (a delegated child tells nobody).
243
+ * Audience `user`. Dedup key: (code, reason, advisorModel, mainModel) per SESSION (the once-per-session record).
234
244
  * - `"classifier.fallback"` (the classifier's candidate ladder, `src/core/runner/classifier-ladder.ts`)
235
245
  * — a gated call's classification did NOT rule on the classifier seat and was re-asked on the model
236
246
  * behind it. Sibling of `route.fallback_to_primary` one layer later: that one fires when the derived
@@ -406,8 +416,12 @@ export interface EngineNotice {
406
416
  * {@link import("./cross-boundary-taint.js").crossBoundaryPostureUndeclaredNotice}. Audience `operator`.
407
417
  * - `"config.cross_boundary_record_unwritten"` — a session's cross-boundary record the engine owed could not be written (the
408
418
  * store threw, or answered with no entry id) and refused nothing: on a tree that is not governed no judgment reads the record;
409
- * at a governed leg's prepare or settle the next leg reads the content after the newest record as off-target. Per failed write;
410
- * `detail: { sessionId, runId, phase, governed, cause }` (`phase`: `prepare` / `exit` / `suspend` / `settle`). Minted by
419
+ * at a governed leg's prepare or settle the next leg reads the content after the newest record as off-target — except where an
420
+ * earlier record of the same write landed and its catch-up did not: that catch-up record has not been written, and until #2160
421
+ * lands the next leg may read the content before the earlier record as covered and may not route its device calls to a person
422
+ * (KNOWN-LIMITS KL-469-c; the terminal shape is fail-closed), which the sentence says. Once per state a leg could not write;
423
+ * `detail: { sessionId, runId, phase, governed, cause }` (`phase`: `prepare` / `exit` / `suspend` / `settle`, where that state
424
+ * first failed). Minted by
411
425
  * {@link import("./cross-boundary-taint.js").crossBoundaryRecordUnwrittenNotice}. Audience `operator`.
412
426
  * - `"config.permission_mode_unavailable"` — the permission mode a task requested is not the mode the leg runs
413
427
  * under: the deployment denies it (`by: "operator_deny"` — `RuntimeCaps.bypassPermissions` / `autoMode` is
@@ -619,7 +633,7 @@ export interface EngineNotice {
619
633
  * secret token never rides a notice). A DIFFERENT record already on file is not this notice — it
620
634
  * is the `checkpoint.execution_outcome_conflict` throw (an engine defect, propagated).
621
635
  *
622
- * - `"classifier.denial_limit"` (#548, CC 2.1.250 `FO`/"too many classifier denials in headless
636
+ * - `"classifier.denial_limit"` (#548, "too many classifier denials in headless
623
637
  * mode") — the auto-mode classifier's DENIAL LIMIT was reached (3 consecutive blocks, or 20 in the
624
638
  * run; `RunnerDeps.autoMode.denialLimit`) and the fallback ask it turns into was never JUDGED by
625
639
  * anyone. Two lanes reach it. SYNCHRONOUS: no approver to go to — none wired, a blanket
@@ -58,7 +58,7 @@ export interface StreamCursorState {
58
58
  acceptedBytes: number;
59
59
  /** `tail.sourceSkippedBytes()` as of the last slice — the delta is disclosed exactly once. */
60
60
  disclosedSkippedBytes: number;
61
- /** Incremental UTF-8 decoder (CC 2.1.220 shape: StringDecoder holds a split multi-byte char across
61
+ /** Incremental UTF-8 decoder (StringDecoder holds a split multi-byte char across
62
62
  * reads instead of emitting replacement chars at every read boundary). */
63
63
  decoder: StringDecoder;
64
64
  }
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * PROVENANCE OF THE DEFAULT (not a taste call, and not a JSDoc-only constraint): the reference
15
15
  * implementation caps its own file-history at the newest 100 snapshots and evicts the rest — CC
16
- * 2.1.250 holds the cap as a module constant (`R1e = 100`) and applies it in the snapshot reducer
16
+ * holds the cap as a module constant and applies it in the snapshot reducer
17
17
  * (`if (E.length > R1e) R = E.slice(-R1e), <evict>(E.slice(0, E.length - R1e), R, …).catch(…)`),
18
18
  * i.e. keep the newest N by commit order, hand the evicted ones to a best-effort eviction pass, and
19
19
  * never let that pass fail the turn that triggered it. Its eviction unlinks a backup only when no
@@ -1,3 +1,4 @@
1
+ import type { GovernanceRetryClass } from "./governance-codes.js";
1
2
  import { type FileHistoryRetentionPolicy } from "./file-history-retention.js";
2
3
  import type { ExecutionEnv } from "../internal/harness-types.js";
3
4
  /** rel path of `child` under `root` in "/" form (win root) / verbatim form (posix root); null when not under.
@@ -80,21 +81,54 @@ export declare function resolveTrackKey(root: string, key: string, minted?: File
80
81
  * value rather than keeping its own list. */
81
82
  export declare const FILE_HISTORY_ERROR_CODES: readonly ["track_failed", "snapshot_failed", "restore_failed", "adopt_failed", "not_found", "aborted", "unsupported_format", "conflict"];
82
83
  export interface FileHistoryError {
83
- /** `not_found` = no boundary for that (scope, entryId); `aborted` = the signal fired (or the
84
+ /** `not_found` = no boundary for that (scope, entryId) — an absence, never a fault (a boundary that is there
85
+ * but cannot be read is the verb's `*_failed` with the fault); `aborted` = the signal fired (or the
84
86
  * fencing discarded a timed-out attempt); `conflict` = a non-destructive-target law refused
85
87
  * (adopt/import onto a scope with its own history) — and ONLY that, because a caller decides
86
88
  * whether to stay quiet on it: the engine's fork-carry seat treats `conflict` as "already carried,
87
89
  * or grown its own" and says nothing, so an adoption that failed for any OTHER reason (a corrupt
88
- * source graph, a blob it cannot reach, a consumed-id ledger it cannot carry — including the
89
- * tombstones a publish owes for unreadable residue it is about to replace) must NOT wear this
90
+ * source graph, a blob it cannot reach, a consumed-id ledger it cannot carry, a target whose
91
+ * history cannot be judged) must NOT wear this
90
92
  * code or the failure disappears into that silence — those are `adopt_failed`, which the
91
93
  * ADOPTION FAMILY shares: `importHistory` publishes under the same target law and answers the
92
- * same code for the same non-conflict failures (its `unsupported_format` names a bad ENVELOPE or
94
+ * same code for the same non-conflict failures, and `exportHistory` answers it for a history it cannot
95
+ * read whole (its `unsupported_format` names a bad ENVELOPE or
93
96
  * work its shape drove; a fault in the PUBLISH step itself — the disk refusing a good graph —
94
97
  * answers `adopt_failed`); `unsupported_format`
95
98
  * = a foreign-epoch or wrong-version envelope; the rest name the verb that failed. */
96
99
  code: (typeof FILE_HISTORY_ERROR_CODES)[number];
97
100
  message: string;
101
+ /** The backend's judgment of a FAULT — it could not do what it was asked — as a {@link GovernanceRetryClass}
102
+ * word (the words are defined there, once). Stated whenever the backend knows: from a caught error's code,
103
+ * or from a state it found itself (a damaged record is `terminal`). Absent when it does not know, and on an
104
+ * outcome that is not a fault (an absent boundary's `not_found`, the empty-target law, an abort). Every
105
+ * reader — the engine's edit refusal, the rewind disclosure, a failed rewind task's `terminal.detail`, a
106
+ * host's metrics — reads this cell, never the message text. */
107
+ permanence?: GovernanceRetryClass;
108
+ }
109
+ /** A fault's judgment as the member a {@link FileHistoryError} spreads in: `{ permanence }` when judged, `{}`
110
+ * when not — so an unjudged failure carries no `permanence` member at all. */
111
+ export type FileHistoryFault = {
112
+ readonly permanence?: GovernanceRetryClass;
113
+ };
114
+ /** The bundled backends' ONE reading of a caught value — by its `code` member only, never its message. */
115
+ export declare function caughtFault(caught: unknown): FileHistoryFault;
116
+ /** A record the backend found damaged (unparseable, the wrong shape, failing its own digest): no retry
117
+ * repairs it. */
118
+ export declare const DAMAGED_RECORD: FileHistoryFault;
119
+ /**
120
+ * The `Error` the engine hands `RunnerDeps.onError` on every `phase: "rewind"` delivery: `code` and
121
+ * `permanence` are the failure's own, read the same way as on a {@link FileHistoryError}; `message` is the
122
+ * engine's sentence about it (not the store's text). A failure the engine itself observed carries the same
123
+ * shape: a timed-out capture is `aborted`; a backend that threw instead of answering is the verb's own
124
+ * `*_failed` code with no `permanence` and the thrown value as the standard `Error.cause`.
125
+ */
126
+ export declare class FileHistoryStoreError extends Error implements Pick<FileHistoryError, "code" | "permanence"> {
127
+ readonly code: FileHistoryError["code"];
128
+ readonly permanence?: FileHistoryError["permanence"];
129
+ constructor(failure: Pick<FileHistoryError, "code" | "permanence">, message: string, thrown?: {
130
+ value: unknown;
131
+ });
98
132
  }
99
133
  export type FileHistoryResult = {
100
134
  ok: true;
@@ -117,6 +151,15 @@ export type FileHistoryTrackResult = {
117
151
  ok: false;
118
152
  error: FileHistoryError;
119
153
  };
154
+ /** {@link FileHistoryStore.exportHistory}'s answer: the envelope, `null` = nothing to export (the one meaning of
155
+ * `null`), or a history that cannot be read whole — `adopt_failed` with the fault, naming what cannot be read. */
156
+ export type FileHistoryExportResult = {
157
+ ok: true;
158
+ history: FileHistoryExport | null;
159
+ } | {
160
+ ok: false;
161
+ error: FileHistoryError;
162
+ };
120
163
  /**
121
164
  * Three-state discriminant + full per-file ledger on EVERY arm (design/381 DV-15). Refusals are
122
165
  * never folded into benign skips; `base_unavailable` (DV-14) is a refusal reason.
@@ -353,10 +396,13 @@ export interface FileHistoryStore {
353
396
  * from "the env died after k writes". Restore is per-file idempotent (identical files skip), so
354
397
  * the documented recovery for a partial outcome is: re-run the same restore; it converges. There
355
398
  * is NO all-or-nothing guarantee and none is claimed. Task-level policy on `"partial"` lives in
356
- * the CALLER (prepare defaults to fail-loud; see DV-15).
399
+ * the CALLER (prepare defaults to fail-loud; see DV-15). A boundary never made is `not_found` and
400
+ * touches NOTHING — no file read or written, an empty ledger — since the engine's refusal of a
401
+ * rewind without a boundary is exactly this answer (the contract kit checks the bytes).
357
402
  */
358
403
  restore(scope: string, entryId: string, env: ExecutionEnv, root: string, signal?: AbortSignal): Promise<FileHistoryRestoreResult>;
359
- /** Whether a boundary exists for (scope, entryId). */
404
+ /** Whether (scope, entryId) has a USABLE boundary: `false` both for one never made and for one that is there but
405
+ * cannot be read — `restore` is the verb that says which (`not_found` / `restore_failed` with the fault). */
360
406
  canRestore(scope: string, entryId: string): Promise<boolean>;
361
407
  /**
362
408
  * Optional dry-run: per-file change stats for a prospective restore (CC diffStats; design/381
@@ -466,7 +512,11 @@ export interface FileHistoryStore {
466
512
  * empty OR already holds a graph with the exact same import identity/digest (pure idempotent
467
513
  * re-publish). A non-empty target with any other content ⇒ typed `conflict` refusal — atomic
468
514
  * visibility prevents half-graphs, this law prevents publishing the WRONG complete graph over
469
- * locally-grown history.
515
+ * locally-grown history. A target whose history cannot be judged ⇒ `adopt_failed`, before any blob is
516
+ * fetched; a source that cannot supply a referenced blob ⇒ `adopt_failed`. `exportHistory` answers a
517
+ * {@link FileHistoryExportResult}: the whole graph, `null` for nothing to export, or `adopt_failed` with the
518
+ * fault for a history it cannot read whole (an unreadable tracked set, boundaries that cannot be judged) —
519
+ * never a partial graph, and never a `null` that hides a fault.
470
520
  */
471
521
  /**
472
522
  * NO `dispose` / lifecycle verb, and that is a decision rather than an omission (design/381 slice 3,
@@ -484,7 +534,7 @@ export interface FileHistoryStore {
484
534
  * sibling file store in this family offers.
485
535
  * A deployment that needs cross-process exclusion takes the assembled backend, or brings its own.
486
536
  */
487
- exportHistory?(scope: string): Promise<FileHistoryExport | null>;
537
+ exportHistory?(scope: string): Promise<FileHistoryExportResult>;
488
538
  importHistory?(scope: string, data: FileHistoryExport, srcGetBlob: (hash: string) => Promise<Uint8Array | undefined>): Promise<FileHistoryResult>;
489
539
  }
490
540
  /**
@@ -751,7 +801,7 @@ export declare class InMemoryFileHistoryStore implements FileHistoryStore {
751
801
  diffStats(scope: string, entryId: string, env: ExecutionEnv, root: string): Promise<FileHistoryDiffStats | null>;
752
802
  reap(scope: string, keepEntryIds: string[]): Promise<number>;
753
803
  adoptScope(fromScope: string, toScope: string): Promise<FileHistoryResult>;
754
- exportHistory(scope: string): Promise<FileHistoryExport | null>;
804
+ exportHistory(scope: string): Promise<FileHistoryExportResult>;
755
805
  /** The export projection as a SYNCHRONOUS function — the import's final claim needs to compare a
756
806
  * target's identity without yielding (see the publish step). */
757
807
  private exportOf;
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { thrownText } from "./thrown-value.js";
2
+ import { readMember, thrownText } from "./thrown-value.js";
3
3
  import { isAbsoluteForFamily, isWinFormPath, pathFamilyOf, relativeInEveryFamily } from "../tools/fs/safety.js";
4
4
  import { fileHistoryBoundariesToKeep, resolveFileHistoryRetention } from "./file-history-retention.js";
5
5
  const normSlash = (p) => p.replace(/\\/g, "/");
@@ -67,6 +67,26 @@ export function resolveTrackKey(root, key, minted) {
67
67
  return { ok: true, abs: joinUnder(root, keyN) };
68
68
  }
69
69
  export const FILE_HISTORY_ERROR_CODES = Object.freeze(["track_failed", "snapshot_failed", "restore_failed", "adopt_failed", "not_found", "aborted", "unsupported_format", "conflict"]);
70
+ const CAUGHT_CODE_RETRY_CLASS = new Map([
71
+ ...["EACCES", "EPERM", "EROFS", "ENAMETOOLONG", "EFBIG", "ENOTDIR", "EISDIR"].map((c) => [c, "terminal"]),
72
+ ...["EAGAIN", "EWOULDBLOCK", "EBUSY", "EINTR", "ETIMEDOUT", "EMFILE", "ENFILE"].map((c) => [c, "transient"]),
73
+ ]);
74
+ export function caughtFault(caught) {
75
+ const code = readMember(caught, "code");
76
+ const judged = typeof code === "string" ? CAUGHT_CODE_RETRY_CLASS.get(code) : undefined;
77
+ return judged === undefined ? {} : { permanence: judged };
78
+ }
79
+ export const DAMAGED_RECORD = Object.freeze({ permanence: "terminal" });
80
+ export class FileHistoryStoreError extends Error {
81
+ code;
82
+ constructor(failure, message, thrown) {
83
+ super(message, thrown !== undefined ? { cause: thrown.value } : undefined);
84
+ this.name = "FileHistoryStoreError";
85
+ this.code = failure.code;
86
+ if (failure.permanence !== undefined)
87
+ this.permanence = failure.permanence;
88
+ }
89
+ }
70
90
  export const FILE_HISTORY_DIFF_LINE_BUDGET = 2000;
71
91
  export function countRestoreLineDiff(current, target) {
72
92
  const isBinary = (b) => b !== undefined && b.includes(0);
@@ -491,7 +511,7 @@ export class InMemoryFileHistoryStore {
491
511
  return { ok: true, minted: true };
492
512
  }
493
513
  catch (err) {
494
- return { ok: false, error: { code: "track_failed", message: `first-touch record for ${absPath} could not be persisted: ${thrownText(err)}` } };
514
+ return { ok: false, error: { code: "track_failed", message: `first-touch record for ${absPath} could not be persisted: ${thrownText(err)}`, ...caughtFault(err) } };
495
515
  }
496
516
  }
497
517
  async annulTrack(scope, absPath, root, verify) {
@@ -532,7 +552,7 @@ export class InMemoryFileHistoryStore {
532
552
  return { ok: true };
533
553
  }
534
554
  catch (err) {
535
- return { ok: false, error: { code: "track_failed", message: `annulling the first-touch record for ${absPath} failed: ${thrownText(err)}` } };
555
+ return { ok: false, error: { code: "track_failed", message: `annulling the first-touch record for ${absPath} failed: ${thrownText(err)}`, ...caughtFault(err) } };
536
556
  }
537
557
  }
538
558
  async snapshot(scope, entryId, env, root, signal) {
@@ -601,14 +621,14 @@ export class InMemoryFileHistoryStore {
601
621
  }
602
622
  catch (err) {
603
623
  sc.spentAttempts.add(entryId);
604
- return { ok: false, error: { code: "snapshot_failed", message: `boundary for entry "${entryId}" could not be committed: ${thrownText(err)}` } };
624
+ return { ok: false, error: { code: "snapshot_failed", message: `boundary for entry "${entryId}" could not be committed: ${thrownText(err)}`, ...caughtFault(err) } };
605
625
  }
606
626
  this.applyBoundaryRetention(scope, entryId);
607
627
  return { ok: true };
608
628
  }
609
629
  catch (err) {
610
630
  sc.spentAttempts.add(entryId);
611
- return { ok: false, error: { code: "snapshot_failed", message: thrownText(err) } };
631
+ return { ok: false, error: { code: "snapshot_failed", message: thrownText(err), ...caughtFault(err) } };
612
632
  }
613
633
  finally {
614
634
  sc.activeAttempts.delete(entryId);
@@ -688,7 +708,7 @@ export class InMemoryFileHistoryStore {
688
708
  }
689
709
  catch (err) {
690
710
  const touched = empty.filesChanged.length > 0 || empty.identicalSkipped.length > 0 || empty.refused.length > 0 || empty.failed.length > 0;
691
- return { outcome: touched ? "partial" : "failed", ...empty, error: { code: "restore_failed", message: thrownText(err) } };
711
+ return { outcome: touched ? "partial" : "failed", ...empty, error: { code: "restore_failed", message: thrownText(err), ...caughtFault(err) } };
692
712
  }
693
713
  }
694
714
  async canRestore(scope, entryId) {
@@ -795,14 +815,12 @@ export class InMemoryFileHistoryStore {
795
815
  return { ok: true };
796
816
  }
797
817
  catch (err) {
798
- return { ok: false, error: { code: "adopt_failed", message: `adoptScope failed: ${thrownText(err)}` } };
818
+ return { ok: false, error: { code: "adopt_failed", message: `adoptScope failed: ${thrownText(err)}`, ...caughtFault(err) } };
799
819
  }
800
820
  }
801
821
  async exportHistory(scope) {
802
822
  const sc = this.scopes.get(scope);
803
- if (sc === undefined || (sc.tracked.size === 0 && sc.boundaries.size === 0))
804
- return null;
805
- return this.exportOf(sc);
823
+ return { ok: true, history: sc === undefined || (sc.tracked.size === 0 && sc.boundaries.size === 0) ? null : this.exportOf(sc) };
806
824
  }
807
825
  exportOf(sc) {
808
826
  return {
@@ -826,9 +844,9 @@ export class InMemoryFileHistoryStore {
826
844
  const shaped = validateFileHistoryExport(data);
827
845
  if (!shaped.ok)
828
846
  return shaped;
829
- const existing = await this.exportHistory(scope);
830
- if (existing !== null) {
831
- if (fileHistoryExportDigest(existing) === fileHistoryExportDigest(data))
847
+ const existing = this.scopes.get(scope);
848
+ if (existing !== undefined && (existing.tracked.size > 0 || existing.boundaries.size > 0)) {
849
+ if (fileHistoryExportDigest(this.exportOf(existing)) === fileHistoryExportDigest(data))
832
850
  return { ok: true };
833
851
  return { ok: false, error: { code: "conflict", message: `importHistory target "${scope}" already holds a DIFFERENT history — a first import requires an empty target and a retry is legal only for the identical envelope (atomic visibility prevents half-graphs; this law prevents publishing the wrong complete graph over locally-grown history)` } };
834
852
  }
@@ -848,7 +866,7 @@ export class InMemoryFileHistoryStore {
848
866
  }
849
867
  }
850
868
  catch (err) {
851
- return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}` } };
869
+ return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}`, ...caughtFault(err) } };
852
870
  }
853
871
  }
854
872
  async importStaged(scope, data, srcGetBlob, needed) {
@@ -862,10 +880,10 @@ export class InMemoryFileHistoryStore {
862
880
  bytes = await srcGetBlob(hash);
863
881
  }
864
882
  catch (err) {
865
- return { ok: false, error: { code: "unsupported_format", message: `source blob ${hash} fetch failed: ${thrownText(err)}` } };
883
+ return { ok: false, error: { code: "unsupported_format", message: `source blob ${hash} fetch failed: ${thrownText(err)}`, ...caughtFault(err) } };
866
884
  }
867
885
  if (bytes === undefined)
868
- return { ok: false, error: { code: "not_found", message: `source blob ${hash} is missing — the envelope's graph cannot be completed, so nothing is published` } };
886
+ return { ok: false, error: { code: "adopt_failed", message: `source blob ${hash} is missing — the envelope's graph cannot be completed, so nothing is published` } };
869
887
  if (createHash("sha256").update(bytes).digest("hex") !== hash) {
870
888
  return { ok: false, error: { code: "unsupported_format", message: `source blob ${hash} fails content-address integrity — refused, nothing published` } };
871
889
  }
@@ -894,7 +912,7 @@ export class InMemoryFileHistoryStore {
894
912
  return { ok: true };
895
913
  }
896
914
  catch (err) {
897
- return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}` } };
915
+ return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}`, ...caughtFault(err) } };
898
916
  }
899
917
  }
900
918
  }
@@ -10,6 +10,7 @@ import { failedJudgmentDeny } from "./runner/policy-throw.js";
10
10
  import { thrownText } from "./thrown-value.js";
11
11
  import { argsAsTheToolReads } from "./effective-path-target.js";
12
12
  import { foldHookAsk, refuseOutOfContractDecision } from "./tool-policy.js";
13
+ import { withProbeMandate } from "./ask-origin.js";
13
14
  import { applyUnansweredSilence, readStationAnswer, silencedBy } from "./shell-read-silence.js";
14
15
  import { asksUnstated, permissionSeatAt, unstatedAskMessage } from "./mode-policy.js";
15
16
  export async function runGateFold(pass) {
@@ -133,7 +134,7 @@ export async function runGateFold(pass) {
133
134
  pass.tightenedBy = input.shellGated === true ? "shell_gate" : "safety";
134
135
  }
135
136
  else if (alreadyAsking && probeMandated === true && pass.decision.action === "ask") {
136
- pass.decision = { ...pass.decision, probeMandated: true };
137
+ pass.decision = withProbeMandate(pass.decision, probeReason);
137
138
  }
138
139
  }
139
140
  if (input.peerMessage === true && pass.decision.action === "allow" && input.autoMode !== undefined) {
@@ -218,7 +219,7 @@ function applyShellReadVerdict(pass, answer, toolName, shellGated) {
218
219
  pass.shellReadDetails = verdict.details;
219
220
  if (pass.decision.action !== "allow") {
220
221
  if (pass.decision.action === "ask")
221
- pass.decision = { ...pass.decision, probeMandated: true, ...silencedBy(verdict, pass.decision) };
222
+ pass.decision = { ...withProbeMandate(pass.decision, account), ...silencedBy(verdict, pass.decision) };
222
223
  return;
223
224
  }
224
225
  const candidate = normalizeReadRootCandidate(verdict.readRootCandidate);
@@ -1,18 +1,19 @@
1
1
  import { argsAsTheToolReads, withArgsReading } from "./effective-path-target.js";
2
2
  import { attest, attestationOf, bindableArgsOf, decisionText, foldHookAsk, mergeAttestation, refusalOf, refuseOutOfContractDecision, withAttestation } from "./tool-policy.js";
3
- import { arbitrateByMode, classifyAsk, decisionAskClassesOf, fileCommandOptionsSentence, persistedRuleMandateOf, safetyAxisOf, stampAskOrigin } from "./ask-origin.js";
3
+ import { arbitrateByMode, classifyAsk, decisionAskClassesOf, dontAskRefusalMessage, fileCommandOptionsSentence, persistedRuleMandateOf, safetyAxisOf, stampAskOrigin } from "./ask-origin.js";
4
4
  import { classifierParseFailureDenyMessage, denialLimitFallbackMessage, rungDidNotRule, unarmedWindow } from "./auto-mode.js";
5
5
  import { classifierNoRulingArm, settleNoRulingAsk } from "./classifier-unavailable-arm.js";
6
6
  import { SETTLEMENT_NOBODY_JUDGED } from "./gate-outcome.js";
7
7
  import { inlineUntrusted } from "./untrusted-text.js";
8
8
  import { ASK_USER_QUESTION_TOOL_NAME } from "./ask-question.js";
9
+ import { toolFace } from "./tool-catalog-entries.js";
9
10
  import { applyPersistedTightening, disclosedRuleSet } from "./persisted-rule-arms.js";
10
11
  import { skillAllowDecision, skillRowRefusal } from "./skill-grant.js";
11
12
  import { allowRuleShadowOf } from "./allow-entry.js";
12
13
  import { CD_GIT_COMPOUND_SENTENCE, changesDirectoryBeforeGit, readOnlyShellVerdict } from "./read-only-shell.js";
13
14
  import { readShellCommand } from "./shell-run-candidates.js";
14
- import { COMMAND_RULE_TOOL } from "./permission-rule-model.js";
15
- import { catalogRuleFaceOf } from "./tool-registry.js";
15
+ import { COMMAND_RULE_TOOL, ruleShapeOf } from "./permission-rule-model.js";
16
+ import { catalogRuleFaceOf, isProtectedWrite } from "./tool-registry.js";
16
17
  import { callFaceSeat } from "./tool-roster.js";
17
18
  import { heldBy, wireOf } from "./tool-name-readings.js";
18
19
  import { asksUnstated, modeAcceptsFileCommand, permissionSeatAt, unstatedAskMessage } from "./mode-policy.js";
@@ -26,6 +27,8 @@ import { agentIdOf, judgedInputOf, raiseToolCheck } from "./mods/stations.js";
26
27
  import { readToolChecks } from "./tool-check-reading.js";
27
28
  import { policyThrowLine, reportPolicyThrow } from "./runner/policy-throw.js";
28
29
  const QUESTION_TOOL_NAMES = new Set([ASK_USER_QUESTION_TOOL_NAME]);
30
+ const CONTENT_ROUTING_CONTRACTS = new Set(["ask-user-question", "structured-output", "report-blocked"].map((id) => toolFace(id).contract.contractId));
31
+ const routesContent = (f) => f?.engineMounted === true && CONTENT_ROUTING_CONTRACTS.has(f.contractId);
29
32
  function parkWith(suspendAsk, parkArgs, carry) {
30
33
  return suspendAsk(...parkArgs, carry);
31
34
  }
@@ -159,7 +162,7 @@ export async function runGateLanes(pass) {
159
162
  pass.decision = orgAnswer.decision;
160
163
  const readPersistedRules = (req) => readPersistedRuleLane(input.persistedRules, req, input.abortSignal);
161
164
  const laneReq = Object.freeze(pass.policyRewrite !== undefined ? await withArgsReading(pass.req, pass.policyRewrite, pass.callSignal ?? input.abortSignal) : { ...pass.req });
162
- const laneAnswer = input.persistedRules !== undefined && wireOf("lanes.persistedLaneSkip", pass.req) !== ASK_USER_QUESTION_TOOL_NAME ? await readPersistedRules(laneReq) : undefined;
165
+ const laneAnswer = input.persistedRules !== undefined && !routesContent(pass.req.face) ? await readPersistedRules(laneReq) : undefined;
163
166
  {
164
167
  const tightened = applyPersistedTightening(pass.decision, laneAnswer, laneReq);
165
168
  pass.decision = tightened.tightened === "deny" ? refusalOf(tightened.decision, laneReq.args) : tightened.decision;
@@ -272,7 +275,7 @@ export async function runGateLanes(pass) {
272
275
  ["hook", () => (pass.hookAllowedBy !== undefined && pass.policyRewrite === undefined ? { entries: pass.hookAllowedBy, clear: () => ({ action: "allow", message: "a PreToolUse hook allowed this call", decisionReason: "hook" }), afterClear: async () => undefined } : undefined)],
273
276
  ["deployment", () => {
274
277
  const hit = input.deploymentAllows?.allowing(pass.req, mode);
275
- return hit && { entries: hit.entries, clear: () => listAllow("deployment_rule", "the deployment's", hit.entries), afterClear: () => notifier.notifyAsync(() => input.deploymentAllows?.onAllowed?.({ toolName: pass.req.toolName, toolCallId, entries: hit.entries }), "toolGate.allowedToolsResolved") };
278
+ return hit && { entries: hit.entries, ...(hit.positions !== undefined ? { positions: hit.positions } : {}), clear: () => listAllow("deployment_rule", "the deployment's", hit.entries), afterClear: () => notifier.notifyAsync(() => input.deploymentAllows?.onAllowed?.({ toolName: pass.req.toolName, toolCallId, entries: hit.entries }), "toolGate.allowedToolsResolved") };
276
279
  }],
277
280
  ["persisted", () => storedAllows && {
278
281
  entries: storedAllows.map((r) => r.rule),
@@ -286,7 +289,7 @@ export async function runGateLanes(pass) {
286
289
  }],
287
290
  ["request", () => {
288
291
  const hit = input.requestAllows?.allowing(pass.req, mode);
289
- return hit && { entries: hit.entries, clear: () => listAllow("request_rule", "this run's", hit.entries), afterClear: () => notifier.notifyAsync(() => input.requestAllows?.onAllowed?.({ toolName: pass.req.toolName, toolCallId, entries: hit.entries }), "toolGate.allowedToolsResolved") };
292
+ return hit && { entries: hit.entries, ...(hit.positions !== undefined ? { positions: hit.positions } : {}), clear: () => listAllow("request_rule", "this run's", hit.entries), afterClear: () => notifier.notifyAsync(() => input.requestAllows?.onAllowed?.({ toolName: pass.req.toolName, toolCallId, entries: hit.entries }), "toolGate.allowedToolsResolved") };
290
293
  }],
291
294
  ];
292
295
  const shadows = [];
@@ -299,6 +302,7 @@ export async function runGateLanes(pass) {
299
302
  continue;
300
303
  if (allowMayClear()) {
301
304
  pass.decision = hit.clear();
305
+ pass.clearedBy = { ...allowRuleShadowOf(member, hit.entries), ...(hit.positions !== undefined ? { positions: hit.positions } : {}) };
302
306
  await hit.afterClear();
303
307
  if (member !== "hook")
304
308
  afterRuleClear();
@@ -468,7 +472,7 @@ export async function runGateLanes(pass) {
468
472
  pass.decision = refusalOf({
469
473
  action: "deny",
470
474
  message: verdict.mode === "dontAsk"
471
- ? `refused by the dontAsk permission mode: this call needed approval (${verdict.askClass}) and the mode answers no ask; run under a mode with an approver, or change the call`
475
+ ? dontAskRefusalMessage({ toolName, classification: pass.askClassification, coveredWrite: isProtectedWrite(pass.req), ruleShape: ruleShapeOf(pass.req, "allow") ?? toolName })
472
476
  : `refused by the bypassPermissions permission mode: the write-protection judge could not resolve this write's target, and with nobody to ask the mode refuses rather than releases it`,
473
477
  decisionReason: "mode",
474
478
  }, finalArgs);
@@ -636,7 +640,7 @@ export async function runGateLanes(pass) {
636
640
  }
637
641
  let editLane;
638
642
  const editLaneReq = Object.freeze(await withArgsReading(pass.req, editArgs, callSignal ?? input.abortSignal));
639
- if (input.persistedRules !== undefined && wireOf("lanes.persistedEditSkip", pass.req) !== ASK_USER_QUESTION_TOOL_NAME) {
643
+ if (input.persistedRules !== undefined && !routesContent(pass.req.face)) {
640
644
  editLane = await readPersistedRules(editLaneReq);
641
645
  const editTightened = applyPersistedTightening(recheck, editLane, editLaneReq);
642
646
  recheck = editTightened.decision;