@sema-agent/server 7.137.0 → 7.139.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 (328) hide show
  1. package/MIGRATION.md +600 -142
  2. package/README.md +7 -3
  3. package/README.zh-CN.md +6 -3
  4. package/USAGE.md +132 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/wire.d.ts +3 -3
  8. package/dist/approval-card.d.ts +23 -16
  9. package/dist/approval-card.js +1 -0
  10. package/dist/audit.js +1 -0
  11. package/dist/background-agent-name.d.ts +5 -0
  12. package/dist/background-agent-name.js +5 -0
  13. package/dist/bench/s1/arms.d.ts +1 -1
  14. package/dist/bench/s1/arms.js +1 -3
  15. package/dist/bench/s1/run-firm.js +2 -2
  16. package/dist/boot/budget-tracing.d.ts +1 -12
  17. package/dist/boot/deployment-mods.d.ts +46 -16
  18. package/dist/boot/deployment-mods.js +82 -6
  19. package/dist/boot/device-lane.d.ts +8 -3
  20. package/dist/boot/device-lane.js +7 -2
  21. package/dist/boot/execution-env.d.ts +4 -2
  22. package/dist/boot/execution-env.js +16 -24
  23. package/dist/boot/execution-lane.d.ts +1 -1
  24. package/dist/boot/governance-seams.d.ts +12 -18
  25. package/dist/boot/governance-seams.js +10 -24
  26. package/dist/boot/leader.d.ts +3 -0
  27. package/dist/boot/leader.js +1 -0
  28. package/dist/boot/memory-consolidation.js +2 -2
  29. package/dist/boot/org-delivery.js +15 -2
  30. package/dist/boot/resolve-spec.d.ts +4 -1
  31. package/dist/boot/resolve-spec.js +29 -19
  32. package/dist/boot/retention-lane.d.ts +14 -8
  33. package/dist/boot/retention-lane.js +2 -8
  34. package/dist/boot/runner-deps.d.ts +10 -4
  35. package/dist/boot/runner-deps.js +2 -0
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-04-stores.d.ts +1 -12
  40. package/dist/boot/stage-05-execution-env.d.ts +3 -12
  41. package/dist/boot/stage-05-execution-env.js +8 -5
  42. package/dist/boot/stage-06-runners.d.ts +3 -12
  43. package/dist/boot/stage-06-runners.js +5 -3
  44. package/dist/boot/stage-07-capability-layer.d.ts +3 -12
  45. package/dist/boot/stage-07-capability-layer.js +2 -2
  46. package/dist/boot/stage-08-reapers.d.ts +3 -12
  47. package/dist/boot/stage-09-leader.d.ts +3 -12
  48. package/dist/boot/stage-09-leader.js +2 -3
  49. package/dist/boot/stage-10-http-server.d.ts +3 -12
  50. package/dist/boot/stage-10-http-server.js +5 -3
  51. package/dist/boot/stores.js +4 -4
  52. package/dist/boot/web-admin.d.ts +5 -2
  53. package/dist/boot/web-admin.js +5 -5
  54. package/dist/brain.js +5 -5
  55. package/dist/budget.d.ts +17 -15
  56. package/dist/budget.js +2 -2
  57. package/dist/capabilities/mcp-probe.d.ts +36 -11
  58. package/dist/capabilities/mcp-probe.js +44 -5
  59. package/dist/capabilities/scenarios.d.ts +1 -1
  60. package/dist/capability-facts.d.ts +21 -7
  61. package/dist/capability-facts.js +13 -3
  62. package/dist/checkpoint-identity.d.ts +13 -6
  63. package/dist/config-catalog.js +14 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +19 -24
  67. package/dist/config-provider.js +27 -12
  68. package/dist/config-types.d.ts +25 -12
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +52 -20
  71. package/dist/delegated-session-owner.d.ts +2 -2
  72. package/dist/deployment-shape.d.ts +7 -7
  73. package/dist/device-enrollment.d.ts +41 -2
  74. package/dist/device-enrollment.js +27 -12
  75. package/dist/device-store.d.ts +58 -85
  76. package/dist/device-store.js +19 -49
  77. package/dist/device-ws-hub.js +2 -1
  78. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  79. package/dist/exec-cut.d.ts +60 -0
  80. package/dist/exec-cut.js +36 -0
  81. package/dist/execution-lane-caps.d.ts +19 -0
  82. package/dist/execution-lane-caps.js +10 -0
  83. package/dist/execution-placement.d.ts +259 -0
  84. package/dist/execution-placement.js +199 -0
  85. package/dist/field-conflict.d.ts +3 -2
  86. package/dist/field-conflict.js +1 -0
  87. package/dist/gate-settlement.d.ts +71 -0
  88. package/dist/gate-settlement.js +40 -0
  89. package/dist/hooks/classic-stations.d.ts +1 -0
  90. package/dist/hooks/classic-stations.js +1 -0
  91. package/dist/hooks/command-spawn.d.ts +2 -2
  92. package/dist/hooks/hook-runner.js +14 -14
  93. package/dist/hosted-posture.d.ts +2 -2
  94. package/dist/http/admission.js +10 -19
  95. package/dist/http/checkpoint-ctx.d.ts +21 -12
  96. package/dist/http/checkpoint-ctx.js +4 -2
  97. package/dist/http/claimed-run.d.ts +1 -1
  98. package/dist/http/dispatch.d.ts +4 -0
  99. package/dist/http/dispatch.js +3 -0
  100. package/dist/http/durable-run-leg.js +12 -6
  101. package/dist/http/execution-context.d.ts +218 -0
  102. package/dist/http/execution-context.js +330 -0
  103. package/dist/http/memory-compliance-failure-reply.js +3 -0
  104. package/dist/http/resume-legs.d.ts +5 -11
  105. package/dist/http/resume-legs.js +179 -104
  106. package/dist/http/route-ctx.d.ts +44 -59
  107. package/dist/http/route-ctx.js +2 -5
  108. package/dist/http/routes/a2a-serve.js +10 -4
  109. package/dist/http/routes/agents-roster.d.ts +1 -1
  110. package/dist/http/routes/agents-roster.js +5 -1
  111. package/dist/http/routes/approvals-assistant.js +6 -6
  112. package/dist/http/routes/background.d.ts +29 -1
  113. package/dist/http/routes/background.js +29 -2
  114. package/dist/http/routes/capabilities.js +12 -10
  115. package/dist/http/routes/devices.d.ts +13 -19
  116. package/dist/http/routes/devices.js +21 -26
  117. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  118. package/dist/http/routes/mcp-reconnect.js +7 -5
  119. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  120. package/dist/http/routes/memory-origin.d.ts +1 -1
  121. package/dist/http/routes/rules.js +3 -3
  122. package/dist/http/routes/runs.js +11 -7
  123. package/dist/http/routes/session-sync.js +110 -15
  124. package/dist/http/routes/sessions.d.ts +6 -0
  125. package/dist/http/routes/sessions.js +119 -13
  126. package/dist/http/routes/side-query.d.ts +12 -4
  127. package/dist/http/routes/side-query.js +6 -8
  128. package/dist/http/routes/tasks.js +800 -782
  129. package/dist/http/server.d.ts +61 -39
  130. package/dist/http/server.js +11 -0
  131. package/dist/http/session-inherit-env.d.ts +3 -4
  132. package/dist/http/session-inherit-env.js +1 -1
  133. package/dist/http/wire-types.d.ts +22 -18
  134. package/dist/identity-source.d.ts +1 -1
  135. package/dist/index.d.ts +3 -0
  136. package/dist/index.js +1 -0
  137. package/dist/leader/wire.d.ts +7 -0
  138. package/dist/leader/wire.js +5 -4
  139. package/dist/mcp-assertion-header.d.ts +51 -40
  140. package/dist/mcp-assertion-header.js +10 -11
  141. package/dist/mcp-authorization-face.d.ts +27 -0
  142. package/dist/mcp-authorization-face.js +15 -0
  143. package/dist/memory-operator-faces.d.ts +13 -4
  144. package/dist/memory-operator-faces.js +1 -17
  145. package/dist/memory-sync-client.d.ts +53 -14
  146. package/dist/memory-sync-client.js +187 -92
  147. package/dist/memory-sync.d.ts +1 -1
  148. package/dist/memory-sync.js +3 -2
  149. package/dist/model-route-gap.d.ts +6 -22
  150. package/dist/model-route-gap.js +7 -5
  151. package/dist/mods-declaration.d.ts +110 -0
  152. package/dist/mods-declaration.js +109 -0
  153. package/dist/observability/cost-taxonomy.d.ts +2 -2
  154. package/dist/observability/err-text.d.ts +5 -0
  155. package/dist/observability/err-text.js +9 -0
  156. package/dist/observability/fail-open.d.ts +17 -1
  157. package/dist/observability/fail-open.js +17 -1
  158. package/dist/observability/run-terminal.js +0 -1
  159. package/dist/observability/secret-env-scrub.d.ts +1 -1
  160. package/dist/observability/secret-env-scrub.js +1 -1
  161. package/dist/orchestration/workflow-notify-journal.js +4 -4
  162. package/dist/org-memory-admission.d.ts +4 -3
  163. package/dist/org-memory-admission.js +6 -6
  164. package/dist/parked-decide.d.ts +49 -11
  165. package/dist/parked-decide.js +25 -26
  166. package/dist/parked-revive-surface.d.ts +12 -15
  167. package/dist/parked-revive-surface.js +3 -6
  168. package/dist/peer-seat.d.ts +156 -0
  169. package/dist/peer-seat.js +273 -0
  170. package/dist/permission-rule-vocab.d.ts +7 -7
  171. package/dist/permission-rule-vocab.js +3 -3
  172. package/dist/plain-string-record.d.ts +38 -0
  173. package/dist/plain-string-record.js +38 -0
  174. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  175. package/dist/plugins/approval-ask-store-file.js +11 -0
  176. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  177. package/dist/plugins/approval-ask-store-memory.js +12 -0
  178. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  179. package/dist/plugins/approval-ask-store-sql.js +30 -12
  180. package/dist/plugins/background-agent-store-sql.js +6 -3
  181. package/dist/plugins/caching-session-store.d.ts +11 -2
  182. package/dist/plugins/caching-session-store.js +15 -3
  183. package/dist/plugins/checkpoint-store-sql.d.ts +21 -25
  184. package/dist/plugins/checkpoint-store-sql.js +13 -15
  185. package/dist/plugins/device-store-sql.d.ts +25 -51
  186. package/dist/plugins/device-store-sql.js +49 -103
  187. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  188. package/dist/plugins/execution-placement-sql.js +223 -0
  189. package/dist/plugins/file-history-store-sql.d.ts +7 -6
  190. package/dist/plugins/file-history-store-sql.js +8 -7
  191. package/dist/plugins/file-run-store.d.ts +6 -1
  192. package/dist/plugins/file-run-store.js +4 -0
  193. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  194. package/dist/plugins/fork-routing-session-store.js +16 -0
  195. package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
  196. package/dist/plugins/k8s-pod-reaper.js +3 -3
  197. package/dist/plugins/local-checkpoint-store.d.ts +1 -11
  198. package/dist/plugins/local-checkpoint-store.js +0 -1
  199. package/dist/plugins/local-session-store.d.ts +183 -45
  200. package/dist/plugins/local-session-store.js +412 -105
  201. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  202. package/dist/plugins/mailbox-store-sql.js +103 -15
  203. package/dist/plugins/memory-key-guards.d.ts +4 -0
  204. package/dist/plugins/memory-key-guards.js +1 -0
  205. package/dist/plugins/memory-run-store.d.ts +1 -1
  206. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  207. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  208. package/dist/plugins/one-time-migrations.d.ts +117 -6
  209. package/dist/plugins/one-time-migrations.js +383 -14
  210. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  211. package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
  212. package/dist/plugins/permission-rule-store-sql.js +34 -29
  213. package/dist/plugins/pg-pool.d.ts +7 -3
  214. package/dist/plugins/pg-session-storage.d.ts +41 -22
  215. package/dist/plugins/pg-session-storage.js +136 -55
  216. package/dist/plugins/remote-env-adb.d.ts +22 -4
  217. package/dist/plugins/remote-env-adb.js +70 -62
  218. package/dist/plugins/remote-env-device.d.ts +32 -12
  219. package/dist/plugins/remote-env-device.js +18 -17
  220. package/dist/plugins/remote-env-e2b.d.ts +19 -3
  221. package/dist/plugins/remote-env-e2b.js +41 -42
  222. package/dist/plugins/remote-env-file-error.d.ts +2 -0
  223. package/dist/plugins/remote-env-file-error.js +1 -1
  224. package/dist/plugins/remote-env-host.d.ts +23 -8
  225. package/dist/plugins/remote-env-host.js +66 -70
  226. package/dist/plugins/remote-env-k8s.d.ts +13 -16
  227. package/dist/plugins/remote-env-k8s.js +61 -54
  228. package/dist/plugins/remote-env-local-docker.d.ts +30 -5
  229. package/dist/plugins/remote-env-local-docker.js +46 -59
  230. package/dist/plugins/remote-env-ssh.d.ts +18 -4
  231. package/dist/plugins/remote-env-ssh.js +72 -56
  232. package/dist/plugins/remote-shell.d.ts +100 -3
  233. package/dist/plugins/remote-shell.js +166 -2
  234. package/dist/plugins/retention-store-sql.d.ts +23 -8
  235. package/dist/plugins/retention-store-sql.js +13 -1
  236. package/dist/plugins/run-store-sql.d.ts +13 -3
  237. package/dist/plugins/run-store-sql.js +5 -0
  238. package/dist/plugins/schema-consistency.d.ts +15 -5
  239. package/dist/plugins/schema-consistency.js +14 -8
  240. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  241. package/dist/plugins/schema-drift-sentence.js +38 -0
  242. package/dist/plugins/session-fork.d.ts +63 -12
  243. package/dist/plugins/session-fork.js +38 -21
  244. package/dist/plugins/session-ownership.d.ts +7 -0
  245. package/dist/plugins/session-ownership.js +5 -0
  246. package/dist/plugins/session-row-sql.d.ts +22 -0
  247. package/dist/plugins/session-row-sql.js +81 -0
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  250. package/dist/plugins/session-workspace-cell.js +38 -0
  251. package/dist/plugins/store-backend.d.ts +1 -1
  252. package/dist/plugins/store-backend.js +26 -14
  253. package/dist/plugins/store-contracts.d.ts +25 -5
  254. package/dist/plugins/tidb-pool.d.ts +12 -7
  255. package/dist/plugins/tidb-pool.js +15 -3
  256. package/dist/plugins/tidb-session-store.d.ts +74 -28
  257. package/dist/plugins/tidb-session-store.js +132 -53
  258. package/dist/plugins/tool-result-store-sql.d.ts +16 -8
  259. package/dist/plugins/tool-result-store-sql.js +26 -17
  260. package/dist/plugins/worktree-isolation.d.ts +3 -2
  261. package/dist/plugins/worktree-isolation.js +1 -7
  262. package/dist/project-identity.d.ts +1 -14
  263. package/dist/project-identity.js +0 -4
  264. package/dist/project-memory.d.ts +9 -15
  265. package/dist/project-memory.js +5 -4
  266. package/dist/request-key-closure.js +1 -1
  267. package/dist/request-value-closure.d.ts +6 -7
  268. package/dist/request-value-closure.js +1 -3
  269. package/dist/rules-consent.js +6 -4
  270. package/dist/run-local.d.ts +6 -0
  271. package/dist/run-local.js +36 -26
  272. package/dist/runs.d.ts +9 -2
  273. package/dist/runs.js +9 -2
  274. package/dist/sandbox-git-plane.d.ts +2 -2
  275. package/dist/security.d.ts +38 -12
  276. package/dist/security.js +8 -2
  277. package/dist/server-secret-env.d.ts +15 -8
  278. package/dist/server-secret-env.js +4 -2
  279. package/dist/session-children-retire.d.ts +27 -9
  280. package/dist/session-children-retire.js +9 -5
  281. package/dist/session-incarnation.d.ts +19 -10
  282. package/dist/session-incarnation.js +5 -3
  283. package/dist/session-row.d.ts +114 -5
  284. package/dist/session-row.js +55 -5
  285. package/dist/session-titler.d.ts +2 -2
  286. package/dist/session-workspace.d.ts +130 -0
  287. package/dist/session-workspace.js +83 -0
  288. package/dist/spec-fields.d.ts +1 -1
  289. package/dist/standing-answer-grant.d.ts +2 -13
  290. package/dist/standing-answer-grant.js +0 -7
  291. package/dist/task-a2a.d.ts +44 -14
  292. package/dist/task-a2a.js +46 -20
  293. package/dist/task-cwd.d.ts +6 -1
  294. package/dist/task-mcp.d.ts +33 -20
  295. package/dist/task-mcp.js +46 -22
  296. package/dist/tool-approval.d.ts +38 -8
  297. package/dist/tool-approval.js +57 -13
  298. package/dist/trace/core-keyset-guard.d.ts +33 -18
  299. package/dist/trace/engine-notice-wire.d.ts +1 -1
  300. package/dist/trace/engine-notice-wire.js +2 -0
  301. package/dist/trace/exit-seat.d.ts +20 -1
  302. package/dist/trace/exit-seat.js +3 -0
  303. package/dist/trace/extension-call-face.d.ts +33 -0
  304. package/dist/trace/extension-call-face.js +39 -0
  305. package/dist/trace/project.d.ts +26 -7
  306. package/dist/trace/project.js +30 -3
  307. package/dist/trace/redact.js +42 -13
  308. package/dist/trace/self-minted-wire.d.ts +240 -12
  309. package/dist/trace/self-minted-wire.js +100 -4
  310. package/dist/trace/wire-projection-faces.d.ts +6 -6
  311. package/dist/trace/wire-projection-faces.js +6 -5
  312. package/dist/url-refusal.d.ts +1 -1
  313. package/dist/user-authored-text.d.ts +2 -2
  314. package/dist/user-authored-text.js +16 -1
  315. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  316. package/dist/web-admin-client/apply-effective.js +20 -10
  317. package/dist/web-admin-client/center-request.d.ts +3 -2
  318. package/dist/web-admin-client/center-request.js +2 -3
  319. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  320. package/dist/web-admin-client/skills-mcp.js +32 -18
  321. package/dist/web-admin-client/types.d.ts +11 -0
  322. package/package.json +3 -3
  323. package/wire-changes/server@7.138.0.json +446 -0
  324. package/wire-changes/server@7.139.0.json +348 -0
  325. package/dist/http/device-placement.d.ts +0 -175
  326. package/dist/http/device-placement.js +0 -145
  327. package/dist/http/mcp-remote-text.d.ts +0 -5
  328. package/dist/http/mcp-remote-text.js +0 -7
