@sema-agent/server 7.138.0 → 7.140.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 (332) hide show
  1. package/MIGRATION.md +457 -7
  2. package/README.md +7 -4
  3. package/README.zh-CN.md +6 -4
  4. package/USAGE.md +91 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/runner.js +1 -1
  8. package/dist/adoption/sql.js +1 -1
  9. package/dist/adoption/wire.d.ts +3 -3
  10. package/dist/approval-card.d.ts +26 -26
  11. package/dist/bench/s1/arms.d.ts +1 -1
  12. package/dist/bench/s1/arms.js +1 -3
  13. package/dist/bench/s1/live-deps.js +3 -2
  14. package/dist/bench/s1/row.d.ts +5 -2
  15. package/dist/bench/s1/row.js +2 -22
  16. package/dist/bench/s1/run-firm.js +2 -2
  17. package/dist/boot/adoption.js +1 -1
  18. package/dist/boot/device-lane.d.ts +8 -3
  19. package/dist/boot/device-lane.js +7 -2
  20. package/dist/boot/execution-env.d.ts +4 -2
  21. package/dist/boot/execution-env.js +28 -34
  22. package/dist/boot/execution-lane.d.ts +9 -1
  23. package/dist/boot/execution-lane.js +6 -1
  24. package/dist/boot/governance-seams.d.ts +3 -2
  25. package/dist/boot/governance-seams.js +9 -11
  26. package/dist/boot/memory-boundary.d.ts +14 -51
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +15 -11
  29. package/dist/boot/memory-consolidation.js +25 -14
  30. package/dist/boot/resolve-spec.d.ts +4 -6
  31. package/dist/boot/resolve-spec.js +10 -13
  32. package/dist/boot/retention-lane.d.ts +5 -5
  33. package/dist/boot/retention-lane.js +0 -6
  34. package/dist/boot/runner-deps.d.ts +6 -4
  35. package/dist/boot/runner-deps.js +3 -1
  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-03-store-backend.d.ts +2 -6
  40. package/dist/boot/stage-03-store-backend.js +2 -3
  41. package/dist/boot/stage-04-stores.d.ts +2 -6
  42. package/dist/boot/stage-04-stores.js +5 -5
  43. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  44. package/dist/boot/stage-05-execution-env.js +2 -2
  45. package/dist/boot/stage-06-runners.d.ts +2 -6
  46. package/dist/boot/stage-06-runners.js +2 -3
  47. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  48. package/dist/boot/stage-08-reapers.d.ts +2 -6
  49. package/dist/boot/stage-09-leader.d.ts +2 -6
  50. package/dist/boot/stage-09-leader.js +1 -2
  51. package/dist/boot/stage-10-http-server.d.ts +2 -6
  52. package/dist/boot/stage-10-http-server.js +7 -6
  53. package/dist/boot/stores.d.ts +3 -6
  54. package/dist/boot/stores.js +27 -40
  55. package/dist/boot/task-list-lane.d.ts +1 -1
  56. package/dist/boot/task-list-lane.js +1 -1
  57. package/dist/boot/web-admin.js +37 -11
  58. package/dist/capabilities/execution-lane.d.ts +2 -2
  59. package/dist/capabilities/mcp-probe.js +1 -1
  60. package/dist/capabilities/scenarios.d.ts +1 -1
  61. package/dist/capability-facts.d.ts +15 -7
  62. package/dist/capability-facts.js +8 -3
  63. package/dist/config-catalog.js +9 -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 +33 -37
  67. package/dist/config-provider.js +51 -30
  68. package/dist/config-types.d.ts +21 -7
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +46 -16
  71. package/dist/delegated-session-owner.d.ts +4 -3
  72. package/dist/delegated-session-owner.js +1 -1
  73. package/dist/deployment-shape.d.ts +10 -9
  74. package/dist/deployment-shape.js +4 -4
  75. package/dist/device-enrollment.d.ts +41 -2
  76. package/dist/device-enrollment.js +27 -12
  77. package/dist/device-store.d.ts +58 -85
  78. package/dist/device-store.js +19 -49
  79. package/dist/device-ws-hub.js +2 -1
  80. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  81. package/dist/exec-cut.d.ts +60 -0
  82. package/dist/exec-cut.js +36 -0
  83. package/dist/execution-lane-caps.d.ts +115 -105
  84. package/dist/execution-lane-caps.js +76 -82
  85. package/dist/execution-placement.d.ts +259 -0
  86. package/dist/execution-placement.js +199 -0
  87. package/dist/field-conflict.d.ts +3 -2
  88. package/dist/field-conflict.js +1 -0
  89. package/dist/hosted-posture.d.ts +2 -2
  90. package/dist/http/admission.js +8 -19
  91. package/dist/http/checkpoint-ctx.d.ts +21 -12
  92. package/dist/http/checkpoint-ctx.js +4 -2
  93. package/dist/http/claimed-run.d.ts +1 -1
  94. package/dist/http/durable-run-leg.js +9 -5
  95. package/dist/http/execution-context.d.ts +218 -0
  96. package/dist/http/execution-context.js +330 -0
  97. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  98. package/dist/http/memory-compliance-failure-reply.js +5 -0
  99. package/dist/http/memory-layer-reply.d.ts +30 -0
  100. package/dist/http/memory-layer-reply.js +24 -0
  101. package/dist/http/resume-legs.js +63 -36
  102. package/dist/http/retired-service-deps.d.ts +1 -1
  103. package/dist/http/retired-service-deps.js +1 -1
  104. package/dist/http/route-ctx.d.ts +14 -8
  105. package/dist/http/routes/adoption.js +1 -1
  106. package/dist/http/routes/background.d.ts +29 -1
  107. package/dist/http/routes/background.js +29 -2
  108. package/dist/http/routes/devices.d.ts +13 -19
  109. package/dist/http/routes/devices.js +21 -26
  110. package/dist/http/routes/memory-bundle.js +3 -0
  111. package/dist/http/routes/memory-compliance.js +8 -1
  112. package/dist/http/routes/memory-consolidation.js +12 -1
  113. package/dist/http/routes/memory-origin.js +14 -2
  114. package/dist/http/routes/memory-policy.js +5 -0
  115. package/dist/http/routes/session-sync.js +110 -15
  116. package/dist/http/routes/sessions.d.ts +6 -0
  117. package/dist/http/routes/sessions.js +143 -22
  118. package/dist/http/routes/side-query.d.ts +12 -4
  119. package/dist/http/routes/side-query.js +4 -5
  120. package/dist/http/routes/tasks.js +41 -33
  121. package/dist/http/server.d.ts +40 -44
  122. package/dist/http/server.js +13 -2
  123. package/dist/http/session-inherit-env.d.ts +3 -4
  124. package/dist/http/session-inherit-env.js +1 -1
  125. package/dist/http/wire-types.d.ts +8 -12
  126. package/dist/identity-source.d.ts +1 -1
  127. package/dist/identity-source.js +1 -1
  128. package/dist/index.d.ts +1 -0
  129. package/dist/lane-home-probe.d.ts +68 -42
  130. package/dist/lane-home-probe.js +53 -30
  131. package/dist/leader/wire.js +5 -4
  132. package/dist/memory-bundle-engine.d.ts +16 -9
  133. package/dist/memory-bundle-engine.js +29 -6
  134. package/dist/memory-operator-faces.d.ts +85 -30
  135. package/dist/memory-operator-faces.js +136 -30
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +29 -5
  141. package/dist/memory-scope.js +16 -4
  142. package/dist/memory-sync-client.d.ts +70 -18
  143. package/dist/memory-sync-client.js +215 -117
  144. package/dist/memory-sync.d.ts +1 -1
  145. package/dist/memory-sync.js +3 -2
  146. package/dist/observability/cost-taxonomy.d.ts +2 -2
  147. package/dist/observability/fail-open.d.ts +12 -0
  148. package/dist/observability/fail-open.js +12 -0
  149. package/dist/observability/run-terminal.js +0 -1
  150. package/dist/orchestration/workflow-notify-journal.js +4 -4
  151. package/dist/org-memory-admission.d.ts +4 -3
  152. package/dist/org-memory-admission.js +6 -6
  153. package/dist/parked-decide.d.ts +34 -9
  154. package/dist/parked-decide.js +12 -19
  155. package/dist/parked-revive-surface.d.ts +12 -15
  156. package/dist/parked-revive-surface.js +3 -6
  157. package/dist/peer-seat.d.ts +13 -7
  158. package/dist/peer-seat.js +37 -3
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  161. package/dist/plugins/approval-ask-store-file.js +11 -0
  162. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  163. package/dist/plugins/approval-ask-store-memory.js +12 -0
  164. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  165. package/dist/plugins/approval-ask-store-sql.js +44 -26
  166. package/dist/plugins/approval-nonce-store.js +4 -4
  167. package/dist/plugins/blob-backend.d.ts +4 -4
  168. package/dist/plugins/blob-backend.js +7 -7
  169. package/dist/plugins/breaker-state-sql.js +2 -2
  170. package/dist/plugins/caching-session-store.d.ts +6 -2
  171. package/dist/plugins/caching-session-store.js +9 -3
  172. package/dist/plugins/checkpoint-index-columns.js +2 -2
  173. package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
  174. package/dist/plugins/checkpoint-store-sql.js +21 -23
  175. package/dist/plugins/device-store-sql.d.ts +25 -51
  176. package/dist/plugins/device-store-sql.js +68 -122
  177. package/dist/plugins/ensure-index.d.ts +1 -2
  178. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  179. package/dist/plugins/execution-placement-sql.js +223 -0
  180. package/dist/plugins/file-history-store-sql.d.ts +9 -8
  181. package/dist/plugins/file-history-store-sql.js +9 -9
  182. package/dist/plugins/file-run-store.d.ts +6 -1
  183. package/dist/plugins/file-run-store.js +4 -0
  184. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  185. package/dist/plugins/fork-routing-session-store.js +16 -0
  186. package/dist/plugins/image-bake-store-sql.js +9 -9
  187. package/dist/plugins/image-index-sql.js +11 -11
  188. package/dist/plugins/leader-run-store-sql.js +4 -4
  189. package/dist/plugins/local-session-store.d.ts +149 -50
  190. package/dist/plugins/local-session-store.js +382 -107
  191. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  192. package/dist/plugins/mailbox-store-sql.js +4 -4
  193. package/dist/plugins/memory-engine-tidb.js +5 -5
  194. package/dist/plugins/memory-key-guards.d.ts +4 -0
  195. package/dist/plugins/memory-key-guards.js +1 -0
  196. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  197. package/dist/plugins/memory-run-store.d.ts +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +117 -8
  200. package/dist/plugins/one-time-migrations.js +408 -38
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  203. package/dist/plugins/outcome-ledger-sql.js +2 -2
  204. package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
  205. package/dist/plugins/permission-rule-store-sql.js +29 -32
  206. package/dist/plugins/pg-pool.d.ts +7 -3
  207. package/dist/plugins/pg-session-storage.d.ts +31 -20
  208. package/dist/plugins/pg-session-storage.js +120 -52
  209. package/dist/plugins/remote-env-adb.d.ts +18 -19
  210. package/dist/plugins/remote-env-adb.js +91 -66
  211. package/dist/plugins/remote-env-device.d.ts +23 -31
  212. package/dist/plugins/remote-env-device.js +19 -17
  213. package/dist/plugins/remote-env-e2b.d.ts +27 -21
  214. package/dist/plugins/remote-env-e2b.js +91 -45
  215. package/dist/plugins/remote-env-file-error.d.ts +3 -1
  216. package/dist/plugins/remote-env-file-error.js +9 -3
  217. package/dist/plugins/remote-env-host.d.ts +31 -34
  218. package/dist/plugins/remote-env-host.js +91 -72
  219. package/dist/plugins/remote-env-k8s.d.ts +13 -29
  220. package/dist/plugins/remote-env-k8s.js +63 -55
  221. package/dist/plugins/remote-env-local-docker.d.ts +24 -12
  222. package/dist/plugins/remote-env-local-docker.js +49 -61
  223. package/dist/plugins/remote-env-ssh.d.ts +11 -19
  224. package/dist/plugins/remote-env-ssh.js +74 -57
  225. package/dist/plugins/remote-shell.d.ts +100 -3
  226. package/dist/plugins/remote-shell.js +166 -2
  227. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  228. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  229. package/dist/plugins/retention-lane-store-sql.js +2 -2
  230. package/dist/plugins/retention-store-sql.d.ts +10 -7
  231. package/dist/plugins/retention-store-sql.js +13 -7
  232. package/dist/plugins/run-store-sql.d.ts +13 -3
  233. package/dist/plugins/run-store-sql.js +20 -15
  234. package/dist/plugins/schema-consistency.d.ts +21 -11
  235. package/dist/plugins/schema-consistency.js +15 -9
  236. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  237. package/dist/plugins/schema-drift-sentence.js +38 -0
  238. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  239. package/dist/plugins/schema-ensure-legs.js +2 -2
  240. package/dist/plugins/schema-live-columns.d.ts +1 -1
  241. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  242. package/dist/plugins/session-fork.d.ts +54 -0
  243. package/dist/plugins/session-fork.js +38 -1
  244. package/dist/plugins/session-row-sql.d.ts +22 -0
  245. package/dist/plugins/session-row-sql.js +81 -0
  246. package/dist/plugins/session-rules-column.d.ts +1 -1
  247. package/dist/plugins/session-rules-column.js +1 -1
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  250. package/dist/plugins/session-workspace-cell.js +1 -1
  251. package/dist/plugins/shared-memory-store-sql.js +2 -2
  252. package/dist/plugins/sql-driver.d.ts +10 -5
  253. package/dist/plugins/sql-driver.js +6 -6
  254. package/dist/plugins/sql-errors.js +1 -1
  255. package/dist/plugins/store-backend.d.ts +5 -7
  256. package/dist/plugins/store-backend.js +23 -11
  257. package/dist/plugins/store-contracts.d.ts +25 -5
  258. package/dist/plugins/task-list-store-sql.js +11 -11
  259. package/dist/plugins/tidb-pool.d.ts +12 -7
  260. package/dist/plugins/tidb-pool.js +13 -7
  261. package/dist/plugins/tidb-session-storage.js +4 -4
  262. package/dist/plugins/tidb-session-store.d.ts +57 -26
  263. package/dist/plugins/tidb-session-store.js +127 -59
  264. package/dist/plugins/tool-result-store-sql.d.ts +5 -4
  265. package/dist/plugins/tool-result-store-sql.js +21 -13
  266. package/dist/plugins/user-input-ledger-sql.js +2 -2
  267. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  268. package/dist/plugins/workflow-run-store-sql.js +19 -19
  269. package/dist/plugins/write-behind-counter.d.ts +2 -3
  270. package/dist/plugins/write-behind-counter.js +5 -5
  271. package/dist/project-memory.d.ts +9 -15
  272. package/dist/project-memory.js +5 -4
  273. package/dist/request-key-closure.js +1 -1
  274. package/dist/request-value-closure.d.ts +6 -7
  275. package/dist/request-value-closure.js +1 -3
  276. package/dist/resource-suspend.d.ts +14 -12
  277. package/dist/resource-suspend.js +1 -1
  278. package/dist/resource-window.d.ts +4 -3
  279. package/dist/run-local.d.ts +6 -0
  280. package/dist/run-local.js +34 -35
  281. package/dist/runs.d.ts +10 -5
  282. package/dist/runs.js +52 -66
  283. package/dist/security.d.ts +38 -12
  284. package/dist/security.js +8 -2
  285. package/dist/server-secret-env.d.ts +9 -5
  286. package/dist/session-children-retire.d.ts +27 -9
  287. package/dist/session-children-retire.js +9 -5
  288. package/dist/session-incarnation.d.ts +20 -10
  289. package/dist/session-incarnation.js +5 -3
  290. package/dist/session-policy-wire.d.ts +1 -1
  291. package/dist/session-row.d.ts +114 -5
  292. package/dist/session-row.js +55 -5
  293. package/dist/session-titler.d.ts +2 -2
  294. package/dist/sql-engine-posture.d.ts +1 -1
  295. package/dist/sql-engine-posture.js +1 -1
  296. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  297. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  298. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  299. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  300. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  301. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  302. package/dist/task-a2a.d.ts +1 -1
  303. package/dist/task-a2a.js +5 -6
  304. package/dist/task-cwd.d.ts +6 -1
  305. package/dist/task-mcp.d.ts +22 -9
  306. package/dist/task-mcp.js +17 -9
  307. package/dist/terminal.d.ts +27 -0
  308. package/dist/terminal.js +6 -0
  309. package/dist/tool-approval.d.ts +6 -0
  310. package/dist/tool-approval.js +8 -1
  311. package/dist/trace/project.d.ts +1 -0
  312. package/dist/trace/project.js +1 -0
  313. package/dist/trace/redact.d.ts +3 -1
  314. package/dist/trace/redact.js +80 -28
  315. package/dist/trace/self-minted-wire.d.ts +169 -2
  316. package/dist/trace/self-minted-wire.js +54 -1
  317. package/dist/url-refusal.d.ts +1 -1
  318. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  319. package/dist/web-admin-client/apply-effective.js +20 -10
  320. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  321. package/dist/web-admin-client/candidate-gate.js +1 -1
  322. package/dist/web-admin-client/http-client.d.ts +5 -4
  323. package/dist/web-admin-client/http-client.js +2 -2
  324. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  325. package/dist/web-admin-client/read-warnings.js +5 -9
  326. package/dist/web-admin-client/skills-mcp.js +3 -2
  327. package/dist/web-admin-client/types.d.ts +6 -0
  328. package/package.json +3 -3
  329. package/wire-changes/server@7.139.0.json +348 -0
  330. package/wire-changes/server@7.140.0.json +184 -0
  331. package/dist/http/device-placement.d.ts +0 -175
  332. package/dist/http/device-placement.js +0 -145
