@sema-agent/server 7.52.1 → 7.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/USAGE.md +7 -1
  2. package/dist/adoption/plan.d.ts +3 -3
  3. package/dist/adoption/quiesce.d.ts +1 -1
  4. package/dist/adoption/sql.d.ts +2 -2
  5. package/dist/adoption/wire.d.ts +1 -1
  6. package/dist/approval-ask-machine.d.ts +2 -2
  7. package/dist/approval-card.d.ts +18 -18
  8. package/dist/approval-deny-reasons.d.ts +1 -1
  9. package/dist/approval-hmac.d.ts +2 -2
  10. package/dist/approval-reconciler.d.ts +14 -14
  11. package/dist/approval.d.ts +2 -2
  12. package/dist/audit.d.ts +4 -4
  13. package/dist/auth-keys.d.ts +6 -6
  14. package/dist/bench/l8/artifact.d.ts +6 -1
  15. package/dist/bench/l8/artifact.js +2 -27
  16. package/dist/bench/l8/escape.d.ts +3 -3
  17. package/dist/bench/l8/index.d.ts +1 -1
  18. package/dist/bench/s1/arms.d.ts +3 -3
  19. package/dist/bench/s1/oracle.d.ts +2 -2
  20. package/dist/bench/s1/reviewer.d.ts +1 -1
  21. package/dist/bench/s1/row.d.ts +5 -5
  22. package/dist/bench/s1/runner-ctx.d.ts +3 -3
  23. package/dist/boot/config-center.d.ts +14 -7
  24. package/dist/boot/config-center.js +4 -2
  25. package/dist/boot/coordinators.d.ts +4 -4
  26. package/dist/boot/crash-last.d.ts +1 -1
  27. package/dist/boot/device-lane.d.ts +3 -3
  28. package/dist/boot/engine-lease.d.ts +4 -4
  29. package/dist/boot/governance-seams.d.ts +5 -5
  30. package/dist/boot/leader.d.ts +5 -5
  31. package/dist/boot/leader.js +2 -0
  32. package/dist/boot/limit-sync.d.ts +1 -1
  33. package/dist/boot/memory-boundary.d.ts +5 -5
  34. package/dist/boot/memory-consolidation.d.ts +8 -2
  35. package/dist/boot/memory-consolidation.js +1 -0
  36. package/dist/boot/org-memory.d.ts +5 -5
  37. package/dist/boot/parked-revive-gate.d.ts +6 -6
  38. package/dist/boot/permission-rules-audit.d.ts +2 -2
  39. package/dist/boot/reapers.d.ts +8 -8
  40. package/dist/boot/resolve-spec.d.ts +4 -4
  41. package/dist/boot/resolve-spec.js +1 -3
  42. package/dist/boot/retention-lane.d.ts +1 -1
  43. package/dist/boot/runner-deps.d.ts +53 -27
  44. package/dist/boot/runner-deps.js +16 -9
  45. package/dist/boot/runtime-caps.d.ts +4 -4
  46. package/dist/boot/session-faces.d.ts +1 -1
  47. package/dist/boot/shutdown.d.ts +10 -10
  48. package/dist/boot/shutdown.js +1 -1
  49. package/dist/boot/side-query-lane.d.ts +14 -14
  50. package/dist/boot/stores.js +5 -0
  51. package/dist/boot/task-list-lane.d.ts +3 -3
  52. package/dist/boot/webfetch-summarize-lane.d.ts +9 -9
  53. package/dist/boot/workflow-orchestration.d.ts +1 -1
  54. package/dist/bounded-session-map.d.ts +1 -1
  55. package/dist/brain.d.ts +3 -3
  56. package/dist/budget.d.ts +3 -3
  57. package/dist/canonical-json.d.ts +53 -0
  58. package/dist/canonical-json.js +48 -0
  59. package/dist/capabilities/center-prompts.d.ts +13 -13
  60. package/dist/capabilities/collab-workflows.d.ts +1 -1
  61. package/dist/capabilities/hands-lane.d.ts +4 -4
  62. package/dist/capabilities/memory-notice.d.ts +4 -4
  63. package/dist/capabilities/prompt.d.ts +2 -2
  64. package/dist/capabilities/sandbox-file-send.d.ts +2 -2
  65. package/dist/capabilities/scenarios.d.ts +16 -16
  66. package/dist/capabilities/skills.d.ts +1 -1
  67. package/dist/capabilities/team.d.ts +13 -12
  68. package/dist/capabilities/tool-defer.d.ts +6 -6
  69. package/dist/config-catalog.d.ts +139 -0
  70. package/dist/config-catalog.js +556 -0
  71. package/dist/config-center/apply-effective.d.ts +42 -30
  72. package/dist/config-center/apply-effective.js +69 -62
  73. package/dist/config-center/apply-ledger.d.ts +6 -6
  74. package/dist/config-center/effective-keys.d.ts +30 -0
  75. package/dist/config-center/effective-keys.js +49 -0
  76. package/dist/config-center/facade.d.ts +6 -5
  77. package/dist/config-center/facade.js +1 -1
  78. package/dist/config-center/hot-keys-registry.d.ts +2 -2
  79. package/dist/config-center/http-client.d.ts +5 -5
  80. package/dist/config-center/mcp-revocation.d.ts +6 -4
  81. package/dist/config-center/mcp-revocation.js +2 -12
  82. package/dist/config-center/restart-signal.d.ts +11 -1
  83. package/dist/config-center/restart-signal.js +4 -10
  84. package/dist/config-center/skills-mcp.d.ts +2 -2
  85. package/dist/config-center/stage-limits.d.ts +3 -3
  86. package/dist/config-center/types.d.ts +42 -59
  87. package/dist/config-invariants.d.ts +1 -1
  88. package/dist/config-lkg.js +7 -1
  89. package/dist/config-provider.d.ts +2 -2
  90. package/dist/config-provider.js +1 -1
  91. package/dist/config-types.d.ts +140 -132
  92. package/dist/config.d.ts +22 -22
  93. package/dist/config.js +2 -1
  94. package/dist/degenerate-instrument.d.ts +1 -1
  95. package/dist/deployment-governance.d.ts +13 -13
  96. package/dist/device-enrollment.d.ts +5 -3
  97. package/dist/device-store.d.ts +17 -3
  98. package/dist/device-store.js +2 -0
  99. package/dist/device-ws-hub.d.ts +10 -5
  100. package/dist/device-ws-hub.js +25 -5
  101. package/dist/device-ws-protocol.d.ts +3 -3
  102. package/dist/digest-form.d.ts +1 -1
  103. package/dist/elicitation.d.ts +1 -1
  104. package/dist/env-facts.d.ts +7 -7
  105. package/dist/execution-lane-caps.d.ts +5 -5
  106. package/dist/fleet/fleet-bus.d.ts +53 -53
  107. package/dist/fleet/fleet-reconciler.d.ts +2 -2
  108. package/dist/fleet/fleet-terminal-window.d.ts +1 -1
  109. package/dist/fleet/subagent-tail-bus.d.ts +3 -3
  110. package/dist/fleet-client.d.ts +3 -3
  111. package/dist/fleet-lease.d.ts +1 -1
  112. package/dist/hooks/branch-transcript.d.ts +2 -2
  113. package/dist/hooks/cc-agent-hook-prompt.d.ts +2 -2
  114. package/dist/hooks/cc-stop-prompt.d.ts +2 -2
  115. package/dist/hooks/hook-llm.d.ts +1 -1
  116. package/dist/hooks/hook-runner.d.ts +5 -5
  117. package/dist/http/active-run-conflict.d.ts +31 -33
  118. package/dist/http/active-run-conflict.js +4 -2
  119. package/dist/http/route-ctx.d.ts +22 -22
  120. package/dist/http/routes/a2a-serve.d.ts +2 -2
  121. package/dist/http/routes/admin-config-refresh.d.ts +1 -1
  122. package/dist/http/routes/admin-drain.d.ts +4 -4
  123. package/dist/http/routes/admin-memory-optout.d.ts +5 -5
  124. package/dist/http/routes/adoption.d.ts +1 -1
  125. package/dist/http/routes/agents-roster.d.ts +1 -1
  126. package/dist/http/routes/approvals-assistant.d.ts +6 -6
  127. package/dist/http/routes/approvals-assistant.js +4 -1
  128. package/dist/http/routes/capabilities.d.ts +1 -1
  129. package/dist/http/routes/capabilities.js +1 -0
  130. package/dist/http/routes/config-catalog.d.ts +28 -0
  131. package/dist/http/routes/config-catalog.js +42 -0
  132. package/dist/http/routes/diagnostics.d.ts +2 -2
  133. package/dist/http/routes/fleet.d.ts +1 -1
  134. package/dist/http/routes/images.d.ts +1 -1
  135. package/dist/http/routes/leader.d.ts +1 -1
  136. package/dist/http/routes/memory-bundle.d.ts +4 -4
  137. package/dist/http/routes/memory-compliance.d.ts +7 -7
  138. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  139. package/dist/http/routes/memory-origin.d.ts +9 -9
  140. package/dist/http/routes/memory-policy.d.ts +3 -3
  141. package/dist/http/routes/notify-wake.d.ts +1 -1
  142. package/dist/http/routes/observability.d.ts +1 -1
  143. package/dist/http/routes/retention-ops.d.ts +1 -1
  144. package/dist/http/routes/rules.d.ts +3 -3
  145. package/dist/http/routes/run-memory-capture.d.ts +1 -1
  146. package/dist/http/routes/runs.d.ts +3 -3
  147. package/dist/http/routes/runs.js +12 -17
  148. package/dist/http/routes/session-sync.d.ts +2 -2
  149. package/dist/http/routes/sessions-list.d.ts +1 -1
  150. package/dist/http/routes/sessions.d.ts +9 -1
  151. package/dist/http/routes/sessions.js +60 -0
  152. package/dist/http/routes/shared-memory.d.ts +1 -1
  153. package/dist/http/routes/side-query.d.ts +1 -1
  154. package/dist/http/routes/tasks.d.ts +1 -1
  155. package/dist/http/routes/trace-usage.d.ts +1 -1
  156. package/dist/http/routes/workflows.d.ts +1 -1
  157. package/dist/http/run-meta.d.ts +1 -1
  158. package/dist/http/send.d.ts +6 -6
  159. package/dist/http/server.d.ts +84 -67
  160. package/dist/http/server.js +39 -10
  161. package/dist/http/sse-lifecycle.d.ts +1 -1
  162. package/dist/http/sse-log.d.ts +6 -6
  163. package/dist/http/verify-rounds.d.ts +3 -3
  164. package/dist/http/wire-gate.d.ts +2 -2
  165. package/dist/http/wire-types.d.ts +57 -38
  166. package/dist/key-resolver.d.ts +1 -1
  167. package/dist/leader/diffout.d.ts +2 -2
  168. package/dist/leader/diffup.d.ts +1 -1
  169. package/dist/leader/endpoint.d.ts +1 -1
  170. package/dist/leader/fanout.d.ts +6 -6
  171. package/dist/leader/grader-env-factory.d.ts +1 -1
  172. package/dist/leader/leader.d.ts +12 -12
  173. package/dist/leader/merge.d.ts +3 -3
  174. package/dist/leader/planner.d.ts +5 -5
  175. package/dist/leader/repair-oracle.d.ts +1 -1
  176. package/dist/leader/wire.d.ts +21 -9
  177. package/dist/leader/wire.js +6 -5
  178. package/dist/lsp/manager.d.ts +2 -2
  179. package/dist/lsp/types.d.ts +1 -1
  180. package/dist/lsp/ws-transport.d.ts +2 -2
  181. package/dist/main.js +12 -2
  182. package/dist/mcp-content-origin.d.ts +1 -1
  183. package/dist/memory-bundle-engine.d.ts +5 -3
  184. package/dist/memory-bundle-engine.js +2 -2
  185. package/dist/memory-operator-faces.d.ts +41 -11
  186. package/dist/memory-operator-faces.js +12 -0
  187. package/dist/memory-origin-fence.d.ts +1 -1
  188. package/dist/memory-origin-fence.js +2 -13
  189. package/dist/memory-posture.d.ts +6 -6
  190. package/dist/memory-scope.d.ts +7 -7
  191. package/dist/model-select.d.ts +5 -5
  192. package/dist/observability/cost-quota.d.ts +6 -6
  193. package/dist/observability/cost-taxonomy.d.ts +2 -2
  194. package/dist/observability/fail-open.d.ts +17 -5
  195. package/dist/observability/fail-open.js +16 -4
  196. package/dist/observability/metrics.d.ts +1 -1
  197. package/dist/observability/prompt-manifest.d.ts +8 -8
  198. package/dist/observability/rate-limit.d.ts +2 -2
  199. package/dist/observability/secret-env-scrub.d.ts +1 -1
  200. package/dist/observability/tool-trace.d.ts +1 -1
  201. package/dist/orchestration/hardened-vm-runner.d.ts +6 -6
  202. package/dist/orchestration/hardened-vm-worker-runner.d.ts +1 -1
  203. package/dist/orchestration/subagent-steer.d.ts +3 -3
  204. package/dist/orchestration/workflow-agent-steer.d.ts +1 -1
  205. package/dist/orchestration/workflow-completion-inbox.d.ts +6 -6
  206. package/dist/orchestration/workflow-notify-journal.d.ts +6 -6
  207. package/dist/parent-watch.d.ts +1 -1
  208. package/dist/parked-decide.d.ts +11 -11
  209. package/dist/plan-cache-probe.d.ts +1 -1
  210. package/dist/plugins/adoption-log-sql.d.ts +4 -4
  211. package/dist/plugins/approval-ask-store-memory.d.ts +1 -1
  212. package/dist/plugins/approval-ask-store-memory.js +12 -0
  213. package/dist/plugins/approval-ask-store-sql.d.ts +39 -17
  214. package/dist/plugins/approval-ask-store-sql.js +23 -6
  215. package/dist/plugins/approval-payload-fingerprint.d.ts +23 -0
  216. package/dist/plugins/approval-payload-fingerprint.js +23 -0
  217. package/dist/plugins/background-agent-store-sql.d.ts +8 -8
  218. package/dist/plugins/background-shell-support.d.ts +12 -12
  219. package/dist/plugins/blob-backend.d.ts +3 -3
  220. package/dist/plugins/breaker-state-sql.d.ts +3 -3
  221. package/dist/plugins/caching-session-store.d.ts +6 -6
  222. package/dist/plugins/checkpoint-store-sql.d.ts +162 -66
  223. package/dist/plugins/checkpoint-store-sql.js +76 -33
  224. package/dist/plugins/device-store-sql.d.ts +4 -3
  225. package/dist/plugins/e2b-orphan-reclaim.d.ts +3 -3
  226. package/dist/plugins/file-history-store-sql.d.ts +9 -8
  227. package/dist/plugins/file-run-store.d.ts +27 -27
  228. package/dist/plugins/fork-routing-session-store.d.ts +9 -9
  229. package/dist/plugins/image-bake-store-sql.d.ts +1 -1
  230. package/dist/plugins/image-index-sql.d.ts +2 -2
  231. package/dist/plugins/k8s-bg-scripts.d.ts +1 -1
  232. package/dist/plugins/k8s-exec-protocol.d.ts +1 -1
  233. package/dist/plugins/leader-run-store-sql.d.ts +1 -1
  234. package/dist/plugins/local-checkpoint-store.d.ts +26 -17
  235. package/dist/plugins/local-checkpoint-store.js +21 -11
  236. package/dist/plugins/local-session-store.d.ts +5 -5
  237. package/dist/plugins/mailbox-store-sql.d.ts +6 -6
  238. package/dist/plugins/memory-embedder-fingerprint.d.ts +2 -2
  239. package/dist/plugins/memory-engine-pg.d.ts +1 -1
  240. package/dist/plugins/memory-engine-tidb.d.ts +1 -1
  241. package/dist/plugins/memory-key-guards.d.ts +3 -3
  242. package/dist/plugins/memory-optout-grant-store-sql.d.ts +10 -10
  243. package/dist/plugins/memory-origin-law.d.ts +13 -13
  244. package/dist/plugins/memory-run-store.d.ts +12 -12
  245. package/dist/plugins/outcome-ledger-sql.d.ts +3 -3
  246. package/dist/plugins/permission-rule-store-file.d.ts +3 -3
  247. package/dist/plugins/permission-rule-store-sql.d.ts +38 -33
  248. package/dist/plugins/permission-rule-store-sql.js +7 -12
  249. package/dist/plugins/pg-cost-quota.d.ts +2 -2
  250. package/dist/plugins/pg-pool.d.ts +29 -2
  251. package/dist/plugins/pg-pool.js +50 -2
  252. package/dist/plugins/pg-query.d.ts +1 -1
  253. package/dist/plugins/pg-rate-limiter.d.ts +2 -2
  254. package/dist/plugins/pg-session-storage.d.ts +9 -9
  255. package/dist/plugins/remote-env-adb.d.ts +1 -1
  256. package/dist/plugins/remote-env-device.d.ts +6 -4
  257. package/dist/plugins/remote-env-device.js +4 -1
  258. package/dist/plugins/remote-env-e2b.d.ts +11 -11
  259. package/dist/plugins/remote-env-file-error.d.ts +2 -2
  260. package/dist/plugins/remote-env-host.d.ts +10 -10
  261. package/dist/plugins/remote-env-k8s.d.ts +7 -7
  262. package/dist/plugins/remote-env-local-docker.d.ts +1 -1
  263. package/dist/plugins/remote-env-ssh.d.ts +9 -9
  264. package/dist/plugins/remote-scratchpad.d.ts +1 -1
  265. package/dist/plugins/resume-anchor-store-sql.d.ts +1 -1
  266. package/dist/plugins/retention-lane-store-sql.d.ts +2 -2
  267. package/dist/plugins/retention-store-sql.d.ts +11 -11
  268. package/dist/plugins/roster-store-sql.d.ts +6 -8
  269. package/dist/plugins/run-store-sql.d.ts +12 -12
  270. package/dist/plugins/scheduler-support.d.ts +1 -1
  271. package/dist/plugins/send-user-file.d.ts +1 -1
  272. package/dist/plugins/session-capture-record-store-sql.d.ts +99 -0
  273. package/dist/plugins/session-capture-record-store-sql.js +110 -0
  274. package/dist/plugins/session-placement.d.ts +1 -1
  275. package/dist/plugins/session-policy-store-sql.d.ts +2 -2
  276. package/dist/plugins/session-store.d.ts +3 -3
  277. package/dist/plugins/shared-memory-store-sql.d.ts +5 -5
  278. package/dist/plugins/sql-driver.d.ts +7 -7
  279. package/dist/plugins/sql-errors.d.ts +2 -2
  280. package/dist/plugins/sql-escape.d.ts +1 -1
  281. package/dist/plugins/sql-row-helpers.d.ts +1 -1
  282. package/dist/plugins/store-backend.d.ts +32 -19
  283. package/dist/plugins/store-backend.js +5 -1
  284. package/dist/plugins/store-contracts.d.ts +9 -9
  285. package/dist/plugins/task-list-store-sql.d.ts +3 -3
  286. package/dist/plugins/tidb-cost-quota.d.ts +3 -3
  287. package/dist/plugins/tidb-pool.d.ts +13 -0
  288. package/dist/plugins/tidb-pool.js +9 -0
  289. package/dist/plugins/tidb-rate-limiter.d.ts +2 -2
  290. package/dist/plugins/tidb-session-storage.d.ts +2 -2
  291. package/dist/plugins/tidb-session-store.d.ts +11 -11
  292. package/dist/plugins/tool-result-store-sql.d.ts +8 -8
  293. package/dist/plugins/usage-window-store-sql.d.ts +3 -3
  294. package/dist/plugins/web-search.d.ts +7 -7
  295. package/dist/plugins/workflow-journal-store-sql.d.ts +6 -6
  296. package/dist/plugins/workflow-run-store-sql.d.ts +1 -1
  297. package/dist/plugins/write-behind-counter.d.ts +6 -6
  298. package/dist/principal-jwt.d.ts +2 -2
  299. package/dist/project-memory.d.ts +1 -1
  300. package/dist/prompts-domain-validate.d.ts +8 -8
  301. package/dist/question.d.ts +3 -3
  302. package/dist/resource-suspend.d.ts +4 -4
  303. package/dist/resource-window.d.ts +3 -3
  304. package/dist/router/route-orchestration.d.ts +1 -1
  305. package/dist/rules-consent.d.ts +48 -40
  306. package/dist/run-local.d.ts +5 -5
  307. package/dist/runs.d.ts +19 -19
  308. package/dist/runtime-caps-resolver.d.ts +6 -6
  309. package/dist/runtime-governance.d.ts +11 -7
  310. package/dist/runtime-governance.js +23 -1
  311. package/dist/security.d.ts +13 -13
  312. package/dist/session-leaf-bus.d.ts +1 -1
  313. package/dist/session-sync-content.d.ts +4 -4
  314. package/dist/session-sync-kernel.d.ts +2 -2
  315. package/dist/session-sync.d.ts +13 -13
  316. package/dist/session-titler.d.ts +1 -1
  317. package/dist/session-watch.d.ts +3 -3
  318. package/dist/shared-memory-scope-authorizer.d.ts +4 -4
  319. package/dist/sighup-idle.d.ts +1 -1
  320. package/dist/spec-fields.d.ts +20 -20
  321. package/dist/ssh-host-key.d.ts +5 -5
  322. package/dist/store-live-probe.d.ts +2 -2
  323. package/dist/task-cwd.d.ts +11 -11
  324. package/dist/task-mcp.d.ts +5 -5
  325. package/dist/task-settings.d.ts +35 -35
  326. package/dist/task-workflow.d.ts +3 -3
  327. package/dist/text-bidi.d.ts +1 -1
  328. package/dist/tool-approval.d.ts +186 -182
  329. package/dist/tool-approval.js +247 -120
  330. package/dist/trace/core-keyset-guard.d.ts +19 -5
  331. package/dist/trace/engine-notice-wire.d.ts +7 -7
  332. package/dist/trace/engine-notice-wire.js +2 -0
  333. package/dist/trace/injection-tier.d.ts +11 -21
  334. package/dist/trace/injection-tier.js +3 -4
  335. package/dist/trace/ledger-events.d.ts +3 -3
  336. package/dist/trace/ledger-sink.d.ts +7 -7
  337. package/dist/trace/project.d.ts +31 -30
  338. package/dist/trace/project.js +1 -0
  339. package/dist/turn-activity.d.ts +4 -4
  340. package/dist/usage-analytics.d.ts +1 -1
  341. package/dist/wall-clock-jump-guard.d.ts +3 -3
  342. package/package.json +6 -6