@@ -4,10 +4,8 @@ import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, t
4
4
  import { decideParkedAgent, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, heldWorkflowParkDecisions, planWorkflowParkedDecide } from "../parked-decide.js";
5
5
  import { parkDecisionFingerprint } from "../orchestration/workflow-park-decision.js";
6
6
  import { headReadOf } from "../session-incarnation.js";
7
+ import { createSessionWorkspaceRecorder, legSessionWitnessOf, sessionWorkspaceStoreOf } from "../session-workspace.js";
7
8
  const DELIVERY_NOT_RECORDED = "workflow park decide: this delivery no longer holds the host session (its claim was released) — the decision was not recorded and this delivery was not started";
8
- const headUnavailable = async (sessionId) => {
9
- throw new Error(`parked decide cannot verify which occupant session ${sessionId} belongs to: this deployment's session store has no head read (headOf; every built-in store has one) — refusing rather than resuming unverified`);
10
- };
11
9
  import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
12
10
  import { graftParkToolCallId, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
13
11
  import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
@@ -38,7 +36,10 @@ import { errorText } from "../observability/err-text.js";
38
36
  import { recordFailOpen } from "../observability/fail-open.js";
39
37
  import { verifyRoundsFromBody } from "./verify-rounds.js";
40
38
  import { planReviewDecisionExitsPlan, storedBodyPermissionMode } from "../task-settings.js";
41
- import { acceptedResumingReceipt, acceptedTaskIdOf, buildHostLegOutcome } from "./route-ctx.js";
39
+ import { acceptedResumingReceipt, acceptedTaskIdOf } from "./route-ctx.js";
40
+ import { heldFor } from "../peer-seat.js";
41
+ import { NO_SETTLEMENT_EVIDENCE, buildSettlementEvidence, decisionCommittedOf } from "../gate-settlement.js";
42
+ import { durableRowIdentity } from "../checkpoint-identity.js";
42
43
  import { mintAndDriveDurableRun } from "./durable-run-leg.js";
43
44
  import { createSessionsLocal } from "./routes/sessions.js";
44
45
  import { createSessionSyncLocal } from "./routes/session-sync.js";
@@ -46,7 +47,7 @@ import { createImagesLocal } from "./routes/images.js";
46
47
  import { createCapabilitiesLocal } from "./routes/capabilities.js";
47
48
  import { gatedPrincipal, explicitOperatorOk, runIdentityOf } from "./principal-gate.js";
48
49
  import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
49
- import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
50
+ import { commitExecutionContext, isRootSessionGoneError, resolveExecutionContext, rootSessionAbsentAnswer, stampSpecRoot } from "./execution-context.js";
50
51
  import { commitSessionInheritEnv } from "./session-inherit-env.js";
51
52
  import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind, isEngineQuestionRow } from "../approval-content-kind.js";