@@ -1,17 +1,14 @@
1
1
  import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
2
2
  import { createHash } from "node:crypto";
3
- import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY, PAUSE_REGISTRY } from "@sema-agent/core";
3
+ import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY } from "@sema-agent/core";
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
7
  import { createSessionWorkspaceRecorder, legSessionWitnessOf, sessionWorkspaceStoreOf } from "../session-workspace.js";
8
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";
9
- const headUnavailable = async (sessionId) => {
10
- 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`);
11
- };
12
9
  import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
13
10
  import { graftParkToolCallId, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
14
- import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
11
+ import { atHostBoundary, failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
15
12
  import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../run-cancel-context.js";
16
13
  import { isTerminalRunStatus } from "../plugins/store-contracts.js";
17
14
  import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
@@ -50,10 +47,11 @@ import { createImagesLocal } from "./routes/images.js";
50
47
  import { createCapabilitiesLocal } from "./routes/capabilities.js";
51
48
  import { gatedPrincipal, explicitOperatorOk, runIdentityOf } from "./principal-gate.js";
52
49
  import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
53
- import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
50
+ import { commitExecutionContext, isRootSessionGoneError, resolveExecutionContext, rootSessionAbsentAnswer, stampSpecRoot } from "./execution-context.js";
54
51
  import { commitSessionInheritEnv } from "./session-inherit-env.js";
55
52
  import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind, isEngineQuestionRow } from "../approval-content-kind.js";
56
53
  import { deploymentListAsks } from "../approval.js";
54
+ import { isParkLedgerEventType } from "../trace/ledger-events.js";
57
55
  import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnsealableAnswer } from "./resume-ctx-read.js";
58
56
  import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
59
57
  import { refusalBody, resumeRefusalFacts } from "./refusal-body.js";
@@ -64,12 +62,6 @@ import { fieldConflictFacts } from "../field-conflict.js";
64
62
  function legMayHaveReopenedCheckpoint(r) {
65
63
  return r.terminal.kind === "failed" && nestedPauseOf(r.terminal) === undefined;
66
64
  }
67
- function nestedParkStatusOf(r) {
68
- const paused = nestedPauseOf(r.terminal);
69
- if (paused === undefined)
70
- return undefined;
71
- return PAUSE_REGISTRY[paused.gate.kind].taskStatus;
72
- }
73
65
  const WORKER_SWAP_REDEEMABLE = {
74
66
  version_newer: true,
75
67
  env_factory_missing: true,
@@ -102,6 +94,7 @@ const WORKER_SWAP_REDEEMABLE = {
102
94
  unknown_model_ref: true,
103
95
  not_a_model_ref: true,
104
96
  unknown_role_word: true,
97
+ no_model_for_role: true,
105
98
  };
106
99
  function checkpointRowRedeemableElsewhere(reason) {
107
100
  return reason !== undefined && WORKER_SWAP_REDEEMABLE[reason] === true;
@@ -416,10 +409,30 @@ export function createResumeLegs(ctx) {
416
409
  checkpointStore: cs,
417
410
  registry: defaultTaskRegistry,
418
411
  reviveSurface: parkedSurface,
419
- headOf: headReadOf(deps.sessionStorage) ?? headUnavailable,
420
412
  ...(deps.parkedKnownAgentTypes ? { knownAgentTypes: deps.parkedKnownAgentTypes } : {}),
421
413
  ...(deps.parkedReviveInheritedGate ? { rebuildInheritedGate: deps.parkedReviveInheritedGate } : {}),
422
414
  ...(deps.toolApproval ? { approverSeatFor: windowZeroSeatOf(deps.toolApproval) } : {}),
415
+ execution: {
416
+ resolve: async (root) => {
417
+ let ctx;
418
+ try {
419
+ ctx = await resolveExecutionContext(deps, { kind: "continuation", root, principal: decodeCheckpointScope(cp.scope) });
420
+ }
421
+ catch (e) {
422
+ if (isRootSessionGoneError(e))
423
+ return { ok: false, gone: true };
424
+ if (e instanceof HttpError)
425
+ return { ok: false, gone: false, answer: resumeRefusalWithCard(resumeBlockedByPolicyAnswer(httpErrorRefusal(e)), true) };
426
+ throw e;
427
+ }
428
+ const occupant = ctx.occupant;
429
+ if (occupant === undefined) {
430
+ 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`);
431
+ }
432
+ return { ok: true, ctx: { ...ctx, occupant } };
433
+ },
434
+ commitDeps: deps,
435
+ },
423
436
  ...(deps.logger ? { warn: parkWarn } : {}),