@@ -3,6 +3,7 @@ import { uuidv7, MAX_RULE_TEXT_CHARS } from "@sema-agent/core";
3
3
  import { precheckCardRuleText } from "./rules-consent.js";
4
4
  import { redactDeep, redactSecrets } from "./trace/redact.js";
5
5
  import { hasBidiControls } from "./text-bidi.js";
6
+ import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerprint.js";
6
7
  import { createLogger } from "./observability/logger.js";
7
8
  import { recordFailOpen } from "./observability/fail-open.js";
8
9
  import { deriveAskId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
@@ -45,6 +46,20 @@ export const ASK_DECISION_CONSUMER_ABSENT = "absent";
45
46
  const DURABLE_CONVERGE_BACKOFF_MS = [50, 100, 200, 400, 800, 1600];
46
47
  const DURABLE_CONVERGE_READ_TIMEOUT_MS = 2_000;
47
48
  const DURABLE_CALL_TIMEOUT_MS = 5_000;
49
+ function ambiguousCasFailOpenTag(intent) {
50
+ switch (intent) {
51
+ case "expire":
52
+ return "server.approval.ambiguous-cas.expire";
53
+ case "cancel":
54
+ return "server.approval.ambiguous-cas.cancel";
55
+ case "unreachable":
56
+ return "server.approval.ambiguous-cas.unreachable";
57
+ default: {
58
+ const unreachable = intent;
59
+ throw new Error(`ambiguousCasFailOpenTag: unknown intent ${String(unreachable)}`);
60
+ }
61
+ }
62
+ }
48
63
  class ApprovalStoreDeadlineError extends Error {
49
64
  constructor(label, timeoutMs) {
50
65
  super(`approval store call ${label} timed out after ${timeoutMs}ms`);
@@ -194,14 +209,21 @@ function effectiveAskWindowMs(ttlMs, windowMarginMs, legDeadlineMonotonic, nowMo
194
209
  return Math.min(ttlMs, Math.max(0, legRemainingMs - windowMarginMs));
195
210
  }
196
211
  function replayTerminalAskRow(row) {
197
- if (row.state === "DECIDED")
198
- return row.decision === "approve";
212
+ if (row.state === "DECIDED") {
213
+ if (row.decision !== "approve")
214
+ return false;
215
+ return row.updatedInput !== undefined ? { allow: true, updatedInput: row.updatedInput } : true;
216
+ }
199
217
  if (row.state === "PARKING" || row.state === "PARKED")
200
218
  return "unavailable";
201
219
  return false;
202
220
  }
203
- function recordRowDerivedApproveReplay(where) {
204
- recordFailOpen("server.hitl.row-derived-approve-drops-updated-input", where);
221
+ function settleArgsOf(outcome) {
222
+ if (outcome === "unavailable")
223
+ return "unavailable";
224
+ if (typeof outcome === "boolean")
225
+ return { allowed: outcome };
226
+ return { allowed: outcome.allow, ...(outcome.updatedInput !== undefined ? { updatedInput: outcome.updatedInput } : {}) };
205
227
  }
206
228
  export class ToolApprovalCoordinator {
207
229
  als = new AsyncLocalStorage();
@@ -432,26 +454,32 @@ export class ToolApprovalCoordinator {
432
454
  return false;
433
455
  }
434
456
  }
435
- async readDecidedForRecovery(askId) {
457
+ async readRowForRecovery(askId, where) {
436
458
  if (!this.askStore)
437
- return undefined;
459
+ return { kind: "absent" };
460
+ let row;
438
461
  try {
439
- const row = await this.withStoreDeadline(this.askStore.getAsk(askId), "getAsk(expire-error-recovery)", DURABLE_CONVERGE_READ_TIMEOUT_MS);
440
- if (row === null || row.state !== "DECIDED")
441
- return undefined;
442
- if (replayTerminalAskRow(row) === true && this.abandonedEditedDecisionPending(askId)) {
443
- this.guardRowDerivedApprove(askId, true, "expire-error-recovery");
444
- return undefined;
445
- }
446
- if (this.editedDecisionInFlight(askId))
447
- return undefined;
448
- const replayed = replayTerminalAskRow(row);
449
- return typeof replayed === "boolean" ? replayed : undefined;
462
+ row = await this.withStoreDeadline(this.askStore.getAsk(askId), `getAsk(${where})`, DURABLE_CONVERGE_READ_TIMEOUT_MS);
450
463
  }
451
464
  catch (err) {
452
- this.noteStoreError(err, "getAsk(expire-error-recovery)");
453
- return undefined;
465
+ this.noteStoreError(err, `getAsk(${where})`);
466
+ return { kind: "unreadable" };
454
467
  }
468
+ if (row === null)
469
+ return { kind: "absent" };
470
+ if (row.state === "STREAM_PENDING") {
471
+ return this.editedDecisionInFlight(askId) ? { kind: "edit-in-flight" } : { kind: "pending" };
472
+ }
473
+ const replayed = settleArgsOf(this.guardRowDerivedApprove(askId, replayTerminalAskRow(row), where));
474
+ if (replayed === "unavailable")
475
+ return { kind: "parked" };
476
+ if (!replayed.allowed)
477
+ return { kind: "denied" };
478
+ if (replayed.updatedInput !== undefined)
479
+ return { kind: "approved", updatedInput: replayed.updatedInput };
480
+ if (this.editedDecisionInFlight(askId))
481
+ return { kind: "edit-in-flight" };
482
+ return { kind: "approved" };
455
483
  }
456
484
  hasOtherSuspendedOwner(askId, self) {
457
485
  if (askId === undefined)
@@ -482,13 +510,17 @@ export class ToolApprovalCoordinator {
482
510
  const now = Date.now();
483
511
  let ownCount = 0;
484
512
  let ownExpiredVictim;
513
+ let ownBridgeVictim;
485
514
  let ownPlainVictim;
486
515
  let ownEvictable;
487
516
  let globalExpiredVictim;
517
+ let globalBridgeVictim;
488
518
  for (const [k, t] of this.parkTombstones) {
489
519
  const expired = now - t.parkedAtMs > this.parkTombstoneTtlMs;
490
520
  if (globalExpiredVictim === undefined && expired && t.inflight === undefined)
491
521
  globalExpiredVictim = k;
522
+ if (globalBridgeVictim === undefined && t.kind === "voidBridge" && t.inflight === undefined)
523
+ globalBridgeVictim = k;
492
524
  if (t.owner !== tomb.owner)
493
525
  continue;
494
526
  ownCount++;
@@ -498,13 +530,16 @@ export class ToolApprovalCoordinator {
498
530
  ownEvictable = k;
499
531
  if (ownExpiredVictim === undefined && expired)
500
532
  ownExpiredVictim = k;
501
- if (ownPlainVictim === undefined && t.redeemed === undefined)
533
+ if (ownBridgeVictim === undefined && t.kind === "voidBridge")
534
+ ownBridgeVictim = k;
535
+ if (ownPlainVictim === undefined && t.kind === "park" && t.redeemed === undefined)
502
536
  ownPlainVictim = k;
503
537
  }
538
+ const insertingBridge = tomb.kind === "voidBridge";
504
539
  if (ownCount >= effectivePerPrincipalMax) {
505
- const victim = ownExpiredVictim ?? ownPlainVictim ?? ownEvictable;
540
+ const victim = insertingBridge ? (ownExpiredVictim ?? ownBridgeVictim) : (ownExpiredVictim ?? ownBridgeVictim ?? ownPlainVictim ?? ownEvictable);
506
541
  if (victim === undefined) {
507
- recordFailOpen("server.approval.park-tombstone-global-cap-refused", `owner=${tomb.owner ?? "<none>"} reason=all-inflight`);
542
+ recordFailOpen("server.approval.park-tombstone-global-cap-refused", `owner=${tomb.owner ?? "<none>"} reason=${insertingBridge ? "bridge-would-evict-redeemable" : "all-inflight"}`);
508
543
  return;
509
544
  }
510
545
  this.parkTombstones.delete(victim);
@@ -512,8 +547,11 @@ export class ToolApprovalCoordinator {
512
547
  else if (this.parkTombstones.size >= this.parkTombstoneMax && globalExpiredVictim !== undefined) {
513
548
  this.parkTombstones.delete(globalExpiredVictim);
514
549
  }
550
+ else if (this.parkTombstones.size >= this.parkTombstoneMax && !insertingBridge && globalBridgeVictim !== undefined) {
551
+ this.parkTombstones.delete(globalBridgeVictim);
552
+ }
515
553
  else if (this.parkTombstones.size >= this.parkTombstoneMax) {
516
- recordFailOpen("server.approval.park-tombstone-global-cap-refused", `owner=${tomb.owner ?? "<none>"}`);
554
+ recordFailOpen("server.approval.park-tombstone-global-cap-refused", `owner=${tomb.owner ?? "<none>"}${insertingBridge ? " reason=bridge-refused-at-global-cap" : ""}`);
517
555
  return;
518
556
  }
519
557
  this.parkTombstones.set(approvalId, tomb);
@@ -745,8 +783,6 @@ export class ToolApprovalCoordinator {
745
783
  if (existingOrCreated.row.state !== "STREAM_PENDING") {
746
784
  releaseAdmission();
747
785
  const replayed = this.guardRowDerivedApprove(askId, replayTerminalAskRow(existingOrCreated.row), "ensure-ask-idempotent-replay");
748
- if (replayed === true)
749
- recordRowDerivedApproveReplay("ensure-ask-idempotent-replay");
750
786
  return replayed === "unavailable" ? this.unattendedOutcome() : replayed;
751
787
  }
752
788
  const persistedEnvelope = ApprovalCardEnvelopeSchema.safeParse(existingOrCreated.row.cardJson);
@@ -872,7 +908,7 @@ export class ToolApprovalCoordinator {
872
908
  sig.removeEventListener("abort", fn);
873
909
  this.pending.delete(id);
874
910
  if (windowRouteUnavailable && askId !== undefined && batchId !== undefined && this.unattendedOutcome() !== false) {
875
- this.recordParkTombstone(wireApprovalId, { askId, batchId, owner: origin.owner, parkedAtMs: Date.now() });
911
+ this.recordParkTombstone(wireApprovalId, { kind: "park", askId, batchId, owner: origin.owner, parkedAtMs: Date.now() });
876
912
  parkRouted = true;
877
913
  }
878
914
  if (askId !== undefined) {
@@ -892,40 +928,37 @@ export class ToolApprovalCoordinator {
892
928
  const convergeFromDurableState = async (where) => {
893
929
  if (!this.askStore || askId === undefined)
894
930
  return;
895
- const store = this.askStore;
896
931
  const pinnedAskId = askId;
897
- for (let attempt = 0; attempt < DURABLE_CONVERGE_BACKOFF_MS.length; attempt++) {
932
+ for (let attempt = 0; attempt <= DURABLE_CONVERGE_BACKOFF_MS.length; attempt++) {
898
933
  if (done)
899
934
  return;
900
- try {
901
- const row = await this.withStoreDeadline(store.getAsk(pinnedAskId), `getAsk(${where})`, DURABLE_CONVERGE_READ_TIMEOUT_MS);
902
- if (done)
903
- return;
904
- if (row === null) {
935
+ const read = await this.readRowForRecovery(pinnedAskId, `converge:${where}`);
936
+ if (done)
937
+ return;
938
+ switch (read.kind) {
939
+ case "absent":
905
940
  settle(false, "expired");
906
941
  return;
907
- }
908
- if (row.state === "STREAM_PENDING" || this.editedDecisionInFlight(pinnedAskId)) {
909
- await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
942
+ case "pending":
943
+ case "edit-in-flight":
944
+ case "unreadable":
945
+ if (attempt < DURABLE_CONVERGE_BACKOFF_MS.length)
946
+ await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
910
947
  continue;
911
- }
912
- const outcome = this.guardRowDerivedApprove(pinnedAskId, replayTerminalAskRow(row), `converge:${where}`);
913
- if (outcome === "unavailable") {
948
+ case "parked":
914
949
  windowRouteUnavailable = true;
915
950
  settle(false, "expired");
916
- }
917
- else if (outcome === true) {
918
- recordRowDerivedApproveReplay(`converge:${where}`);
919
- settle(true, "allowed");
920
- }
921
- else {
951
+ return;
952
+ case "approved":
953
+ settle(true, "allowed", read.updatedInput);
954
+ return;
955
+ case "denied":
922
956
  settle(false, "denied");
957
+ return;
958
+ default: {
959
+ const unreachable = read;
960
+ throw new Error(`convergeFromDurableState: unknown recovery read ${String(unreachable)}`);
923
961
  }
924
- return;
925
- }
926
- catch (err) {
927
- this.noteStoreError(err, `getAsk(${where})`);
928
- await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
929
962
  }
930
963
  }
931
964
  if (!done) {
@@ -938,6 +971,90 @@ export class ToolApprovalCoordinator {
938
971
  }
939
972
  }
940
973
  };
974
+ const recoverFromUnknownStore = async (arm, casStillInFlight) => {
975
+ if (askId === undefined) {
976
+ arm.settleByIntent();
977
+ return;
978
+ }
979
+ const pinnedAskId = askId;
980
+ for (let attempt = 0; attempt <= DURABLE_CONVERGE_BACKOFF_MS.length; attempt++) {
981
+ if (done)
982
+ return;
983
+ const read = await this.readRowForRecovery(pinnedAskId, `recovery:${arm.intent}`);
984
+ if (done)
985
+ return;
986
+ switch (read.kind) {
987
+ case "approved":
988
+ settle(true, "allowed", read.updatedInput);
989
+ return;
990
+ case "denied":
991
+ settle(false, "denied");
992
+ return;
993
+ case "parked":
994
+ windowRouteUnavailable = true;
995
+ settle(false, "expired");
996
+ return;
997
+ case "pending": {
998
+ if (casStillInFlight) {
999
+ arm.settleByIntent();
1000
+ return;
1001
+ }
1002
+ let result;
1003
+ try {
1004
+ result = await arm.cas();
1005
+ }
1006
+ catch (err) {
1007
+ this.noteStoreError(err, arm.storeLabel);
1008
+ arm.settleByIntent();
1009
+ return;
1010
+ }
1011
+ if (arm.won(result)) {
1012
+ arm.onWon(result);
1013
+ return;
1014
+ }
1015
+ await convergeFromDurableState(arm.where);
1016
+ return;
1017
+ }
1018
+ case "absent":
1019
+ arm.settleByIntent();
1020
+ return;
1021
+ case "edit-in-flight":
1022
+ case "unreadable":
1023
+ if (attempt < DURABLE_CONVERGE_BACKOFF_MS.length)
1024
+ await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
1025
+ continue;
1026
+ default: {
1027
+ const unreachable = read;
1028
+ throw new Error(`recoverFromUnknownStore: unknown recovery read ${String(unreachable)}`);
1029
+ }
1030
+ }
1031
+ }
1032
+ if (done)
1033
+ return;
1034
+ if (this.editedDecisionInFlight(pinnedAskId)) {
1035
+ windowRouteUnavailable = true;
1036
+ settle(false, "expired");
1037
+ return;
1038
+ }
1039
+ recordFailOpen(ambiguousCasFailOpenTag(arm.intent), `askId=${pinnedAskId} reads=${DURABLE_CONVERGE_BACKOFF_MS.length + 1}`);
1040
+ arm.settleByIntent();
1041
+ };
1042
+ const resolveLocalOutcome = async (arm) => {
1043
+ let result;
1044
+ try {
1045
+ result = await arm.cas();
1046
+ }
1047
+ catch (err) {
1048
+ this.noteStoreError(err, arm.storeLabel);
1049
+ await recoverFromUnknownStore(arm, err instanceof ApprovalStoreDeadlineError);
1050
+ return;
1051
+ }
1052
+ if (arm.won(result)) {
1053
+ arm.onWon(result);
1054
+ return;
1055
+ }
1056
+ await convergeFromDurableState(arm.where);
1057
+ };
941
1058
  const settleParkRouteIfUnsettled = (hostSettledBy) => {
942
1059
  if (done)
943
1060
  return;
@@ -950,36 +1067,30 @@ export class ToolApprovalCoordinator {
950
1067
  settleParkRouteIfUnsettled(hostSelfReport);
951
1068
  return;
952
1069
  }
953
- try {
954
- const res = await this.withStoreDeadline(this.askStore.expireAsk(askId, batchId), "expireAsk(window)", DURABLE_CALL_TIMEOUT_MS, (late) => {
1070
+ const store = this.askStore;
1071
+ const pinnedAskId = askId;
1072
+ const pinnedBatchId = batchId;
1073
+ await resolveLocalOutcome({
1074
+ intent: "expire",
1075
+ storeLabel: "expireAsk",
1076
+ where: "windowExpired",
1077
+ cas: () => this.withStoreDeadline(store.expireAsk(pinnedAskId, pinnedBatchId), "expireAsk(window)", DURABLE_CALL_TIMEOUT_MS, (late) => {
955
1078
  if (!late.won)
956
1079
  return;
957
- this.settleSameAskIdParkRoute(askId);
1080
+ this.settleSameAskIdParkRoute(pinnedAskId);
958
1081
  this.settleVoidedSiblings(late.voidedSiblings);
959
- this.emitRevokeTo(aliveCtxs, buildRevokeFrame(batchId, late.voidedSiblings, "superseded_by_park", Date.now()));
960
- });
961
- if (res.won) {
1082
+ this.emitRevokeTo(aliveCtxs, buildRevokeFrame(pinnedBatchId, late.voidedSiblings, "superseded_by_park", Date.now()));
1083
+ }),
1084
+ won: (res) => res.won,
1085
+ onWon: (res) => {
962
1086
  windowRouteUnavailable = true;
963
1087
  settle(false, "expired");
964
- this.settleSameAskIdParkRoute(askId);
1088
+ this.settleSameAskIdParkRoute(pinnedAskId);
965
1089
  this.settleVoidedSiblings(res.voidedSiblings);
966
- this.emitRevokeTo(aliveCtxs, buildRevokeFrame(batchId, res.voidedSiblings, "superseded_by_park", Date.now()));
967
- }
968
- else {
969
- await convergeFromDurableState("windowExpired");
970
- }
971
- }
972
- catch (err) {
973
- this.noteStoreError(err, "expireAsk");
974
- const decided = await this.readDecidedForRecovery(askId);
975
- if (decided !== undefined && !done) {
976
- if (decided)
977
- recordRowDerivedApproveReplay("expire-error-recovery");
978
- settle(decided, decided ? "allowed" : "denied");
979
- return;
980
- }
981
- settleParkRouteIfUnsettled(hostSelfReport);
982
- }
1090
+ this.emitRevokeTo(aliveCtxs, buildRevokeFrame(pinnedBatchId, res.voidedSiblings, "superseded_by_park", Date.now()));
1091
+ },
1092
+ settleByIntent: () => settleParkRouteIfUnsettled(hostSelfReport),
1093
+ });
983
1094
  })();
984
1095
  };
985
1096
  const forceParkNow = () => {
@@ -1002,26 +1113,29 @@ export class ToolApprovalCoordinator {
1002
1113
  settle(false, "expired");
1003
1114
  return;
1004
1115
  }
1005
- try {
1006
- const won = await this.withStoreDeadline(this.askStore.transitionAsk(askId, "STREAM_PENDING", "VOID", { updatedAtMs: Date.now() }), "transitionAsk(cancel)", DURABLE_CALL_TIMEOUT_MS, (lateWon) => {
1116
+ const store = this.askStore;
1117
+ const pinnedAskId = askId;
1118
+ const pinnedBatchId = batchId;
1119
+ await resolveLocalOutcome({
1120
+ intent: "cancel",
1121
+ storeLabel: "transitionAsk(cancel)",
1122
+ where: "runCancel",
1123
+ cas: () => this.withStoreDeadline(store.transitionAsk(pinnedAskId, "STREAM_PENDING", "VOID", { updatedAtMs: Date.now() }), "transitionAsk(cancel)", DURABLE_CALL_TIMEOUT_MS, (lateWon) => {
1007
1124
  if (!lateWon)
1008
1125
  return;
1009
- this.settleVoidedSiblings([askId]);
1010
- this.emitRevokeTo(aliveCtxs, buildRevokeFrame(batchId, [askId], "aborted", Date.now()));
1011
- });
1012
- if (won) {
1126
+ this.settleVoidedSiblings([pinnedAskId]);
1127
+ this.recordParkTombstone(wireApprovalId, { kind: "voidBridge", askId: pinnedAskId, batchId: pinnedBatchId, owner: origin.owner, parkedAtMs: Date.now() });
1128
+ this.emitRevokeTo(aliveCtxs, buildRevokeFrame(pinnedBatchId, [pinnedAskId], "aborted", Date.now()));
1129
+ }),
1130
+ won: (won) => won,
1131
+ onWon: () => {
1013
1132
  settle(false, "expired");
1014
- this.settleVoidedSiblings([askId]);
1015
- this.emitRevokeTo(aliveCtxs, buildRevokeFrame(batchId, [askId], "aborted", Date.now()));
1016
- }
1017
- else {
1018
- await convergeFromDurableState("runCancel");
1019
- }
1020
- }
1021
- catch (err) {
1022
- this.noteStoreError(err, "transitionAsk(cancel)");
1023
- settle(false, "expired");
1024
- }
1133
+ this.settleVoidedSiblings([pinnedAskId]);
1134
+ this.recordParkTombstone(wireApprovalId, { kind: "voidBridge", askId: pinnedAskId, batchId: pinnedBatchId, owner: origin.owner, parkedAtMs: Date.now() });
1135
+ this.emitRevokeTo(aliveCtxs, buildRevokeFrame(pinnedBatchId, [pinnedAskId], "aborted", Date.now()));
1136
+ },
1137
+ settleByIntent: () => settle(false, "expired"),
1138
+ });
1025
1139
  };
1026
1140
  if (signal) {
1027
1141
  const onTaskAbort = () => void runCancel();
@@ -1111,31 +1225,42 @@ export class ToolApprovalCoordinator {
1111
1225
  const emitAllP = (async () => {
1112
1226
  const results = await Promise.all(openByCtx.values());
1113
1227
  const anySucceeded = reachability === "unreached" || this.hasOtherSuspendedOwner(askId, selfEntry) || results.some((r) => r.legacy || r.card);
1228
+ emitSettled = true;
1114
1229
  if (!anySucceeded && !done) {
1115
1230
  if (this.askStore && askId !== undefined && batchId !== undefined) {
1116
- try {
1117
- const res = await this.withStoreDeadline(this.askStore.expireAsk(askId, batchId), "expireAsk(emit-failed)");
1118
- if (res.won && !done) {
1119
- emitFailed = true;
1120
- settle(false, "expired");
1121
- this.settleSameAskIdParkRoute(askId);
1231
+ const store = this.askStore;
1232
+ const pinnedAskId = askId;
1233
+ const pinnedBatchId = batchId;
1234
+ const settleUnreachable = () => {
1235
+ if (done)
1236
+ return;
1237
+ emitFailed = true;
1238
+ settle(false, "expired");
1239
+ };
1240
+ await resolveLocalOutcome({
1241
+ intent: "unreachable",
1242
+ storeLabel: "expireAsk(emit-failed)",
1243
+ where: "emitFailed",
1244
+ cas: () => this.withStoreDeadline(store.expireAsk(pinnedAskId, pinnedBatchId), "expireAsk(emit-failed)", DURABLE_CALL_TIMEOUT_MS, (late) => {
1245
+ if (!late.won)
1246
+ return;
1247
+ this.settleSameAskIdParkRoute(pinnedAskId);
1248
+ this.settleVoidedSiblings(late.voidedSiblings);
1249
+ }),
1250
+ won: (res) => res.won,
1251
+ onWon: (res) => {
1252
+ settleUnreachable();
1253
+ this.settleSameAskIdParkRoute(pinnedAskId);
1122
1254
  this.settleVoidedSiblings(res.voidedSiblings);
1123
- }
1124
- }
1125
- catch (err) {
1126
- this.noteStoreError(err, "expireAsk(emit-failed)");
1127
- if (!done) {
1128
- emitFailed = true;
1129
- settle(false, "expired");
1130
- }
1131
- }
1255
+ },
1256
+ settleByIntent: settleUnreachable,
1257
+ });
1132
1258
  }
1133
1259
  else if (!done) {
1134
1260
  emitFailed = true;
1135
1261
  settle(false, "expired");
1136
1262
  }
1137
1263
  }
1138
- emitSettled = true;
1139
1264
  })();
1140
1265
  await Promise.race([emitAllP, allowedP]);
1141
1266
  const settled = await allowedP;
@@ -1288,9 +1413,7 @@ export class ToolApprovalCoordinator {
1288
1413
  });
1289
1414
  return this.notPending();
1290
1415
  }
1291
- const settled = this.notifyExternalDecision(row.askId, row.decision === "approve");
1292
- if (row.decision === "approve" && settled.settled > 0)
1293
- recordRowDerivedApproveReplay("respond-late-replay");
1416
+ this.notifyExternalDecision(row.askId, row.decision === "approve", row.updatedInput);
1294
1417
  return {
1295
1418
  status: 200,
1296
1419
  body: {
@@ -1559,11 +1682,13 @@ export class ToolApprovalCoordinator {
1559
1682
  }
1560
1683
  async respondWithCas(askStore, askId, batchId, id, entry, parsed, ruleEligibility) {
1561
1684
  let decidedWon = false;
1685
+ let confirmedRowPayload;
1562
1686
  let noteLanded = false;
1563
1687
  try {
1564
1688
  const decided = await this.withStoreDeadline(askStore.decideAsk(askId, batchId, {
1565
1689
  decision: parsed.value === "deny" ? "deny" : "approve",
1566
1690
  ...(parsed.note !== undefined ? { decisionNote: parsed.note } : {}),
1691
+ ...(parsed.value !== "deny" && parsed.updatedInput !== undefined ? { updatedInput: parsed.updatedInput } : {}),
1567
1692
  }), "decideAsk", DURABLE_CALL_TIMEOUT_MS, (late) => {
1568
1693
  if (late.ok)
1569
1694
  this.notifyExternalDecision(askId, parsed.value !== "deny", parsed.updatedInput);
@@ -1582,6 +1707,7 @@ export class ToolApprovalCoordinator {
1582
1707
  const row = await this.withStoreDeadline(askStore.getAsk(askId), "getAsk(decideAsk-confirm)", DURABLE_CONVERGE_READ_TIMEOUT_MS);
1583
1708
  if (row?.state === "DECIDED" && row.decision === (parsed.value === "deny" ? "deny" : "approve")) {
1584
1709
  decidedWon = true;
1710
+ confirmedRowPayload = { updatedInput: row.updatedInput };
1585
1711
  noteLanded = parsed.note === undefined || row.decisionNote === parsed.note;
1586
1712
  }
1587
1713
  }
@@ -1592,15 +1718,20 @@ export class ToolApprovalCoordinator {
1592
1718
  const wantAllowed = parsed.value !== "deny";
1593
1719
  if (this.pending.get(id) !== entry) {
1594
1720
  if (decidedWon)
1595
- this.notifyExternalDecision(askId, wantAllowed, parsed.updatedInput);
1721
+ this.notifyExternalDecision(askId, wantAllowed, confirmedRowPayload !== undefined ? confirmedRowPayload.updatedInput : parsed.updatedInput);
1596
1722
  const converged = decidedWon && entry.settledOutcome === (wantAllowed ? "allowed" : "denied");
1597
1723
  if (!converged) {
1598
1724
  return this.notPending();
1599
1725
  }
1600
1726
  return this.finishRespond(id, entry, parsed, { updatedInputDelivered: false, noteRecorded: noteLanded });
1601
1727
  }
1602
- const result = this.finishRespond(id, entry, parsed, { noteRecorded: noteLanded });
1603
- this.notifyExternalDecision(askId, wantAllowed, parsed.updatedInput);
1728
+ const payloadIsMine = confirmedRowPayload === undefined ||
1729
+ approvalPayloadFingerprint({ updatedInput: confirmedRowPayload.updatedInput }) === approvalPayloadFingerprint({ updatedInput: parsed.updatedInput });
1730
+ const result = this.finishRespond(id, entry, parsed, {
1731
+ noteRecorded: noteLanded,
1732
+ ...(confirmedRowPayload !== undefined ? { settlePayload: confirmedRowPayload, updatedInputDelivered: payloadIsMine } : {}),
1733
+ });
1734
+ this.notifyExternalDecision(askId, wantAllowed, confirmedRowPayload !== undefined ? confirmedRowPayload.updatedInput : parsed.updatedInput);
1604
1735
  return result;
1605
1736
  }
1606
1737
  finishRespond(id, entry, parsed, opts) {
@@ -1617,7 +1748,7 @@ export class ToolApprovalCoordinator {
1617
1748
  this.allowAllSessions.add(sessionAllowKey(entry.owner, entry.sessionId, sessionAllowCategory(entry.toolName)));
1618
1749
  }
1619
1750
  const allowed = parsed.value !== "deny";
1620
- entry.settle(allowed, allowed ? "allowed" : "denied", parsed.updatedInput, undefined, allowed ? undefined : parsed.note);
1751
+ entry.settle(allowed, allowed ? "allowed" : "denied", opts?.settlePayload !== undefined ? opts.settlePayload.updatedInput : parsed.updatedInput, undefined, allowed ? undefined : parsed.note);
1621
1752
  const updatedInputDelivered = opts?.updatedInputDelivered ?? true;
1622
1753
  const noteRecorded = opts?.noteRecorded ?? false;
1623
1754
  return {
@@ -1687,18 +1818,14 @@ export class ToolApprovalCoordinator {
1687
1818
  continue;
1688
1819
  if (this.editedDecisionInFlight(askId))
1689
1820
  continue;
1690
- const outcome = replayTerminalAskRow(row);
1821
+ const outcome = settleArgsOf(replayTerminalAskRow(row));
1691
1822
  if (outcome === "unavailable") {
1692
1823
  const before = this.pendingByAskId.get(askId)?.size ?? 0;
1693
1824
  this.settleSameAskIdParkRoute(askId);
1694
1825
  settled += before;
1695
1826
  continue;
1696
1827
  }
1697
- if (typeof outcome !== "boolean")
1698
- continue;
1699
- const delivered = this.notifyExternalDecision(askId, outcome).settled;
1700
- if (outcome && delivered > 0)
1701
- recordRowDerivedApproveReplay("poll-suspended");
1828
+ const delivered = this.notifyExternalDecision(askId, outcome.allowed, outcome.updatedInput).settled;
1702
1829
  settled += delivered;
1703
1830
  }
1704
1831
  const backlog = Math.max(0, candidates.length - polled);
@@ -1,9 +1,9 @@
1
1
  /**
2
- * L1 键集机器门([1549] 方法论倡议 server 认领件;clay 令 2026-07-23「不能永远编码有限→复审补漏」)。
2
+ * L1 键集机器门([ref] 方法论倡议 server 认领件;clay 令 2026-07-23「不能永远编码有限→复审补漏」)。
3
3
  *
4
4
  * 病族:**投影白名单剥键**——core 给源类型加 additive 新键,server 的列投影/白名单静默丢,直到穿透
5
- * 审查才被人肉抓。已四案:RosterEntry.model([1532])、task_progress.status / TaskNotificationPayload
6
- * 四键 / BackgroundChildEvent.usage+transcriptId([1543] 族B)、SDK 0.0.72 fleet meta 剥键。
5
+ * 审查才被人肉抓。已四案:RosterEntry.model([ref])、task_progress.status / TaskNotificationPayload
6
+ * 四键 / BackgroundChildEvent.usage+transcriptId([ref] 族B)、SDK 0.0.72 fleet meta 剥键。
7
7
  *
8
8
  * 机制:对每个「core 源类型 → server 投影」面声明**已处置键集** = PROJECTED(真透传)∪ EXCLUDED
9
9
  * (显式记账不透传,理由必写)。`Exclude<keyof 源, 已处置>` 约束为 never——**升 core 后源类型出新键
@@ -14,8 +14,10 @@
14
14
  *
15
15
  * 维护:core 新键到来时,transparently 透传的加进对应 PROJECTED 并去实现投影;有意不上 wire 的加进
16
16
  * EXCLUDED 并写一行理由。谁改投影(project.ts / fleet-bus.ts / roster-store-sql.ts)谁同步本文件。
17
+ * ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
18
+ * TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
17
19
  */
18
- import type { TaskNotificationPayload, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxStore } from "@sema-agent/core";
20
+ import type { TaskNotificationPayload, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxStore, TaskSpec, TaskResult } from "@sema-agent/core";
19
21
  /** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。 */
20
22
  type AssertAllKeysHandled<T extends never> = T;
21
23
  type NotificationProjected = "task_id" | "task_type" | "toolUseId" | "status" | "summary" | "result" | "output_file" | "usage" | "sessionId" | "seq" | "lines" | "stoppedBy" | "source" | "exitCode" | "partial" | "diagnostics" | "recentSteps" | "editedFiles" | "resumable" | "completionId" | "error" | "errorCode" | "apiFailure";
@@ -47,6 +49,14 @@ type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<Permiss
47
49
  type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls";
48
50
  type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld";
49
51
  type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
52
+ type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends";
53
+ type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars";
54
+ type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
55
+ type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
56
+ type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "status" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "blockedReason" | "checkpointId" | "checkpointGate" | "workspaceRestoreMode" | "remoteEnvFailures" | "errorMessage" | "errorCode" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveMemoryScopes" | "stats";
57
+ type TaskResultStripped = "checkpointToken";
58
+ type _GuardTaskResult = AssertAllKeysHandled<Exclude<keyof TaskResult, TaskResultForwarded | TaskResultStripped>>;
59
+ type _GuardTaskResultReverse = AssertAllKeysHandled<Exclude<TaskResultForwarded | TaskResultStripped, keyof TaskResult>>;
50
60
  export type CoreKeysetGuards = [
51
61
  _GuardNotification,
52
62
  _GuardBgNotif,
@@ -58,7 +68,11 @@ export type CoreKeysetGuards = [
58
68
  _GuardPermissionTrace,
59
69
  _GuardPermissionTraceReverse,
60
70
  _GuardPermissionTraceNonEmpty,
61
- _GuardInboxSummary
71
+ _GuardInboxSummary,
72
+ _GuardTaskSpec,
73
+ _GuardTaskSpecReverse,
74
+ _GuardTaskResult,
75
+ _GuardTaskResultReverse
62
76
  ];
63
77
  export {};
64
78
  //# sourceMappingURL=core-keyset-guard.d.ts.map