52
53
  import { deploymentListAsks } from "../approval.js";
@@ -75,6 +76,8 @@ const WORKER_SWAP_REDEEMABLE = {
75
76
  ledger_event_unknown: true,
76
77
  real_approval_damaged: false,
77
78
  real_approval_forged: false,
79
+ tool_identity_missing: true,
80
+ bound_input_hash_missing: false,
78
81
  requirements_damaged: false,
79
82
  read_face_damaged: false,
80
83
  read_face_forged: false,
@@ -96,6 +99,7 @@ const WORKER_SWAP_REDEEMABLE = {
96
99
  unknown_model_ref: true,
97
100
  not_a_model_ref: true,
98
101
  unknown_role_word: true,
102
+ no_model_for_role: true,
99
103
  };
100
104
  function checkpointRowRedeemableElsewhere(reason) {
101
105
  return reason !== undefined && WORKER_SWAP_REDEEMABLE[reason] === true;
@@ -125,9 +129,9 @@ const CHECKPOINT_ERROR_DISPOSITION = {
125
129
  "checkpoint.gate_mismatch": "terminal",
126
130
  "checkpoint.reopen_failed": "terminal",
127
131
  "checkpoint.cwd_conflicts_restore": "terminal",
132
+ "steering.duplicate_input_id": "retriable",
128
133
  "checkpoint.already_exists": "terminal",
129
134
  "checkpoint.execution_outcome_conflict": "terminal",
130
- "steering.duplicate_input_id": "terminal",
131
135
  "steering.invalid_content": "terminal",
132
136
  "steering.queue_full": "terminal",
133
137
  };
@@ -159,6 +163,10 @@ const CHECKPOINT_ERROR_FIELD_ROLE = {
159
163
  hostDecision: "outcome_cell",
160
164
  approver: "outcome_cell",
161
165
  feedback: "outcome_cell",
166
+ outcome: "outcome_cell",
167
+ updatedInput: "outcome_cell",
168
+ decision: "outcome_cell",
169
+ reason: "outcome_cell",
162
170
  taskConfig: "config",
163
171
  deps: "config",
164
172
  inheritedGate: "config",
@@ -365,9 +373,7 @@ export function createResumeLegs(ctx) {
365
373
  }
366
374
  : undefined;
367
375
  const noteLateSettleIfAccepted = (out) => {
368
- if (lateSettleSink === undefined || lateSettleAnchor === undefined || out.status !== 200)
369
- return;
370
- if (out.body?.errorCode === "cancelled" && out.decisionCommitted !== true)
376
+ if (lateSettleSink === undefined || lateSettleAnchor === undefined || !decisionCommittedOf(out))
371
377
  return;
372
378
  try {
373
379
  lateSettleSink.recordLateSettle({ ...lateSettleAnchor, outcome: decision === "approve" ? "allowed" : "denied", decidedBy: hostDecision.decidedBy, ...(hostDecision.approver !== undefined ? { approver: hostDecision.approver } : {}), settledAtMs: Date.now() });
@@ -383,6 +389,7 @@ export function createResumeLegs(ctx) {
383
389
  if (parkedForkLeaseDenial)
384
390
  return parkedForkLeaseDenial;
385
391
  const ownerLeaseAdmittedHere = req !== undefined;
392
+ const captureSelectedGate = () => durableRowIdentity("decision-receipt", cp.checkpointId, token);
386
393
  if (req !== undefined) {
387
394
  const parkedAgentStore = deps.backgroundAgentStore;
388
395
  const parkedSurface = deps.parkedReviveSurface;
@@ -407,12 +414,32 @@ export function createResumeLegs(ctx) {
407
414
  checkpointStore: cs,
408
415
  registry: defaultTaskRegistry,
409
416
  reviveSurface: parkedSurface,
410
- headOf: headReadOf(deps.sessionStorage) ?? headUnavailable,
411
417
  ...(deps.parkedKnownAgentTypes ? { knownAgentTypes: deps.parkedKnownAgentTypes } : {}),
412
418
  ...(deps.parkedReviveInheritedGate ? { rebuildInheritedGate: deps.parkedReviveInheritedGate } : {}),
413
419
  ...(deps.toolApproval ? { approverSeatFor: windowZeroSeatOf(deps.toolApproval) } : {}),
420
+ execution: {
421
+ resolve: async (root) => {
422
+ let ctx;
423
+ try {
424
+ ctx = await resolveExecutionContext(deps, { kind: "continuation", root, principal: decodeCheckpointScope(cp.scope) });
425
+ }
426
+ catch (e) {
427
+ if (isRootSessionGoneError(e))
428
+ return { ok: false, gone: true };
429
+ if (e instanceof HttpError)
430
+ return { ok: false, gone: false, answer: resumeRefusalWithCard(resumeBlockedByPolicyAnswer(httpErrorRefusal(e)), true) };
431
+ throw e;
432
+ }
433
+ const occupant = ctx.occupant;
434
+ if (occupant === undefined) {
435
+ throw new Error(`parked decide: root session ${root} resolved without an occupant reading — the parked revive leg is wired to a session store without an execution placement seat (sessionStorage.executionPlacement), which createHttpServer should have refused at startup; refusing rather than resuming with nobody judging the root session's occupant after the claim`);
436
+ }
437
+ return { ok: true, ctx: { ...ctx, occupant } };
438
+ },
439
+ commitDeps: deps,
440
+ },
414
441
  ...(deps.logger ? { warn: parkWarn } : {}),
415
- }, parkedReq, { handle: origin.handle, row: origin.row }));
442
+ }, { ...parkedReq, selectedGate: captureSelectedGate() }, { handle: origin.handle, row: origin.row }));
416
443
  void ctxPromise.catch(() => { });
417
444
  noteLateSettleIfAccepted(parked);
418
445
  return parked;
@@ -440,7 +467,7 @@ export function createResumeLegs(ctx) {
440
467
  if (held === null || held.fingerprint !== fingerprint)
441
468
  return undefined;
442
469
  const run = await runStore.getRun(held.deliveredTaskId);
443
- return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId) : undefined;
470
+ return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId, NO_SETTLEMENT_EVIDENCE) : undefined;
444
471
  };