424
437
  }, { ...parkedReq, selectedGate: captureSelectedGate() }, { handle: origin.handle, row: origin.row }));
425
438
  void ctxPromise.catch(() => { });
@@ -520,7 +533,7 @@ export function createResumeLegs(ctx) {
520
533
  hostDecision,
521
534
  };
522
535
  const verifyRounds = verifyRoundsFromBody(ctx.body);
523
- const driven = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, 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 } : {}) });
536
+ 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 } : {}) });
524
537
  noteLateSettleIfAccepted(driven);
525
538
  return driven;
526
539
  }
@@ -528,17 +541,19 @@ export function createResumeLegs(ctx) {
528
541
  resolved.peerLease.release();
529
542
  }
530
543
  }
531
- const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles", "userAttachments", "images"];
544
+ const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "userAttachments", "images"];
532
545
  const ONE_SHOT_SUBMIT_STRIP = Object.fromEntries(ONE_SHOT_SUBMIT_KEYS.map((k) => [k, undefined]));
533
546
  const resolveResumeSpec = async (ctx, req, auth, lease) => {
534
547
  const engineNotices = [];
535
- const spec = await deps.resolveSpec({ ...ctx.body, ...ONE_SHOT_SUBMIT_STRIP }, req, auth, {
548
+ const execution = await resolveExecutionContext(deps, { kind: "continuation", root: auth.sessionId, principal: auth.principal });
549
+ const spec = stampSpecRoot(await deps.resolveSpec({ ...ctx.body, ...ONE_SHOT_SUBMIT_STRIP }, req, auth, {
536
550
  leg: "resume",
537
551
  onNotice: (n) => engineNotices.push(n),
538
552
  ...(ctx.unattendedLeg === true ? { unattendedLeg: true } : {}),
539
553
  lease,
540
- });
541
- return { spec, engineNotices };
554
+ execution,
555
+ }), execution);
556
+ return { spec, engineNotices, execution };
542
557
  };
543
558
  const refuseStoredBodyReplay = (sessionId, refusal) => {
544
559
  deps.logger?.warn?.("resume_refused_stored_body", { sessionId, ...storedBodyReplayLogFields(refusal) });
@@ -550,8 +565,8 @@ export function createResumeLegs(ctx) {
550
565
  return { ok: false, answer: refuseStoredBodyReplay(auth.sessionId, replay) };
551
566
  const lease = deps.peerDesk.lease("resume");
552
567
  try {
553
- const { spec, engineNotices } = await resolveResumeSpec(ctx, req, auth, lease);
554
- return { ok: true, spec, engineNotices, peerLease: lease };
568
+ const { spec, engineNotices, execution } = await resolveResumeSpec(ctx, req, auth, lease);
569
+ return { ok: true, spec, engineNotices, execution, peerLease: lease };
555
570
  }
556
571
  catch (e) {
557
572
  lease.release();
@@ -583,7 +598,7 @@ export function createResumeLegs(ctx) {
583
598
  }
584
599
  async function driveResumeLeg(args, onAccepted) {
585
600
  const resumeLegStartedAt = Date.now();
586
- const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx } = args;
601
+ const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx, execution } = args;
587
602
  const reparkStatus = outcome.gate === "plan_review" ? "needs_review" : "suspended";
588
603
  const commitResumeCtx = () => Promise.resolve().then(() => deps.checkpointStore.putCtx(resumeCtx.sessionId, sealCheckpointCtx({ ...resumeCtx.ctx, writerVersion: serviceVersion() }, deps.checkpointCtxSealing, resumeCtx.sessionId))).catch((e) => {
589
604
  deps.metrics?.inc("resume_ctx_not_persisted_total");
@@ -627,12 +642,14 @@ export function createResumeLegs(ctx) {
627
642
  if (created.existing) {
628
643
  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`);
629
644
  }
645
+ if (created.sessionAbsent)
646
+ return rootSessionAbsentAnswer();
630
647
  if (!created.ok) {
631
648
  return { status: 409, body: await buildActiveRunConflict({ runStore: deps.runStore, checkpointStore: deps.checkpointStore, governance: deps.config, runStaleSec: deps.config.runStaleSec, turnActivity: readTurnActivityMs }, sessionId, created.activeTaskId) };
632
649
  }
633
- const placementReject = await commitPlacement(deps, deps.runStore, freshId, undefined);
650
+ const placementReject = await commitExecutionContext(deps, { kind: "run", runStore: deps.runStore, taskId: freshId }, execution);
634
651
  if (placementReject)
635
- return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
652
+ return placementReject;
636
653
  taskId = freshId;
637
654
  freshRunClaimed = true;
638
655
  }
@@ -643,11 +660,12 @@ export function createResumeLegs(ctx) {
643
660
  if (deps.toolApproval && taskId) {
644
661
  const toolApproval = deps.toolApproval;
645
662
  resumeTaskConfig.onAsk = approvalLeg.unattended
646
- ? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId }, approvalLeg.unattended)
663
+ ? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId, executionLane: execution.lane }, approvalLeg.unattended)
647
664
  : toolApproval.boundAsk({
648
665
  owner: principal ?? null,
649
666
  taskId,
650
667
  sessionId,
668
+ executionLane: execution.lane,
651
669
  ...(approvalLeg.active ? { legKey, ...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}) } : {}),
652
670
  });
653
671
  }
@@ -713,6 +731,11 @@ export function createResumeLegs(ctx) {
713
731
  claimedRow = freshRunClaimed ? true : await deps.runStore.markResuming(taskId, deps.instanceId ?? "default");
714
732
  if (!claimedRow)
715
733
  return notResumableAnswer();
734
+ if (!freshRunClaimed) {
735
+ const placementReject = await commitExecutionContext(deps, { kind: "run", runStore: deps.runStore, taskId }, execution);
736
+ if (placementReject)
737
+ return placementReject;
738
+ }
716
739
  markChildrenStoppedByUserOnAbort(cancelCtrl.signal, taskId, principal);
717
740
  fleetPub?.onStart();
718
741
  const rs = deps.runStore;
@@ -827,12 +850,14 @@ export function createResumeLegs(ctx) {
827
850
  resumeLegLive = true;
828
851
  const vrEngine = await resumeWithVerification(legRunner, token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, resumeObjective, verifyRounds, verifyInternals);
829
852
  const vrCause = cancelCtrl.cause();
830
- const vr = vrCause !== undefined && taskId !== undefined ? mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user", engineResult: vrEngine, cause: vrCause }) : vrEngine;
853
+ const vrJudged = atHostBoundary(vrEngine, sessionId);
854
+ const vr = vrCause !== undefined && taskId !== undefined ? mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user", engineResult: vrJudged, cause: vrCause }) : vrJudged;
831
855
  await commitResumeCtx();
832
856
  let safe = sealResultForExit(vr);
833
- const vrNested = nestedPauseOf(vr.terminal);
834
- const reParkStatus = nestedParkStatusOf(vr);
835
- const reSuspended = vrNested !== undefined;
857
+ const vrPaused = vr.terminal.kind === "paused" ? vr.terminal : undefined;
858
+ const vrParkWord = vrPaused !== undefined ? outwardPlane(vrPaused).status : undefined;
859
+ const reParkEvent = vrParkWord !== undefined && isParkLedgerEventType(vrParkWord) ? vrParkWord : undefined;
860
+ const reSuspended = reParkEvent !== undefined;
836
861
  const vrReopened = await reopenedAfterLeg(vr, vrCause !== undefined);
837
862
  if (vrReopened)
838
863
  reopenConfirmedPark = true;
@@ -843,8 +868,8 @@ export function createResumeLegs(ctx) {
843
868
  await appendPromptManifest(ap, deps.promptManifests, taskId);
844
869
  if (reSuspended || vrReopened) {
845
870
  await appendModelUsageDelta(ap, deps.modelUsage, taskId);
846
- const vrParkCallId = vrNested !== undefined ? await parkToolCallId({ terminal: vrNested }, deps.checkpointStore) : undefined;
847
- await ap(reParkStatus ?? reparkStatus, parkEventData({ terminal: vrNested, ...(vrParkCallId !== undefined ? { toolCallId: vrParkCallId } : {}) }, vrReopened ? (failureCodeOf(vr.terminal) ?? null) : undefined));
871
+ const vrParkCallId = reSuspended ? await parkToolCallId(vr, deps.checkpointStore) : undefined;
872
+ await ap(reParkEvent ?? reparkStatus, parkEventData({ terminal: vrPaused, ...(vrParkCallId !== undefined ? { toolCallId: vrParkCallId } : {}) }, vrReopened ? (failureCodeOf(vr.terminal) ?? null) : undefined));
848
873
  }
849
874
  else {
850
875
  safe = await attachModelUsage(safe, { append: ap, getEvents: (id, a) => rs2.getEvents(id, a), modelUsage: deps.modelUsage, taskId });
@@ -856,8 +881,8 @@ export function createResumeLegs(ctx) {
856
881
  throw e;
857
882
  }
858
883
  }
859
- if (vrNested !== undefined)
860
- return { ...safe, terminal: vrNested };
884
+ if (reSuspended)
885
+ return safe;
861
886
  if (vr.verification?.verdict)
862
887
  deps.metrics?.inc("verifications_total", { verdict: vr.verification.verdict });
863
888
  return safe;
@@ -1140,6 +1165,7 @@ export function createResumeLegs(ctx) {
1140
1165
  owner: principal ?? null,
1141
1166
  abortSignal: cancelCtrl.signal,
1142
1167
  sessionId,
1168
+ executionLane: execution.lane,
1143
1169
  emitCard: createApprovalCardEmitter({ appendDurable: (f) => approvalEmit(f) }),
1144
1170
  emitRevoke: (f) => approvalEmit(f),
1145
1171
  legKey,
@@ -1328,7 +1354,7 @@ export function createResumeLegs(ctx) {
1328
1354
  const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1329
1355
  const verifyRounds = verifyRoundsFromBody(ctx.body);
1330
1356
  const selectedGate = durableRowIdentity("decision-receipt", cp.checkpointId, token);
1331
- return await driveResumeIntoRunLog({ peerLease: resolved.peerLease, 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 } : {}) });
1357
+ 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 } : {}) });
1332
1358
  }
1333
1359
  finally {
1334
1360
  resolved.peerLease.release();
@@ -1367,9 +1393,9 @@ export function createResumeLegs(ctx) {
1367
1393
  return resolved.answer;
1368
1394
  try {
1369
1395
  const { spec, engineNotices } = resolved;
1370
- const hostSpec = { ...spec, objective, sessionId };
1396
+ const hostSpec = { ...spec, objective };
1371
1397
  const out = await mintAndDriveDurableRun(routeCtxBase, deps.runStore, {
1372
- prepared: { spec: hostSpec, runner: runnerFor(hostSpec), auth, body: ctx.body, acceptance: createRunAcceptance({}, { objective }), peerLease: resolved.peerLease, ...(engineNotices.length > 0 ? { engineNotices } : {}) },
1398
+ prepared: { spec: hostSpec, runner: runnerFor(hostSpec), auth, body: ctx.body, acceptance: createRunAcceptance({}, { objective }), peerLease: resolved.peerLease, execution: resolved.execution, ...(engineNotices.length > 0 ? { engineNotices } : {}) },
1373
1399
  fleetScope: resumeFleetScope(req, auth),
1374
1400
  elicitOwner: principal ?? null,
1375
1401
  source: "workflow_parked_decide",
@@ -1455,6 +1481,7 @@ export function createResumeLegs(ctx) {
1455
1481
  const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
1456
1482
  return await driveResumeIntoRunLog({
1457
1483
  peerLease: resolved.peerLease,
1484
+ execution: resolved.execution,
1458
1485
  runner: runnerFor(spec),
1459
1486
  token,
1460
1487
  sessionId: cp.sessionId,
@@ -1581,7 +1608,7 @@ export function createResumeLegs(ctx) {
1581
1608
  hostDecision,
1582
1609
  };
1583
1610
  const selectedGate = durableRowIdentity("decision-receipt", cp.checkpointId, token);
1584
- const out = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, 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 } : {}) });
1611
+ 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 } : {}) });
1585
1612
  return out;
1586
1613
  }
1587
1614
  finally {
@@ -22,7 +22,7 @@ export interface RetiredServiceDep {
22
22
  export declare const RETIRED_SERVICE_DEPS: {
23
23
  readonly sqlEngineFacts: {
24
24
  readonly retiredIn: "7.132.0";
25
- readonly wayOut: "replaced by sqlEngine — pass the SQL pool's engine reading as sqlEngine: `{ dialect: \"tidb\" | \"pg\", facts }` where `facts` is read live (a getter) and is null while the pool's connection setup has not succeeded, or leave both unset when there is no SQL store";
25
+ readonly wayOut: "replaced by sqlEngine — pass the SQL pool's engine reading as sqlEngine: `{ dialect: \"mysql\" | \"pg\", facts }` where `facts` is read live (a getter) and is null while the pool's connection setup has not succeeded, or leave both unset when there is no SQL store";
26
26
  };
27
27
  readonly snapshotBlobSqlCapBytes: {
28
28
  readonly retiredIn: "7.132.0";
@@ -2,7 +2,7 @@ const BLOB_CAP_WAY_OUT = "the HTTP surface no longer holds a blob cap of its own
2
2
  export const RETIRED_SERVICE_DEPS = {
3
3
  sqlEngineFacts: {
4
4
  retiredIn: "7.132.0",
5
- wayOut: "replaced by sqlEngine — pass the SQL pool's engine reading as sqlEngine: `{ dialect: \"tidb\" | \"pg\", facts }` where `facts` is read live (a getter) and is null while the pool's connection setup has not succeeded, or leave both unset when there is no SQL store",
5
+ wayOut: "replaced by sqlEngine — pass the SQL pool's engine reading as sqlEngine: `{ dialect: \"mysql\" | \"pg\", facts }` where `facts` is read live (a getter) and is null while the pool's connection setup has not succeeded, or leave both unset when there is no SQL store",
6
6
  },
7
7
  snapshotBlobSqlCapBytes: { retiredIn: "7.132.0", wayOut: BLOB_CAP_WAY_OUT },
8
8
  snapshotBlobSqlCap: { retiredIn: "7.133.0", wayOut: BLOB_CAP_WAY_OUT },
@@ -32,6 +32,7 @@ import type { VerifyRoundsSpec } from "./verify-rounds.js";
32
32
  import type { RunAbort } from "../run-cancel-context.js";
33
33
  import type { RunIdentity } from "../identity-source.js";
34
34
  import type { PeerLease } from "../peer-seat.js";
35
+ import type { RootedTaskSpec } from "./execution-context.js";
35
36
  import type { GateCommitment, GateSettlementEvidence } from "../gate-settlement.js";
36
37
  /** 跨域可变状态 = lens1 §C2 实测的那 8 条「运行期登记簿」,一个对象。
37
38
  * [ref]:`cancelledViaVerb`(按 taskId 键控的「这次是取消动词」旁表)删除 —— 中止成因改挂在每条腿自己的
@@ -117,11 +118,13 @@ export interface RouteRequestState {
117
118
  /** B4①(命名化):`prepareSpec` 的返回形(tasks.ts/runs.ts 两域共同消费的属性面)——原为 6 成员内联匿名对象。
118
119
  * 命名后好处不只是过 B4 门:两个消费域现在都能对同一个符号做 `import type` 标注,而不是各自重推断结构。 */
119
120
  export interface PreparedTaskSubmission {
120
- spec: TaskSpec;
121
+ /** 盖过根的 spec(`http/execution-context.ts` `stampSpecRoot` 是唯一写者):`sessionId` 恒在 = 这次提交的根会话。 */
122
+ spec: RootedTaskSpec;
121
123
  /** [ref]:本次提交的执行 Runner —— 场景 hands=none 时是无手孪生(不挂 executionEnvFactory)。
122
124
  * 两个消费域一律用它,不再直接摸 `deps.runner`(那正是「场景表态了、执行点没听」的缝)。 */
123
125
  runner: Runner;
124
- auth?: RequestAuth;
126
+ /** authorizer 交出的身份与根(必填席,恒在;续跑家族的内部车道交它从 checkpoint 重建的那一只)。 */
127
+ auth: RequestAuth;
125
128
  verify?: VerifyRoundsSpec;
126
129
  cascade?: boolean;
127
130
  jobId?: string;
@@ -137,13 +140,11 @@ export interface PreparedTaskSubmission {
137
140
  */
138
141
  warnings?: readonly EntryProblem[];
139
142
  /**
140
- * [ref](device lane 首绑写协议,design §4.3.2 两阶段 placement)—— **已校验、未写库**的 placement 意图。
141
- *
142
- * 缺席的两种含义都等价于「本腿没有 placement 要提交」:①本部署不是 device 车道(绝大多数);
143
- * ②本腿不是 fresh 根提交(resume 家族根本不经 `prepareSpec`)。在场 ⇒ 每个 `runStore.createRun(`
144
- * 成功点**必须**把它交给 `commitPlacement`(唯一写入属主;类级机器门钉住每个调用点都接了)。
143
+ * 这次执行的**执行上下文**(唯一解析器 `http/execution-context.ts` `resolveExecutionContext` 的产物:根 · 住客 · 放置态 · 车道 · device 车道的设备意图)。
144
+ * 提交门在装配之前铸(fresh);`/decide` 的宿主新 turn 交续跑折叠器铸的那一只(continuation)。每个 `runStore.createRun(` 成功点**必须**把它交给
145
+ * 认领之后的唯一写口 `commitExecutionContext`(类级机器门钉住每个调用点都接了)。
145
146
  */
146
- bindingIntent?: import("./device-placement.js").DevicePlacementIntent;
147
+ execution: import("./execution-context.js").RootedExecutionContext;
147
148
  /**
148
149
  * 这次提交的**受理座**(`run-acceptance.ts`):标题器只在引擎受理之后起。提交门(`prepareSpec`)随 spec 一起铸;
149
150
  * 三条提交腿只喂它(持流的形逐帧 `observe`,终局落行之后 `settle` 一次),不再各自判「什么时候起标题」。
@@ -226,6 +227,11 @@ export interface WorkflowDecisionDelivery {
226
227
  /** B4①(命名化):`driveResumeIntoRunLog` 的入参形(resume 家族共用)——原为 9 成员内联匿名对象。 */
227
228
  export interface DriveResumeArgs {
228
229
  token: CheckpointToken;
230
+ /**
231
+ * 这条续跑的执行上下文(续跑折叠器经唯一解析器铸的 continuation 形:根 = checkpoint 的会话)。本腿认领之后(wake 的建行 / `markResuming` CAS 赢)
232
+ * 交给唯一写口 `commitExecutionContext`(按解析器那一读的住客再断言 writer(L) / absent 落格;住客换了 ⇒ 释放 + 404)。
233
+ */
234
+ execution: import("./execution-context.js").RootedExecutionContext;
229
235
  /**
230
236
  * 🔴 **[ref] 合并复审 F2 [high] —— 「本次续跑真正用的那个体」,由本腿在赢下 CAS 之后写回耐久层**
231
237
  * (验真后修;必填 —— `driveResumeIntoRunLog` 今天恰有**四个**调用点:legacy `/decide`(`resumeCheckpoint`)、
@@ -69,7 +69,7 @@ export async function handleAdoption(req, res, match, ctx) {
69
69
  }
70
70
  }
71
71
  const store = fact.backend.adoptionLog();
72
- const runner = createAdoptionRunner({ store, dialect: deps.backend?.kind === "pg" ? "pg" : "tidb", now: Date.now, logger: deps.logger });
72
+ const runner = createAdoptionRunner({ store, dialect: deps.backend?.kind === "pg" ? "pg" : "mysql", now: Date.now, logger: deps.logger });
73
73
  switch (match.decl.id) {
74
74
  case "adoption-detail": {
75
75
  const id = ctx.helpers.safeDecode(match.params.id);
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * [ref](core 7.32.0 [ref] 的 server 半场;wire 形 [ref] / [ref] / [ref] 定)—— **会话后台任务的两只读口**:
3
3
  *
4
- * `GET /v1/sessions/:id/background` → 200 `{ rows: [{ id, description?, type, status, createdAt, sessionScoped, retained? }] }`
4
+ * `GET /v1/sessions/:id/background` → 200 `{ rows: [{ id, description?, type, status, createdAt, sessionScoped, retained?, sessionId? }] }`
5
5
  * `POST /v1/sessions/:id/background/stop` body `{ includeRetained: boolean }` → 200 `{ receipts: [{ id, outcome }] }`
6
6
  *
7
7
  * 消费方:cli [ref] ①(删前核登记)/ [ref](退出三选一:「Exit and stop tasks」= `includeRetained:true`,`sema kill` =
@@ -28,6 +28,16 @@
28
28
  * oracle);显式 operator 可读可停任何会话(监督动作)。零模型工作 ⇒ 不进 billable、不吃 drain 503;停止口是 mutating ⇒
29
29
  * `rateLimited`。能力位 `background: { listFace, exitFaces }` 与两口的 501 从同一个谓词推导(会话属主面在场)。
30
30
  *
31
+ * ── 结清口:子代结清只有一只协调器 ─────────────────────────────────────────────────────────────────
32
+ * `POST /v1/sessions/:id/children/retire`(无体)→ 200 `{ children: string[] }`:**显式**结清以本会话为根的后台子代(running 停掉、parked
33
+ * 作废卡并对账翻终态),与 `DELETE /v1/sessions/:id` 同调**一只**预检 `probeSessionChildren` 与**一只**协调器 `retireSessionChildren`
34
+ * (`session-children-retire.ts`)、同一个 scope(`sessionChildrenScope`)。预检有本副本结束不了的 ⇒ 409 `conflict.session_children_alive{remaining}`
35
+ * (与删除门同码同投影,一只都不动);停不净(时序失败)⇒ 与删除门同一失败契约(通用 500,重试收敛)。`children` = 首轮观察到、且已全部
36
+ * 结清的子代,每项经名册的 id 投影 `rosterAgentId`(与换绑 / 导入两门 409 体的 `children` 同形同投影)。它不判根认领(停止口也不判):
37
+ * 根 run 活着时它可再派子代 —— 由换绑 / 导入门写事务内的认领读兜。鉴权 / 限流 / 无凭据写门与停止口同。
38
+ * **停止口的已发语义逐字不动**:它只中止 running 的后台 agent / workflow 并收 shell,停驻子代与待决卡留着 —— 「停别的、留卡待决」与
39
+ * 「把树结清」是两个意图、两只口。
40
+ *
31
41
  * 🔴 **停止口在无凭据写门内**(`credentialGated: ["POST"]`;车OL F1 = 7.102.0 合并复审 gpt-6 腿 [high]):门的两个合取项它都中
32
42
  * —— ① 授权的唯一输入是 principal 头(属主判 `sessionOwnerOk` 与 operator 臂 `explicitOperatorOk` 读的都是 `gatedPrincipal`,
33
43
  * 无 service 凭证的部署上它收调用方自报的头);② 不可撤销、爆炸半径跨租户(知道会话 id + 自报它的属主 ⇒ 停掉那个会话的后台
@@ -53,12 +63,24 @@ export interface SessionBackgroundRow {
53
63
  createdAt: number;
54
64
  sessionScoped: boolean;
55
65
  retained?: true;
66
+ /** 后台 agent 行的子会话 id(引擎绑定之后才有;缺席 ⇒ 键不在)。 */
67
+ sessionId?: string;
56
68
  }
57
69
  /** 停止口的一条受据(`outcome` 逐字透传 core 闭集)。 */
58
70
  export interface SessionBackgroundReceipt {
59
71
  id: string;
60
72
  outcome: BackgroundShellHardKillReceipt["outcome"];
61
73
  }
74
+ /** 结清口的路径(换绑 / 导入两门的子代半 409 以它点名下一步;与执行面拒体带 `forkPath` 同一形)。 */
75
+ export declare function childrenRetirePath(sessionId: string): string;
76
+ /**
77
+ * 子代半命中的拒体两键(换绑 409 `device.rebind_active_run`、导入 409 `session_active` 同一只):`children` = 预检那一次枚举观察到的活子代
78
+ * (`probeSessionChildren` 的 `liveChildren`)经名册 id 投影,`retirePath` = 结清口。
79
+ */
80
+ export declare function liveChildrenRefusal(sessionId: string, liveChildren: readonly string[]): {
81
+ children: string[];
82
+ retirePath: string;
83
+ };
62
84
  export declare function handleBackground(req: IncomingMessage, res: ServerResponse, match: RouteMatch<BackgroundRouteId>, ctx: RouteCtx): Promise<void>;
63
85
  export declare const BACKGROUND_ROUTES: readonly [{
64
86
  readonly id: "session-background-list";
@@ -71,6 +93,12 @@ export declare const BACKGROUND_ROUTES: readonly [{
71
93
  readonly label: "/v1/sessions/:id/background/stop";
72
94
  readonly credentialGated: readonly ["POST"];
73
95
  readonly methods: readonly ["POST"];
96
+ }, {
97
+ readonly id: "session-children-retire";
98
+ readonly pattern: RegExp;
99
+ readonly label: "/v1/sessions/:id/children/retire";
100
+ readonly credentialGated: readonly ["POST"];
101
+ readonly methods: readonly ["POST"];
74
102
  }];
75
103
  /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
76
104
  export type BackgroundRouteId = RouteIdsOf<typeof BACKGROUND_ROUTES>;
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { defaultTaskRegistry } from "@sema-agent/core";
3
3
  import { reapSessionBackgroundHard } from "../../background-session-reap.js";
4
+ import { probeSessionChildren, retireSessionChildren, sessionChildrenScope, SessionChildrenStopIncompleteError } from "../../session-children-retire.js";
5
+ import { rosterAgentId } from "./agents-roster.js";
4
6
  import { redactSecrets } from "../../trace/redact.js";
5
7
  import { sendJson, sendError } from "../send.js";
6
8
  import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
@@ -20,6 +22,7 @@ const ROW_KEY_DISPOSITION = {
20
22
  description: "keep",
21
23
  toolUseId: "strip",
22
24
  updatedAt: "strip",
25
+ sessionId: "keep",
23
26
  };
24
27
  void ROW_KEY_DISPOSITION;
25
28
  function projectRow(r) {
@@ -31,13 +34,20 @@ function projectRow(r) {
31
34
  createdAt: r.createdAt,
32
35
  sessionScoped: r.sessionScoped,
33
36
  ...(r.retained === true ? { retained: true } : {}),
37
+ ...(r.sessionId !== undefined ? { sessionId: r.sessionId } : {}),
34
38
  };
35
39
  }
40
+ export function childrenRetirePath(sessionId) {
41
+ return `/v1/sessions/${encodeURIComponent(sessionId)}/children/retire`;
42
+ }
43
+ export function liveChildrenRefusal(sessionId, liveChildren) {
44
+ return { children: liveChildren.map((handle) => rosterAgentId({ handle })), retirePath: childrenRetirePath(sessionId) };
45
+ }
36
46
  const StopBodySchema = z.object({ includeRetained: z.boolean() }).strict();
37
47
  export async function handleBackground(req, res, match, ctx) {
38
48
  const { deps } = ctx;
39
49
  const id = match.decl.id;
40
- if (id === "session-background-stop" && ctx.helpers.rateLimited(req, res))
50
+ if ((id === "session-background-stop" || id === "session-children-retire") && ctx.helpers.rateLimited(req, res))
41
51
  return;
42
52
  const principal = gatedPrincipal(req, deps.config);
43
53
  if (deps.config.requirePrincipal && principal === undefined) {
@@ -71,7 +81,7 @@ export async function handleBackground(req, res, match, ctx) {
71
81
  const operator = explicitOperatorOk(principal, deps.config);
72
82
  if (!operator && !ctx.helpers.sessionOwnerOk(req, res, await sessionOwnerCellOf(owned.sessionStorage, sessionId, owner)))
73
83
  return;
74
- const scope = owner ?? "default";
84
+ const scope = sessionChildrenScope(owner);
75
85
  switch (id) {
76
86
  case "session-background-list": {
77
87
  const rows = defaultTaskRegistry.list({ owner: sessionId, sessionId, scope }, { limit: LIST_LIMIT }).map(projectRow);
@@ -83,6 +93,22 @@ export async function handleBackground(req, res, match, ctx) {
83
93
  sendJson(res, 200, { receipts: receipts.map((r) => ({ id: r.taskId, outcome: r.outcome })) });
84
94
  return;
85
95
  }
96
+ case "session-children-retire": {
97
+ const sources = { agentStore: deps.backgroundAgentStore, checkpointStore: deps.checkpointStore, registry: defaultTaskRegistry };
98
+ const reach = await probeSessionChildren(sources, sessionId, scope);
99
+ if (!reach.reachable) {
100
+ deps.logger?.warn("session_children_retire_unreachable", { sessionId, remaining: reach.unreachableHandles.length, handles: reach.unreachableHandles });
101
+ sendError(res, 409, "conflict.session_children_alive", "this session still has background agents running where this replica cannot stop them (on another replica, for example), so nothing was retired — `remaining` lists their ids (the same ids `GET /v1/agents/roster` shows); retry once they finish, or stop them where they run", { remaining: reach.unreachableHandles.map((handle) => rosterAgentId({ handle })) });
102
+ return;
103
+ }
104
+ const out = await retireSessionChildren(sources, sessionId, scope);
105
+ if (out.stopped === "incomplete") {
106
+ deps.logger?.warn("session_children_retire_incomplete", { sessionId, remaining: out.remainingHandles.length, handles: out.remainingHandles });
107
+ throw new SessionChildrenStopIncompleteError(sessionId, out.remainingHandles.length);
108
+ }
109
+ sendJson(res, 200, { children: out.observed.map((handle) => rosterAgentId({ handle })) });
110
+ return;
111
+ }
86
112
  default:
87
113
  return assertNever(id);
88
114
  }
@@ -90,5 +116,6 @@ export async function handleBackground(req, res, match, ctx) {
90
116
  export const BACKGROUND_ROUTES = [
91
117
  { id: "session-background-list", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/background$/, label: "/v1/sessions/:id/background", methods: ["GET"] },
92
118
  { id: "session-background-stop", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/background\/stop$/, label: "/v1/sessions/:id/background/stop", credentialGated: ["POST"], methods: ["POST"] },
119
+ { id: "session-children-retire", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/children\/retire$/, label: "/v1/sessions/:id/children/retire", credentialGated: ["POST"], methods: ["POST"] },
93
120
  ];
94
121
  //# sourceMappingURL=background.js.map
@@ -8,19 +8,19 @@
8
8
  * **hub 在场性**判 —— v1 部署形 = 单副本(基线稿 §4.6),汇聚端的活连接集就是本副本的 presence
9
9
  * 真源;`name` = displayName,空串诚实缺席;`lastSeenAt` ISO-8601)。**吊销行不列**:revoked 是
10
10
  * 终态(重注册 = 新 deviceId),列出来只会被壳当换绑目标然后撞 404。
11
- * · `GET /v1/devices/sessions/:rootSessionId` —— S-4 绑定读面(壳换绑 UX 的全部材料):
12
- * `{ rootSessionId, boundDevice: { deviceId, rev, boundAt } }`。稿 §4 允许「并入 session 读面」或
13
- * 「独立读面」两形,选**独立读面**:sessions 的 GET 面承诺「无参请求字节不变」(routes/sessions.ts
14
- * 的 additive contract 注),且 audit 投影由 boot/session-faces 组装、不持 device 店 —— 并入即跨
15
- * 装配缝加一次店读;独立读面把 device wire 族收在一个文件里,户口与门序自洽。
16
- * · `POST /v1/devices/sessions/:rootSessionId/rebind` —— 显式换绑动词(v2 稿 §2 门序五步,逐字):
17
- * ① gatedPrincipal + owner 门(非属主 404 反枚举同串,[ref] 纪律)
11
+ * · 绑定读面在会话族:`GET /v1/sessions/:id/execution-placement`(`routes/sessions.ts`;体 = 执行放置格的 wire 投影,
12
+ * device 车道上是 `{ role: "writer", lane: "device", device: { deviceId, rev } }`)。绑定是根会话行上的执行格,读只有会话店的执行席一只。
13
+ * · `POST /v1/devices/sessions/:rootSessionId/rebind` —— 显式换绑动词(v2 稿 §2 门序五步):
14
+ * ① gatedPrincipal + owner 门 —— 一次行读(会话店执行席:住客 + 格),属主门判**格上**两属主列(非属主 404 反枚举同串,[ref] 纪律)
18
15
  * ② 目标设备在场门(不存在 / 不是该域的 / 已吊销 ⇒ 404 **同串** —— 设备 id 也是不可枚举面)
19
- * ③ 无活跃 run 门(**CAS 前判、CAS 语句不兼职**;durable 判据 = run store 的 session claim
20
- * `getActiveTaskId`,hub 内存在途计数不作门 —— 内存态跨副本不可见;拒 = 409
21
- * `device.rebind_active_run` 体带 `activeTaskId`)
22
- * ④ 前态 CAS(店内单事务:`rev` 恒变列 + `session_rebound` 审计行;输 = 409
23
- * `device.rebind_rev_conflict` 体带 `currentRev` —— [ref] currentPending 哲学,壳一跳重定位)
16
+ * ③ 无活跃 run 门:池级 `getActiveTaskId` 读一次作**早拒**(零审计行);**保证**在设备店的换绑事务里 —— 同一连接先锁根会话行、
17
+ * 再锁定读 `task_active`,有认领 ⇒ 不写(读写序律;hub 内存在途计数不作门 —— 内存态跨副本不可见);两处同拒 409
18
+ * `device.rebind_active_run` 体带 `activeTaskId`
19
+ * ③′ 子代半(写前一次观察):以根为根的活后台子代(running / parked;`probeSessionChildren` 那一次枚举,scope = 会话属主 `sessionChildrenScope`,
20
+ * 与删除门同读)⇒ 同码 409,体 `children`(名册 id)+ `retirePath`(显式结清口 —— 先结清、再换绑)
21
+ * ④ 前态 CAS(店内单事务:按门那一读的住客 ∧ 格上两属主列 ∧ rev 定址,`execution_rev` 恒变列 + `session_rebound` 审计行;输 = 409
22
+ * `device.rebind_rev_conflict` 体带 `currentRev` —— [ref] currentPending 哲学,壳一跳重定位;门后会话被删并同 id 重登记 ⇒ 住客不符 ⇒ 设备 404,
23
+ * 永不写到新住客的格上)
24
24
  * ⑤ 赢 = 200 `{ rootSessionId, deviceId, rev, workspaceCarryover: "none" }`。
25
25
  * 幂等形:toDeviceId ≡ 当前 ∧ expectedRev ≡ 当前 rev ⇒ 200 回显不加 rev(真幂等,不写审计)。
26
26
  * `workspaceCarryover: "none"` 是 **wire 冻结字面**(v2 稿 §5):换绑只改路由,不搬字节 —— 壳据
@@ -34,7 +34,7 @@
34
34
  * 才构成一个 device 域;缺 tenant claim 的凭据(纯 principal 头 / 静态 token 直连)**判不出域** ⇒
35
35
  * 非 operator 一律响亮 403(fail-closed + loud,[ref]:静默空列表是假健康)——基线稿对 enrollment 的
36
36
  * 同向裁定是「缺 tenant/global scope 明确拒注册」。enrollment/管理端点与**首绑写协议**都从
37
- * 这同一个函数取域([ref] 起它住在 `http/device-placement.ts` —— 提交腿也要用,放在路由域里是层序倒挂),
37
+ * 这同一个函数取域(它住在 `http/execution-context.ts` —— 提交腿的设备准入也要用,放在路由域里是层序倒挂),
38
38
  * 不得手抄第二份派生。
39
39
  *
40
40
  * ── 反枚举 404 的单一铸造点 ──────────────────────────────────────────────────────────────────────
@@ -81,12 +81,6 @@ export declare const DEVICES_ROUTES: readonly [{
81
81
  readonly pattern: RegExp;
82
82
  readonly label: "/v1/devices/sessions/:id/rebind";
83
83
  readonly methods: readonly ["POST"];
84
- }, {
85
- readonly id: "device-session";
86
- readonly pattern: RegExp;
87
- readonly label: "/v1/devices/sessions/:id";
88
- readonly credentialGated: readonly ["GET"];
89
- readonly methods: readonly ["GET"];
90
84
  }, {
91
85
  readonly prefix: "/v1/devices/";
92
86
  readonly credentialGated: readonly ["POST"];