445
472
  const replay = await liveReceiptOf(await decisions.get(token));
446
473
  if (replay !== undefined)
@@ -486,44 +513,52 @@ export function createResumeLegs(ctx) {
486
513
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
487
514
  if (!resolved.ok)
488
515
  return resolved.answer;
489
- const { spec, engineNotices } = resolved;
490
- const pendingTool = cp.pendingAction.toolName;
491
- if (decision === "approve" && pendingTool === ASK_USER_QUESTION_TOOL_NAME && !answer) {
492
- return { status: 400, body: refusalBody({ errorCode: "request.field_conflict", message: "pending action is AskUserQuestion — approve must carry body.answer ({ answers: [{ header, selected: string[], note? }] })", facts: { ...fieldConflictFacts("ask_answer", ["decision", "answer"]) } }) };
516
+ try {
517
+ const { spec, engineNotices } = resolved;
518
+ const pendingTool = cp.pendingAction.toolName;
519
+ if (decision === "approve" && pendingTool === ASK_USER_QUESTION_TOOL_NAME && !answer) {
520
+ return { status: 400, body: refusalBody({ errorCode: "request.field_conflict", message: "pending action is AskUserQuestion — approve must carry body.answer ({ answers: [{ header, selected: string[], note? }] })", facts: { ...fieldConflictFacts("ask_answer", ["decision", "answer"]) } }) };
521
+ }
522
+ if (answer && (decision !== "approve" || pendingTool !== ASK_USER_QUESTION_TOOL_NAME)) {
523
+ return { status: 400, body: refusalBody({ errorCode: "request.field_conflict", message: `body.answer is only valid when approving a pending AskUserQuestion (decision: "${decision}", pending: "${pendingTool ?? "unknown"}")`, facts: { ...fieldConflictFacts("ask_answer", ["answer", "decision"]) } }) };
524
+ }
525
+ const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
526
+ const pa = cp.pendingAction;
527
+ const persistedCallId = pa.kind === "tool_approval" ? pa.toolCallId : "";
528
+ const persistedHash = pa.kind === "tool_approval" ? (pa.boundInputHash ?? "") : "";
529
+ const outcome = {
530
+ gate: "policy_ask",
531
+ boundCallId: binding?.boundCallId ?? persistedCallId,
532
+ boundInputHash: binding?.boundInputHash ?? persistedHash,
533
+ decision: decision === "approve" ? "allow" : "deny",
534
+ ...(decision === "approve" && !isEngineQuestionRow(pa) && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
535
+ ...(reason ? { reason } : {}),
536
+ ...(binding?.feedback !== undefined ? { feedback: binding.feedback } : {}),
537
+ ...(answer !== undefined ? { answer } : {}),
538
+ hostDecision,
539
+ };
540
+ const verifyRounds = verifyRoundsFromBody(ctx.body);
541
+ const driven = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, commitment: { kind: "gate_resolved", ...captureSelectedGate() }, verifyRounds, resumeCtx: { sessionId, ctx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(boundGrant ? { onResumeCommitted: boundGrant } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}), ...(ownerLeaseAdmittedHere ? { ownerLeaseAdmitted: true } : {}) });
542
+ noteLateSettleIfAccepted(driven);
543
+ return driven;
493
544
  }
494
- if (answer && (decision !== "approve" || pendingTool !== ASK_USER_QUESTION_TOOL_NAME)) {
495
- return { status: 400, body: refusalBody({ errorCode: "request.field_conflict", message: `body.answer is only valid when approving a pending AskUserQuestion (decision: "${decision}", pending: "${pendingTool ?? "unknown"}")`, facts: { ...fieldConflictFacts("ask_answer", ["answer", "decision"]) } }) };
545
+ finally {
546
+ resolved.peerLease.release();
496
547
  }
497
- const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
498
- const pa = cp.pendingAction;
499
- const persistedCallId = pa.kind === "tool_approval" ? pa.toolCallId : "";
500
- const persistedHash = pa.kind === "tool_approval" ? (pa.boundInputHash ?? "") : "";
501
- const outcome = {
502
- gate: "policy_ask",
503
- boundCallId: binding?.boundCallId ?? persistedCallId,
504
- boundInputHash: binding?.boundInputHash ?? persistedHash,
505
- decision: decision === "approve" ? "allow" : "deny",
506
- ...(decision === "approve" && !isEngineQuestionRow(pa) && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
507
- ...(reason ? { reason } : {}),
508
- ...(binding?.feedback !== undefined ? { feedback: binding.feedback } : {}),
509
- ...(answer !== undefined ? { answer } : {}),
510
- hostDecision,
511
- };
512
- const verifyRounds = verifyRoundsFromBody(ctx.body);
513
- const driven = await driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx: { sessionId, ctx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(boundGrant ? { onResumeCommitted: boundGrant } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}), ...(ownerLeaseAdmittedHere ? { ownerLeaseAdmitted: true } : {}) });
514
- noteLateSettleIfAccepted(driven);
515
- return driven;
516
548
  }
517
- const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles", "userAttachments", "images"];
549
+ const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "userAttachments", "images"];
518
550
  const ONE_SHOT_SUBMIT_STRIP = Object.fromEntries(ONE_SHOT_SUBMIT_KEYS.map((k) => [k, undefined]));
519
- const resolveResumeSpec = async (ctx, req, auth) => {
551
+ const resolveResumeSpec = async (ctx, req, auth, lease) => {
520
552
  const engineNotices = [];
521
- const spec = await deps.resolveSpec({ ...ctx.body, ...ONE_SHOT_SUBMIT_STRIP }, req, auth, {
553
+ const execution = await resolveExecutionContext(deps, { kind: "continuation", root: auth.sessionId, principal: auth.principal });
554
+ const spec = stampSpecRoot(await deps.resolveSpec({ ...ctx.body, ...ONE_SHOT_SUBMIT_STRIP }, req, auth, {
522
555
  leg: "resume",
523
556
  onNotice: (n) => engineNotices.push(n),
524
557
  ...(ctx.unattendedLeg === true ? { unattendedLeg: true } : {}),
525
- });
526
- return { spec, engineNotices };
558
+ lease,
559
+ execution,
560
+ }), execution);
561
+ return { spec, engineNotices, execution };
527
562
  };
528
563
  const refuseStoredBodyReplay = (sessionId, refusal) => {
529
564
  deps.logger?.warn?.("resume_refused_stored_body", { sessionId, ...storedBodyReplayLogFields(refusal) });
@@ -533,11 +568,13 @@ export function createResumeLegs(ctx) {
533
568
  const replay = storedBodyReplayRefusal(ctx, deps.config);
534
569
  if (replay !== undefined)
535
570
  return { ok: false, answer: refuseStoredBodyReplay(auth.sessionId, replay) };
571
+ const lease = deps.peerDesk.lease("resume");
536
572
  try {
537
- const { spec, engineNotices } = await resolveResumeSpec(ctx, req, auth);
538
- return { ok: true, spec, engineNotices };
573
+ const { spec, engineNotices, execution } = await resolveResumeSpec(ctx, req, auth, lease);
574
+ return { ok: true, spec, engineNotices, execution, peerLease: lease };
539
575
  }
540
576
  catch (e) {
577
+ lease.release();
541
578
  if (e instanceof HttpError) {
542
579
  return { ok: false, answer: resumeRefusalWithCard(resumeBlockedByPolicyAnswer(httpErrorRefusal(e)), true) };
543
580
  }
@@ -546,11 +583,11 @@ export function createResumeLegs(ctx) {
546
583
  };
547
584
  async function driveResumeIntoRunLog(args) {
548
585
  if (args.acceptEarly !== true)
549
- return await driveResumeLeg(args);
586
+ return await heldFor(args.peerLease, () => driveResumeLeg(args));
550
587
  let settleAccepted;
551
588
  let wasAccepted = false;
552
589
  const accepted = new Promise((resolve) => { settleAccepted = resolve; });
553
- const leg = driveResumeLeg(args, (receipt) => { wasAccepted = true; settleAccepted?.(receipt); });
590
+ const leg = heldFor(args.peerLease, () => driveResumeLeg(args, (receipt) => { wasAccepted = true; settleAccepted?.(receipt); }));
554
591
  void leg.then((out) => {
555
592
  if (wasAccepted && out.status >= 400) {
556
593
  deps.logger?.warn?.("resume_leg_failed_after_accept", { sessionId: args.sessionId, status: out.status, body: out.body });
@@ -566,7 +603,7 @@ export function createResumeLegs(ctx) {
566
603
  }
567
604
  async function driveResumeLeg(args, onAccepted) {
568
605
  const resumeLegStartedAt = Date.now();
569
- const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx } = args;
606
+ const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx, execution } = args;
570
607
  const reparkStatus = outcome.gate === "plan_review" ? "needs_review" : "suspended";
571
608
  const commitResumeCtx = () => Promise.resolve().then(() => deps.checkpointStore.putCtx(resumeCtx.sessionId, sealCheckpointCtx({ ...resumeCtx.ctx, writerVersion: serviceVersion() }, deps.checkpointCtxSealing, resumeCtx.sessionId))).catch((e) => {
572
609
  deps.metrics?.inc("resume_ctx_not_persisted_total");
@@ -610,12 +647,14 @@ export function createResumeLegs(ctx) {
610
647
  if (created.existing) {
611
648
  throw new Error(`wake fresh run: createRun observed an existing row for the freshly minted taskId ${freshId} (no idempotency key was sent) — refusing to treat it as a busy session`);
612
649
  }
650
+ if (created.sessionAbsent)
651
+ return rootSessionAbsentAnswer();
613
652
  if (!created.ok) {
614
653
  return { status: 409, body: await buildActiveRunConflict({ runStore: deps.runStore, checkpointStore: deps.checkpointStore, governance: deps.config, runStaleSec: deps.config.runStaleSec, turnActivity: readTurnActivityMs }, sessionId, created.activeTaskId) };
615
654
  }
616
- const placementReject = await commitPlacement(deps, deps.runStore, freshId, undefined);
655
+ const placementReject = await commitExecutionContext(deps, { kind: "run", runStore: deps.runStore, taskId: freshId }, execution);
617
656
  if (placementReject)
618
- return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
657
+ return placementReject;
619
658
  taskId = freshId;
620
659
  freshRunClaimed = true;
621
660
  }
@@ -626,11 +665,12 @@ export function createResumeLegs(ctx) {
626
665
  if (deps.toolApproval && taskId) {
627
666
  const toolApproval = deps.toolApproval;
628
667
  resumeTaskConfig.onAsk = approvalLeg.unattended
629
- ? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId }, approvalLeg.unattended)
668
+ ? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId, executionLane: execution.lane }, approvalLeg.unattended)
630
669
  : toolApproval.boundAsk({
631
670
  owner: principal ?? null,
632
671
  taskId,
633
672
  sessionId,
673
+ executionLane: execution.lane,
634
674
  ...(approvalLeg.active ? { legKey, ...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}) } : {}),
635
675
  });
636
676
  }
@@ -696,6 +736,11 @@ export function createResumeLegs(ctx) {
696
736
  claimedRow = freshRunClaimed ? true : await deps.runStore.markResuming(taskId, deps.instanceId ?? "default");
697
737
  if (!claimedRow)
698
738
  return notResumableAnswer();
739
+ if (!freshRunClaimed) {
740
+ const placementReject = await commitExecutionContext(deps, { kind: "run", runStore: deps.runStore, taskId }, execution);
741
+ if (placementReject)
742
+ return placementReject;
743
+ }
699
744
  markChildrenStoppedByUserOnAbort(cancelCtrl.signal, taskId, principal);
700
745
  fleetPub?.onStart();
701
746
  const rs = deps.runStore;
@@ -889,6 +934,7 @@ export function createResumeLegs(ctx) {
889
934
  const driveResume = async () => {
890
935
  resumeLegLive = true;
891
936
  const tailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
937
+ const resumeWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), legSessionWitnessOf(resumeTaskConfig));
892
938
  const stream = await legRunner.resumeStream(token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, {
893
939
  ...launchInternals,
894
940
  onForwardEvent: (e) => {
@@ -951,12 +997,14 @@ export function createResumeLegs(ctx) {
951
997
  if (taskId)
952
998
  steerableRuns.set(taskId, stream);
953
999
  if (!taskId || !rs) {
1000
+ for await (const ev of stream)
1001
+ await resumeWorkspace.observe(ev);
954
1002
  const rNoRow = await stream.result();
955
1003
  if (await reopenedAfterLeg(rNoRow, cancelCtrl.cause() !== undefined))
956
1004
  reopenConfirmedPark = true;
957
1005
  return sealResultForExit(rNoRow);
958
1006
  }
959
- onAccepted?.(acceptedResumingReceipt(taskId, sessionId));
1007
+ onAccepted?.(acceptedResumingReceipt(taskId, sessionId, buildSettlementEvidence({ at: "engine_accepted", commitment: args.commitment })));
960
1008
  let causeAtDone;
961
1009
  for await (const ev of stream) {
962
1010
  if (ev.type === "done")
@@ -964,6 +1012,7 @@ export function createResumeLegs(ctx) {
964
1012
  recordTurnActivity(taskId);
965
1013
  fleetPub?.onEvent(ev);
966
1014
  await deps.userInputLedger?.observeLegEvent(stream, ev);
1015
+ await resumeWorkspace.observe(ev);
967
1016
  await appendPromptManifest(append, deps.promptManifests, taskId);
968
1017
  switch (ev.type) {
969
1018
  case "text_delta":
@@ -1119,6 +1168,7 @@ export function createResumeLegs(ctx) {
1119
1168
  owner: principal ?? null,
1120
1169
  abortSignal: cancelCtrl.signal,
1121
1170
  sessionId,
1171
+ executionLane: execution.lane,
1122
1172
  emitCard: createApprovalCardEmitter({ appendDurable: (f) => approvalEmit(f) }),
1123
1173
  emitRevoke: (f) => approvalEmit(f),
1124
1174
  legKey,
@@ -1221,13 +1271,13 @@ export function createResumeLegs(ctx) {
1221
1271
  settleFleet(checkpointReopened ? reparkStatus : resumePlane.status, fleetRunResiduals(sealed));
1222
1272
  return {
1223
1273
  status: 200,
1224
- decisionCommitted: true,
1225
1274
  body: {
1226
1275
  taskId, sessionId, status: resumePlane.status,
1227
1276
  bindingEnforced: true,
1228
1277
  ...(resumePlane.errorCode ? { errorCode: resumePlane.errorCode } : {}),
1229
1278
  ...(resumePlane.errorMessage ? { errorMessage: redactErrorMessage(resumePlane.errorMessage) ?? "" } : {}),
1230
1279
  ...(checkpointReopened ? { retriable: true } : {}),
1280
+ ...buildSettlementEvidence({ at: "engine_terminal", commitment: args.commitment }),
1231
1281
  },
1232
1282
  };
1233
1283
  }
@@ -1302,10 +1352,16 @@ export function createResumeLegs(ctx) {
1302
1352
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
1303
1353
  if (!resolved.ok)
1304
1354
  return resolved.answer;
1305
- const { spec, engineNotices } = resolved;
1306
- const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1307
- const verifyRounds = verifyRoundsFromBody(ctx.body);
1308
- return driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome: { gate: "resource_limit", decision: "continue" }, verifyRounds, resumeCtx: { sessionId, ctx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
1355
+ try {
1356
+ const { spec, engineNotices } = resolved;
1357
+ const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1358
+ const verifyRounds = verifyRoundsFromBody(ctx.body);
1359
+ const selectedGate = durableRowIdentity("decision-receipt", cp.checkpointId, token);
1360
+ return await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome: { gate: "resource_limit", decision: "continue" }, commitment: { kind: "gate_resolved", ...selectedGate }, verifyRounds, resumeCtx: { sessionId, ctx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
1361
+ }
1362
+ finally {
1363
+ resolved.peerLease.release();
1364
+ }
1309
1365
  }
1310
1366
  async function startFreshHostTurn(sessionId, objective, principal, req, workflowDecision, runId) {
1311
1367
  const cs = deps.checkpointStore;
@@ -1338,23 +1394,28 @@ export function createResumeLegs(ctx) {
1338
1394
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
1339
1395
  if (!resolved.ok)
1340
1396
  return resolved.answer;
1341
- const { spec, engineNotices } = resolved;
1342
- const hostSpec = { ...spec, objective, sessionId };
1343
- const out = await mintAndDriveDurableRun(routeCtxBase, deps.runStore, {
1344
- prepared: { spec: hostSpec, runner: runnerFor(hostSpec), auth, body: ctx.body, acceptance: createRunAcceptance({}, { objective }), ...(engineNotices.length > 0 ? { engineNotices } : {}) },
1345
- fleetScope: resumeFleetScope(req, auth),
1346
- elicitOwner: principal ?? null,
1347
- source: "workflow_parked_decide",
1348
- identity: runIdentityOf(req, deps.config, principal ?? null),
1349
- workflowDecision,
1350
- });
1351
- if (out.existing) {
1352
- throw new Error(`decide host turn: createRun observed an existing row ${out.existing.taskId} for a freshly minted taskId (no idempotency key was sent)`);
1397
+ try {
1398
+ const { spec, engineNotices } = resolved;
1399
+ const hostSpec = { ...spec, objective };
1400
+ const out = await mintAndDriveDurableRun(routeCtxBase, deps.runStore, {
1401
+ prepared: { spec: hostSpec, runner: runnerFor(hostSpec), auth, body: ctx.body, acceptance: createRunAcceptance({}, { objective }), peerLease: resolved.peerLease, execution: resolved.execution, ...(engineNotices.length > 0 ? { engineNotices } : {}) },
1402
+ fleetScope: resumeFleetScope(req, auth),
1403
+ elicitOwner: principal ?? null,
1404
+ source: "workflow_parked_decide",
1405
+ identity: runIdentityOf(req, deps.config, principal ?? null),
1406
+ workflowDecision,
1407
+ });
1408
+ if (out.existing) {
1409
+ throw new Error(`decide host turn: createRun observed an existing row ${out.existing.taskId} for a freshly minted taskId (no idempotency key was sent)`);
1410
+ }
1411
+ if (out.status !== 202)
1412
+ return { status: out.status, body: out.body };
1413
+ const minted = out.body;
1414
+ return acceptedResumingReceipt(minted.taskId, minted.sessionId, NO_SETTLEMENT_EVIDENCE);
1415
+ }
1416
+ finally {
1417
+ resolved.peerLease.release();
1353
1418
  }
1354
- if (out.status !== 202)
1355
- return { status: out.status, body: out.body };
1356
- const minted = out.body;
1357
- return acceptedResumingReceipt(minted.taskId, minted.sessionId);
1358
1419
  }
1359
1420
  async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowDecision, ownerLeaseAdmittedFor) {
1360
1421
  const cs = deps.checkpointStore;
@@ -1418,26 +1479,34 @@ export function createResumeLegs(ctx) {
1418
1479
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
1419
1480
  if (!resolved.ok)
1420
1481
  return resolved.answer;
1421
- const { spec, engineNotices } = resolved;
1422
- const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1423
- return buildHostLegOutcome(await driveResumeIntoRunLog({
1424
- runner: runnerFor(spec),
1425
- token,
1426
- sessionId: cp.sessionId,
1427
- principal: auth.principal,
1428
- fleetScope: resumeFleetScope(req, auth),
1429
- taskConfig,
1430
- resumeObjective,
1431
- outcome: { gate: "wake", ...(message !== undefined ? { message: { text: message, trusted } } : {}) },
1432
- verifyRounds: undefined,
1433
- resumeCtx: { sessionId, ctx },
1434
- mintFreshRun: { source: "wake", identity: runIdentityOf(req, deps.config, auth.principal ?? null) },
1435
- ...(workflowDecision !== undefined ? { workflowDecision } : {}),
1436
- ...(engineNotices.length > 0 ? { engineNotices } : {}),
1437
- ...(acceptEarly === true ? { acceptEarly: true } : {}),
1438
- ...(req !== undefined ? { callerInitiated: true } : {}),
1439
- ...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
1440
- }));
1482
+ try {
1483
+ const { spec, engineNotices } = resolved;
1484
+ const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1485
+ return await driveResumeIntoRunLog({
1486
+ peerLease: resolved.peerLease,
1487
+ execution: resolved.execution,
1488
+ runner: runnerFor(spec),
1489
+ token,
1490
+ sessionId: cp.sessionId,
1491
+ principal: auth.principal,
1492
+ fleetScope: resumeFleetScope(req, auth),
1493
+ taskConfig,
1494
+ resumeObjective,
1495
+ outcome: { gate: "wake", ...(message !== undefined ? { message: { text: message, trusted } } : {}) },
1496
+ commitment: { kind: "delivered" },
1497
+ verifyRounds: undefined,
1498
+ resumeCtx: { sessionId, ctx },
1499
+ mintFreshRun: { source: "wake", identity: runIdentityOf(req, deps.config, auth.principal ?? null) },
1500
+ ...(workflowDecision !== undefined ? { workflowDecision } : {}),
1501
+ ...(engineNotices.length > 0 ? { engineNotices } : {}),
1502
+ ...(acceptEarly === true ? { acceptEarly: true } : {}),
1503
+ ...(req !== undefined ? { callerInitiated: true } : {}),
1504
+ ...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
1505
+ });
1506
+ }
1507
+ finally {
1508
+ resolved.peerLease.release();
1509
+ }
1441
1510
  }
1442
1511
  async function preparePlanReview(sessionId, log, decider, sighting) {
1443
1512
  const cs = deps.checkpointStore;
@@ -1530,18 +1599,24 @@ export function createResumeLegs(ctx) {
1530
1599
  const resolved = await resolveResumeSpecOrBlocked(rebuildCtx, req, auth);
1531
1600
  if (!resolved.ok)
1532
1601
  return resolved.answer;
1533
- const { spec, engineNotices } = resolved;
1534
- const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1535
- const verifyRounds = verifyRoundsFromBody(ctx.body);
1536
- const outcome = {
1537
- gate: "plan_review",
1538
- decision,
1539
- ...(editedPlan !== undefined ? { editedPlan } : {}),
1540
- ...(reason ? { reason } : {}),
1541
- hostDecision,
1542
- };
1543
- const out = await driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx: { sessionId, ctx: rebuildCtx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
1544
- return out;
1602
+ try {
1603
+ const { spec, engineNotices } = resolved;
1604
+ const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1605
+ const verifyRounds = verifyRoundsFromBody(ctx.body);
1606
+ const outcome = {
1607
+ gate: "plan_review",
1608
+ decision,
1609
+ ...(editedPlan !== undefined ? { editedPlan } : {}),
1610
+ ...(reason ? { reason } : {}),
1611
+ hostDecision,
1612
+ };
1613
+ const selectedGate = durableRowIdentity("decision-receipt", cp.checkpointId, token);
1614
+ const out = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, commitment: { kind: "gate_resolved", ...selectedGate }, verifyRounds, resumeCtx: { sessionId, ctx: rebuildCtx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
1615
+ return out;
1616
+ }
1617
+ finally {
1618
+ resolved.peerLease.release();
1619
+ }
1545
1620
  }
1546
1621
  const routeCtxBase = {
1547
1622
  deps,