@sema-agent/server 7.131.0 → 7.133.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 (509) hide show
  1. package/MIGRATION.md +575 -0
  2. package/README.md +12 -9
  3. package/README.zh-CN.md +9 -6
  4. package/USAGE.md +326 -98
  5. package/deploy/sema-up/README.md +19 -1
  6. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
  7. package/deploy/sema-up/chart/templates/server.yaml +5 -2
  8. package/deploy/sema-up/stack/compose.yaml +3 -0
  9. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  10. package/dist/adoption/plan.d.ts +3 -2
  11. package/dist/adoption/plan.js +7 -5
  12. package/dist/adoption/runner.js +3 -10
  13. package/dist/adoption/sql.d.ts +3 -2
  14. package/dist/adoption/sql.js +3 -5
  15. package/dist/adoption/wire.d.ts +4 -1
  16. package/dist/adoption/wire.js +1 -0
  17. package/dist/approval-ask-audit-store.d.ts +31 -13
  18. package/dist/approval-ask-audit-store.js +272 -74
  19. package/dist/approval-ask-machine.d.ts +15 -0
  20. package/dist/approval-ask-machine.js +6 -0
  21. package/dist/approval-ask-retention.d.ts +136 -0
  22. package/dist/approval-ask-retention.js +102 -0
  23. package/dist/approval-card.d.ts +82 -20
  24. package/dist/approval-card.js +46 -16
  25. package/dist/approval-deny-reasons.d.ts +1 -1
  26. package/dist/approval.d.ts +9 -0
  27. package/dist/approval.js +24 -3
  28. package/dist/audit.d.ts +33 -19
  29. package/dist/audit.js +12 -16
  30. package/dist/auth-bridge.d.ts +4 -0
  31. package/dist/auth-bridge.js +10 -11
  32. package/dist/bake-runner/main.js +3 -2
  33. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  34. package/dist/boot/deployment-mods.d.ts +31 -0
  35. package/dist/boot/deployment-mods.js +26 -0
  36. package/dist/boot/execution-env.d.ts +1 -1
  37. package/dist/boot/execution-env.js +27 -12
  38. package/dist/boot/execution-lane.js +11 -0
  39. package/dist/boot/leader.js +2 -0
  40. package/dist/boot/memory-boundary.d.ts +2 -2
  41. package/dist/boot/memory-boundary.js +2 -2
  42. package/dist/boot/memory-consolidation.d.ts +5 -4
  43. package/dist/boot/memory-consolidation.js +7 -6
  44. package/dist/boot/org-delivery.d.ts +28 -0
  45. package/dist/boot/org-delivery.js +20 -0
  46. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  47. package/dist/boot/read-deny-anchor-audit.js +36 -0
  48. package/dist/boot/reapers.d.ts +3 -0
  49. package/dist/boot/reapers.js +21 -1
  50. package/dist/boot/resolve-spec.d.ts +4 -5
  51. package/dist/boot/resolve-spec.js +74 -123
  52. package/dist/boot/runner-deps.d.ts +19 -8
  53. package/dist/boot/runner-deps.js +15 -2
  54. package/dist/boot/session-faces.d.ts +2 -7
  55. package/dist/boot/session-faces.js +4 -2
  56. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  57. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  58. package/dist/boot/stage-03-store-backend.js +2 -2
  59. package/dist/boot/stage-04-stores.d.ts +1 -1
  60. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  61. package/dist/boot/stage-05-execution-env.js +7 -10
  62. package/dist/boot/stage-06-runners.d.ts +4 -2
  63. package/dist/boot/stage-06-runners.js +11 -7
  64. package/dist/boot/stage-07-capability-layer.d.ts +4 -2
  65. package/dist/boot/stage-07-capability-layer.js +27 -6
  66. package/dist/boot/stage-08-reapers.d.ts +4 -2
  67. package/dist/boot/stage-08-reapers.js +2 -2
  68. package/dist/boot/stage-09-leader.d.ts +6 -9
  69. package/dist/boot/stage-10-http-server.d.ts +6 -9
  70. package/dist/boot/stage-10-http-server.js +7 -9
  71. package/dist/boot/stores.d.ts +1 -1
  72. package/dist/boot/stores.js +7 -8
  73. package/dist/boot/web-admin.d.ts +4 -3
  74. package/dist/boot/web-admin.js +186 -98
  75. package/dist/boot/workflow-orchestration.d.ts +2 -0
  76. package/dist/boot/workflow-orchestration.js +2 -1
  77. package/dist/brain.js +25 -25
  78. package/dist/breaker-key.d.ts +6 -0
  79. package/dist/breaker-key.js +29 -0
  80. package/dist/budget.js +5 -4
  81. package/dist/caller-text-echo.d.ts +23 -0
  82. package/dist/caller-text-echo.js +21 -0
  83. package/dist/capabilities/brand-identity.d.ts +26 -0
  84. package/dist/capabilities/brand-identity.js +27 -0
  85. package/dist/capabilities/center-plugins.d.ts +7 -0
  86. package/dist/capabilities/center-plugins.js +1 -1
  87. package/dist/capabilities/center-prompts.d.ts +32 -59
  88. package/dist/capabilities/center-prompts.js +18 -32
  89. package/dist/capabilities/composite-members.d.ts +1 -1
  90. package/dist/capabilities/composite-members.js +1 -1
  91. package/dist/capabilities/engine-capabilities.js +2 -1
  92. package/dist/capabilities/execution-lane.js +2 -2
  93. package/dist/capabilities/hands-lane.d.ts +30 -6
  94. package/dist/capabilities/hands-lane.js +11 -1
  95. package/dist/capabilities/mcp-probe.d.ts +48 -3
  96. package/dist/capabilities/mcp-probe.js +47 -135
  97. package/dist/capabilities/scenarios.d.ts +95 -45
  98. package/dist/capabilities/scenarios.js +53 -38
  99. package/dist/capabilities/skills.d.ts +19 -4
  100. package/dist/capabilities/skills.js +30 -10
  101. package/dist/capability-facts.d.ts +34 -4
  102. package/dist/capability-facts.js +28 -2
  103. package/dist/center-config-tier.d.ts +69 -0
  104. package/dist/center-config-tier.js +42 -0
  105. package/dist/checkpoint-identity.d.ts +16 -0
  106. package/dist/checkpoint-identity.js +19 -0
  107. package/dist/config-catalog.d.ts +17 -10
  108. package/dist/config-catalog.js +47 -32
  109. package/dist/config-d-migration.d.ts +3 -1
  110. package/dist/config-d-migration.js +2 -2
  111. package/dist/config-dir-trust.d.ts +114 -0
  112. package/dist/config-dir-trust.js +388 -0
  113. package/dist/config-invariants.d.ts +18 -5
  114. package/dist/config-invariants.js +35 -3
  115. package/dist/config-lkg.d.ts +9 -9
  116. package/dist/config-lkg.js +20 -4
  117. package/dist/config-provider.d.ts +9 -2
  118. package/dist/config-provider.js +18 -6
  119. package/dist/config-types.d.ts +75 -20
  120. package/dist/config-types.js +8 -0
  121. package/dist/config.d.ts +13 -0
  122. package/dist/config.js +137 -62
  123. package/dist/delegated-session-owner.d.ts +185 -0
  124. package/dist/delegated-session-owner.js +119 -0
  125. package/dist/device-protocol.d.ts +5 -3
  126. package/dist/device-protocol.js +1 -1
  127. package/dist/device-ws-hub.js +21 -2
  128. package/dist/device-ws-protocol.d.ts +70 -6
  129. package/dist/device-ws-protocol.js +50 -22
  130. package/dist/elicitation.js +3 -2
  131. package/dist/entry-problems.d.ts +34 -0
  132. package/dist/entry-problems.js +14 -0
  133. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  134. package/dist/env-name-allowlist-knobs.js +5 -4
  135. package/dist/exec-bounds.d.ts +15 -3
  136. package/dist/exec-bounds.js +4 -0
  137. package/dist/execution-lane-caps.d.ts +56 -10
  138. package/dist/execution-lane-caps.js +50 -0
  139. package/dist/field-conflict.d.ts +43 -0
  140. package/dist/field-conflict.js +20 -0
  141. package/dist/fleet/fleet-bus.d.ts +80 -6
  142. package/dist/fleet/fleet-bus.js +41 -9
  143. package/dist/hooks/classic-stations.d.ts +91 -0
  144. package/dist/hooks/classic-stations.js +27 -0
  145. package/dist/hooks/command-spawn.d.ts +57 -0
  146. package/dist/hooks/command-spawn.js +139 -0
  147. package/dist/hooks/hook-llm.d.ts +8 -5
  148. package/dist/hooks/hook-llm.js +79 -36
  149. package/dist/hooks/hook-runner.d.ts +349 -95
  150. package/dist/hooks/hook-runner.js +855 -568
  151. package/dist/hooks/hook-source.d.ts +44 -0
  152. package/dist/hooks/hook-source.js +40 -0
  153. package/dist/http/active-run-conflict.d.ts +5 -13
  154. package/dist/http/active-run-conflict.js +17 -27
  155. package/dist/http/admission.d.ts +8 -2
  156. package/dist/http/admission.js +188 -101
  157. package/dist/http/approval-sweeps.js +99 -21
  158. package/dist/http/caller-trust.d.ts +77 -0
  159. package/dist/http/caller-trust.js +143 -0
  160. package/dist/http/checkpoint-ctx.d.ts +41 -24
  161. package/dist/http/checkpoint-ctx.js +23 -16
  162. package/dist/http/claimed-run.d.ts +14 -0
  163. package/dist/http/claimed-run.js +15 -0
  164. package/dist/http/device-placement.d.ts +2 -4
  165. package/dist/http/device-placement.js +7 -11
  166. package/dist/http/dispatch.d.ts +0 -4
  167. package/dist/http/dispatch.js +15 -13
  168. package/dist/http/durable-run-leg.d.ts +6 -33
  169. package/dist/http/durable-run-leg.js +36 -10
  170. package/dist/http/health-self-report.d.ts +7 -5
  171. package/dist/http/health-self-report.js +4 -18
  172. package/dist/http/owner-access.d.ts +56 -0
  173. package/dist/http/owner-access.js +42 -0
  174. package/dist/http/principal-gate.d.ts +70 -28
  175. package/dist/http/principal-gate.js +61 -15
  176. package/dist/http/readiness.d.ts +11 -5
  177. package/dist/http/readiness.js +10 -3
  178. package/dist/http/refusal-body.d.ts +21 -0
  179. package/dist/http/refusal-body.js +4 -0
  180. package/dist/http/request-guards.d.ts +43 -23
  181. package/dist/http/request-guards.js +53 -17
  182. package/dist/http/resume-ctx-read.d.ts +2 -5
  183. package/dist/http/resume-ctx-read.js +7 -4
  184. package/dist/http/resume-legs.d.ts +28 -4
  185. package/dist/http/resume-legs.js +169 -91
  186. package/dist/http/retired-service-deps.d.ts +43 -0
  187. package/dist/http/retired-service-deps.js +18 -0
  188. package/dist/http/route-ctx.d.ts +44 -28
  189. package/dist/http/route-ctx.js +4 -0
  190. package/dist/http/route-table.js +2 -0
  191. package/dist/http/routes/a2a-serve.js +3 -4
  192. package/dist/http/routes/admin-config-refresh.js +1 -1
  193. package/dist/http/routes/admin-drain.js +1 -1
  194. package/dist/http/routes/admin-memory-optout.js +5 -1
  195. package/dist/http/routes/adoption.js +2 -9
  196. package/dist/http/routes/approvals-assistant.js +49 -48
  197. package/dist/http/routes/background.js +3 -2
  198. package/dist/http/routes/capabilities.js +21 -5
  199. package/dist/http/routes/config-catalog.d.ts +3 -3
  200. package/dist/http/routes/config-catalog.js +1 -1
  201. package/dist/http/routes/devices.js +3 -17
  202. package/dist/http/routes/diagnostics.js +6 -2
  203. package/dist/http/routes/images.js +6 -7
  204. package/dist/http/routes/leader.js +1 -8
  205. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  206. package/dist/http/routes/mcp-reconnect.js +3 -2
  207. package/dist/http/routes/memory-bundle.js +1 -1
  208. package/dist/http/routes/memory-compliance.js +1 -1
  209. package/dist/http/routes/memory-consolidation.js +1 -1
  210. package/dist/http/routes/memory-origin.js +1 -1
  211. package/dist/http/routes/memory-policy.d.ts +1 -1
  212. package/dist/http/routes/memory-policy.js +3 -2
  213. package/dist/http/routes/notify-wake.js +8 -21
  214. package/dist/http/routes/observability.js +2 -2
  215. package/dist/http/routes/principal-inbox.d.ts +39 -0
  216. package/dist/http/routes/principal-inbox.js +76 -0
  217. package/dist/http/routes/retention-ops.js +2 -9
  218. package/dist/http/routes/rules.js +5 -18
  219. package/dist/http/routes/runs.d.ts +5 -0
  220. package/dist/http/routes/runs.js +34 -107
  221. package/dist/http/routes/session-sync.js +15 -26
  222. package/dist/http/routes/sessions.js +30 -40
  223. package/dist/http/routes/shared-memory.js +2 -13
  224. package/dist/http/routes/side-query.js +8 -7
  225. package/dist/http/routes/tasks.js +67 -21
  226. package/dist/http/routes/workflows.d.ts +351 -0
  227. package/dist/http/routes/workflows.js +153 -119
  228. package/dist/http/send.d.ts +33 -10
  229. package/dist/http/send.js +21 -4
  230. package/dist/http/server.d.ts +27 -18
  231. package/dist/http/server.js +34 -6
  232. package/dist/http/stored-body-replay.js +17 -4
  233. package/dist/http/submit-ctx.d.ts +25 -0
  234. package/dist/http/submit-ctx.js +40 -0
  235. package/dist/http/submit-replay.d.ts +4 -3
  236. package/dist/http/submit-replay.js +5 -4
  237. package/dist/http/wire-types.d.ts +11 -8
  238. package/dist/index.d.ts +2 -2
  239. package/dist/index.js +1 -2
  240. package/dist/lane-home-probe.d.ts +112 -0
  241. package/dist/lane-home-probe.js +101 -0
  242. package/dist/leader/endpoint.d.ts +4 -1
  243. package/dist/leader/endpoint.js +11 -10
  244. package/dist/limit-refusal.d.ts +59 -0
  245. package/dist/limit-refusal.js +15 -0
  246. package/dist/main.js +1 -1
  247. package/dist/mcp-assertion-header.d.ts +32 -19
  248. package/dist/mcp-assertion-header.js +10 -9
  249. package/dist/mcp-tool-faces.d.ts +5 -0
  250. package/dist/mcp-tool-faces.js +6 -4
  251. package/dist/memory-project-layer.d.ts +37 -0
  252. package/dist/memory-project-layer.js +46 -0
  253. package/dist/memory-scope.d.ts +24 -17
  254. package/dist/memory-scope.js +24 -8
  255. package/dist/memory-sync-client.d.ts +27 -13
  256. package/dist/memory-sync-client.js +107 -18
  257. package/dist/model-entry-refusal.d.ts +9 -1
  258. package/dist/model-entry-refusal.js +6 -1
  259. package/dist/model-plane-face.d.ts +51 -0
  260. package/dist/model-plane-face.js +24 -0
  261. package/dist/model-provider.d.ts +44 -12
  262. package/dist/model-provider.js +21 -2
  263. package/dist/model-route-endpoint.d.ts +9 -12
  264. package/dist/model-route-endpoint.js +8 -11
  265. package/dist/model-route-env.d.ts +22 -0
  266. package/dist/model-route-env.js +2 -0
  267. package/dist/model-route-gap.d.ts +60 -30
  268. package/dist/model-route-gap.js +21 -15
  269. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  270. package/dist/observability/corrupt-read-seat.js +9 -0
  271. package/dist/observability/cost-taxonomy.d.ts +7 -7
  272. package/dist/observability/err-text.d.ts +6 -0
  273. package/dist/observability/err-text.js +18 -4
  274. package/dist/observability/fail-open.d.ts +76 -14
  275. package/dist/observability/fail-open.js +76 -14
  276. package/dist/observability/metrics.js +4 -1
  277. package/dist/observability/prompt-manifest.d.ts +33 -82
  278. package/dist/observability/prompt-manifest.js +122 -17
  279. package/dist/observability/run-terminal.js +14 -6
  280. package/dist/observability/secret-env-scrub.d.ts +13 -1
  281. package/dist/observability/secret-env-scrub.js +13 -1
  282. package/dist/observability/tool-trace.d.ts +16 -21
  283. package/dist/observability/tool-trace.js +58 -49
  284. package/dist/operator-ask.d.ts +10 -0
  285. package/dist/operator-ask.js +3 -0
  286. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  287. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  288. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  289. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  290. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  291. package/dist/orchestration/workflow-notify-journal.js +27 -34
  292. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  293. package/dist/orchestration/workflow-park-decision.js +118 -0
  294. package/dist/org-delivery-gate.d.ts +81 -0
  295. package/dist/org-delivery-gate.js +59 -0
  296. package/dist/panel-tool-names.d.ts +23 -0
  297. package/dist/panel-tool-names.js +8 -0
  298. package/dist/parked-decide.d.ts +39 -6
  299. package/dist/parked-decide.js +98 -51
  300. package/dist/parked-revive-surface.d.ts +9 -2
  301. package/dist/parked-revive-surface.js +11 -8
  302. package/dist/per-task-image.d.ts +7 -1
  303. package/dist/per-task-image.js +3 -3
  304. package/dist/permission-rule-list.d.ts +1 -1
  305. package/dist/permission-rule-list.js +2 -2
  306. package/dist/plain-string-record.d.ts +25 -0
  307. package/dist/plain-string-record.js +32 -0
  308. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  309. package/dist/plugins/approval-ask-store-file.js +21 -0
  310. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  311. package/dist/plugins/approval-ask-store-memory.js +34 -2
  312. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  313. package/dist/plugins/approval-ask-store-sql.js +37 -1
  314. package/dist/plugins/blob-backend.d.ts +83 -15
  315. package/dist/plugins/blob-backend.js +68 -15
  316. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  317. package/dist/plugins/breaker-state-sql.js +4 -4
  318. package/dist/plugins/caching-session-store.d.ts +3 -0
  319. package/dist/plugins/caching-session-store.js +3 -0
  320. package/dist/plugins/checkpoint-store-sql.d.ts +28 -4
  321. package/dist/plugins/checkpoint-store-sql.js +59 -28
  322. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  323. package/dist/plugins/file-history-store-sql.js +3 -1
  324. package/dist/plugins/file-run-store.d.ts +5 -13
  325. package/dist/plugins/file-run-store.js +9 -0
  326. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  327. package/dist/plugins/fork-routing-session-store.js +12 -1
  328. package/dist/plugins/host-platform.d.ts +2 -0
  329. package/dist/plugins/host-platform.js +2 -1
  330. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  331. package/dist/plugins/k8s-bg-scripts.js +22 -0
  332. package/dist/plugins/local-checkpoint-store.d.ts +2 -0
  333. package/dist/plugins/local-checkpoint-store.js +11 -3
  334. package/dist/plugins/local-session-store.d.ts +1 -1
  335. package/dist/plugins/local-session-store.js +1 -1
  336. package/dist/plugins/local-session-window.d.ts +39 -0
  337. package/dist/plugins/local-session-window.js +54 -0
  338. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  339. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  340. package/dist/plugins/mailbox-store-sql.js +77 -52
  341. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  342. package/dist/plugins/memory-run-store.d.ts +5 -13
  343. package/dist/plugins/memory-run-store.js +8 -0
  344. package/dist/plugins/one-time-migrations.d.ts +245 -93
  345. package/dist/plugins/one-time-migrations.js +668 -207
  346. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  347. package/dist/plugins/org-delivery-quota-store.js +228 -0
  348. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  349. package/dist/plugins/permission-rule-store-sql.js +4 -18
  350. package/dist/plugins/pg-pool.js +39 -7
  351. package/dist/plugins/pg-session-storage.d.ts +20 -8
  352. package/dist/plugins/pg-session-storage.js +34 -21
  353. package/dist/plugins/remote-env-adb.d.ts +12 -12
  354. package/dist/plugins/remote-env-adb.js +21 -6
  355. package/dist/plugins/remote-env-device.js +3 -0
  356. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  357. package/dist/plugins/remote-env-e2b.js +21 -9
  358. package/dist/plugins/remote-env-host.d.ts +9 -9
  359. package/dist/plugins/remote-env-host.js +10 -32
  360. package/dist/plugins/remote-env-k8s.d.ts +103 -38
  361. package/dist/plugins/remote-env-k8s.js +288 -95
  362. package/dist/plugins/remote-env-local-docker.d.ts +27 -13
  363. package/dist/plugins/remote-env-local-docker.js +82 -11
  364. package/dist/plugins/remote-env-ssh.d.ts +21 -12
  365. package/dist/plugins/remote-env-ssh.js +117 -5
  366. package/dist/plugins/remote-shell.d.ts +0 -1
  367. package/dist/plugins/remote-shell.js +1 -2
  368. package/dist/plugins/retention-store-sql.js +6 -2
  369. package/dist/plugins/run-store-sql.d.ts +16 -14
  370. package/dist/plugins/run-store-sql.js +22 -2
  371. package/dist/plugins/schema-consistency.d.ts +54 -0
  372. package/dist/plugins/schema-consistency.js +52 -0
  373. package/dist/plugins/schema-indexes.js +2 -0
  374. package/dist/plugins/send-user-file.js +2 -2
  375. package/dist/plugins/session-epoch-anchor.d.ts +39 -0
  376. package/dist/plugins/session-epoch-anchor.js +22 -0
  377. package/dist/plugins/session-store.d.ts +18 -8
  378. package/dist/plugins/session-store.js +15 -4
  379. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  380. package/dist/plugins/shared-memory-store-sql.js +5 -16
  381. package/dist/plugins/sql-driver.d.ts +205 -1
  382. package/dist/plugins/sql-driver.js +325 -13
  383. package/dist/plugins/sql-errors.d.ts +64 -0
  384. package/dist/plugins/sql-errors.js +57 -0
  385. package/dist/plugins/store-backend.d.ts +36 -1
  386. package/dist/plugins/store-backend.js +42 -15
  387. package/dist/plugins/store-contracts.d.ts +42 -0
  388. package/dist/plugins/store-contracts.js +1 -0
  389. package/dist/plugins/tidb-pool.d.ts +6 -1
  390. package/dist/plugins/tidb-pool.js +44 -10
  391. package/dist/plugins/tidb-session-storage.d.ts +8 -11
  392. package/dist/plugins/tidb-session-storage.js +6 -16
  393. package/dist/plugins/tidb-session-store.d.ts +24 -4
  394. package/dist/plugins/tidb-session-store.js +29 -5
  395. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  396. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  397. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  398. package/dist/plugins/workflow-run-store-sql.js +50 -9
  399. package/dist/posture-source.d.ts +18 -10
  400. package/dist/posture-source.js +4 -1
  401. package/dist/project-identity.d.ts +45 -6
  402. package/dist/project-identity.js +162 -8
  403. package/dist/project-layer-key.d.ts +28 -0
  404. package/dist/project-layer-key.js +29 -0
  405. package/dist/prompts-domain-validate.d.ts +17 -61
  406. package/dist/prompts-domain-validate.js +9 -119
  407. package/dist/question.js +3 -2
  408. package/dist/read-deny-entry.d.ts +25 -0
  409. package/dist/read-deny-entry.js +12 -0
  410. package/dist/read-face-posture.d.ts +37 -11
  411. package/dist/read-face-posture.js +32 -3
  412. package/dist/request-key-closure.d.ts +9 -2
  413. package/dist/request-key-closure.js +11 -5
  414. package/dist/request-value-closure.d.ts +5 -0
  415. package/dist/request-value-closure.js +11 -3
  416. package/dist/rule-entry-list.d.ts +38 -11
  417. package/dist/rule-entry-list.js +106 -30
  418. package/dist/rules-consent.js +3 -4
  419. package/dist/run-acceptance.d.ts +19 -3
  420. package/dist/run-acceptance.js +13 -1
  421. package/dist/run-local.js +54 -24
  422. package/dist/runs.d.ts +15 -8
  423. package/dist/runs.js +35 -1
  424. package/dist/runtime-caps-resolver.d.ts +16 -6
  425. package/dist/runtime-caps-resolver.js +18 -5
  426. package/dist/sandbox-pkg-source.d.ts +19 -1
  427. package/dist/sandbox-pkg-source.js +29 -7
  428. package/dist/security.d.ts +87 -9
  429. package/dist/security.js +52 -9
  430. package/dist/server-secret-env.d.ts +44 -18
  431. package/dist/server-secret-env.js +49 -8
  432. package/dist/session-error-wire.d.ts +19 -2
  433. package/dist/session-error-wire.js +5 -1
  434. package/dist/session-incarnation.d.ts +15 -5
  435. package/dist/session-incarnation.js +16 -9
  436. package/dist/session-titled-frame.d.ts +33 -0
  437. package/dist/session-titled-frame.js +4 -0
  438. package/dist/session-titler.d.ts +9 -1
  439. package/dist/session-titler.js +27 -6
  440. package/dist/spec-fields.d.ts +28 -16
  441. package/dist/spec-fields.js +66 -54
  442. package/dist/sql-engine-posture.d.ts +31 -4
  443. package/dist/sql-engine-posture.js +36 -1
  444. package/dist/stored-request-tool-axes-m19.d.ts +19 -4
  445. package/dist/stored-request-tool-axes-m19.js +30 -2
  446. package/dist/task-a2a.d.ts +9 -1
  447. package/dist/task-a2a.js +73 -47
  448. package/dist/task-cwd.d.ts +1 -1
  449. package/dist/task-mcp.d.ts +46 -58
  450. package/dist/task-mcp.js +82 -107
  451. package/dist/task-settings.d.ts +95 -108
  452. package/dist/task-settings.js +77 -98
  453. package/dist/text-bidi.d.ts +1 -48
  454. package/dist/text-bidi.js +3 -5
  455. package/dist/tighten-only-seats.d.ts +150 -0
  456. package/dist/tighten-only-seats.js +196 -0
  457. package/dist/tool-approval.d.ts +304 -88
  458. package/dist/tool-approval.js +623 -239
  459. package/dist/trace/core-keyset-guard.d.ts +122 -20
  460. package/dist/trace/engine-notice-wire.d.ts +11 -3
  461. package/dist/trace/engine-notice-wire.js +8 -1
  462. package/dist/trace/exit-seat.d.ts +28 -0
  463. package/dist/trace/exit-seat.js +76 -0
  464. package/dist/trace/ledger-events.d.ts +6 -3
  465. package/dist/trace/ledger-sink.js +4 -1
  466. package/dist/trace/model-segment.js +1 -1
  467. package/dist/trace/project.d.ts +98 -17
  468. package/dist/trace/project.js +98 -50
  469. package/dist/trace/projection-table.d.ts +100 -0
  470. package/dist/trace/projection-table.js +130 -0
  471. package/dist/trace/redact.d.ts +54 -0
  472. package/dist/trace/redact.js +778 -152
  473. package/dist/trace/self-minted-wire.d.ts +345 -1
  474. package/dist/trace/self-minted-wire.js +117 -1
  475. package/dist/trace/wire-projection-faces.d.ts +377 -3
  476. package/dist/trace/wire-projection-faces.js +164 -1
  477. package/dist/url-refusal.js +2 -0
  478. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  479. package/dist/web-admin-client/apply-effective.js +56 -5
  480. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  481. package/dist/web-admin-client/candidate-gate.js +3 -3
  482. package/dist/web-admin-client/facade.d.ts +1 -1
  483. package/dist/web-admin-client/facade.js +1 -1
  484. package/dist/web-admin-client/http-client.js +0 -6
  485. package/dist/web-admin-client/read-face.js +5 -9
  486. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  487. package/dist/web-admin-client/read-warnings.js +10 -0
  488. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  489. package/dist/web-admin-client/skills-mcp.js +11 -9
  490. package/dist/web-admin-client/types.d.ts +2 -3
  491. package/dist/workspace-cache.d.ts +35 -0
  492. package/dist/workspace-cache.js +36 -0
  493. package/dist/workspace-space.d.ts +3 -2
  494. package/dist/workspace-space.js +2 -0
  495. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +65 -10
  496. package/package.json +8 -4
  497. package/scripts/drain-check-local.mjs +34 -1
  498. package/scripts/drain-check.mysql.sql +30 -2
  499. package/scripts/drain-check.pg.sql +29 -2
  500. package/dist/capabilities/memory-notice.d.ts +0 -138
  501. package/dist/capabilities/memory-notice.js +0 -64
  502. package/dist/capabilities/prompt.d.ts +0 -17
  503. package/dist/capabilities/prompt.js +0 -6
  504. package/dist/mcp-content-origin.d.ts +0 -34
  505. package/dist/mcp-content-origin.js +0 -10
  506. package/dist/tool-axis-words.d.ts +0 -27
  507. package/dist/tool-axis-words.js +0 -9
  508. package/dist/wire-name-bound.d.ts +0 -14
  509. package/dist/wire-name-bound.js +0 -13
@@ -1,7 +1,9 @@
1
1
  import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
2
2
  import { createHash } from "node:crypto";
3
3
  import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY, PAUSE_REGISTRY } from "@sema-agent/core";
4
- import { decideParkedAgent, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, planWorkflowParkedDecide } from "../parked-decide.js";
4
+ import { decideParkedAgent, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, heldWorkflowParkDecisions, planWorkflowParkedDecide } from "../parked-decide.js";
5
+ import { parkDecisionFingerprint } from "../orchestration/workflow-park-decision.js";
6
+ 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";
5
7
  const sessionFaceUnavailable = async (sessionId) => {
6
8
  throw new Error(`parked decide cannot verify which occupant session ${sessionId} belongs to: this deployment's session store has no sessionFaceOf read (every built-in store has one) — refusing rather than resuming unverified`);
7
9
  };
@@ -9,6 +11,7 @@ import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../se
9
11
  import { parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
10
12
  import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
11
13
  import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../run-cancel-context.js";
14
+ import { isTerminalRunStatus } from "../plugins/store-contracts.js";
12
15
  import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
13
16
  import { recordResumeResourceWindow } from "../resource-window.js";
14
17
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
@@ -22,7 +25,7 @@ import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
22
25
  import { withPrincipal } from "../observability/principal-context.js";
23
26
  import { withModelReachScope } from "../model-reach-scope.js";
24
27
  import { createInstructionSelectionLeg } from "../instruction-selection.js";
25
- import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
28
+ import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
26
29
  import { redactSecrets } from "../trace/redact.js";
27
30
  import { buildRunObjectivePreview } from "../user-authored-text.js";
28
31
  import { createForwardAppendFailedNote, createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
@@ -32,7 +35,7 @@ import { errorText } from "../observability/err-text.js";
32
35
  import { recordFailOpen } from "../observability/fail-open.js";
33
36
  import { verifyRoundsFromBody } from "./verify-rounds.js";
34
37
  import { planReviewDecisionExitsPlan, storedBodyPermissionMode } from "../task-settings.js";
35
- import { acceptedResumingReceipt, buildHostLegOutcome } from "./route-ctx.js";
38
+ import { acceptedResumingReceipt, acceptedTaskIdOf, buildHostLegOutcome } from "./route-ctx.js";
36
39
  import { mintAndDriveDurableRun } from "./durable-run-leg.js";
37
40
  import { createSessionsLocal } from "./routes/sessions.js";
38
41
  import { createSessionSyncLocal } from "./routes/session-sync.js";
@@ -45,10 +48,12 @@ import { commitSessionInheritEnv } from "./session-inherit-env.js";
45
48
  import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
46
49
  import { deploymentListAsks } from "../approval.js";
47
50
  import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
48
- import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
51
+ import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
52
+ import { refusalBody } from "./refusal-body.js";
49
53
  import { storedBodyReplayRefusal } from "./stored-body-replay.js";
50
- import { httpErrorCode } from "./send.js";
54
+ import { httpErrorRefusal } from "./send.js";
51
55
  import { createRunAcceptance } from "../run-acceptance.js";
56
+ import { fieldConflictFacts } from "../field-conflict.js";
52
57
  function legMayHaveReopenedCheckpoint(r) {
53
58
  return r.terminal.kind === "failed" && nestedPauseOf(r.terminal) === undefined;
54
59
  }
@@ -77,6 +82,15 @@ const WORKER_SWAP_REDEEMABLE = {
77
82
  installation_record_absent: true,
78
83
  installation_record_misaligned: false,
79
84
  installation_record_invalid: false,
85
+ unknown_name: true,
86
+ unsupported_pattern: true,
87
+ not_a_list: false,
88
+ not_a_string: false,
89
+ invalid_record: false,
90
+ renamed_mount: true,
91
+ registry_occupied: true,
92
+ unknown_model_ref: true,
93
+ not_a_model_ref: true,
80
94
  };
81
95
  function checkpointRowRedeemableElsewhere(reason) {
82
96
  return reason !== undefined && WORKER_SWAP_REDEEMABLE[reason] === true;
@@ -136,6 +150,7 @@ const CHECKPOINT_ERROR_FIELD_ROLE = {
136
150
  hostDecision: "outcome_cell",
137
151
  approver: "outcome_cell",
138
152
  taskConfig: "config",
153
+ deps: "config",
139
154
  inheritedGate: "config",
140
155
  };
141
156
  const FIELD_ROLE_CAUSE = {
@@ -159,7 +174,28 @@ function decisionRefusalCauseOf(e, gate) {
159
174
  return role === undefined ? undefined : FIELD_ROLE_CAUSE[role];
160
175
  }
161
176
  function notResumableAnswer() {
162
- return { status: 409, body: { error: "run is not in a resumable (suspended) state (already resumed, decided, or expired)", errorCode: "conflict.not_resumable" } };
177
+ return { status: 409, body: refusalBody({ errorCode: "conflict.not_resumable", message: "run is not in a resumable (suspended) state (already resumed, decided, or expired)" }) };
178
+ }
179
+ export function checkpointRefusalAnswer(e, at) {
180
+ const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, at.gate) : undefined;
181
+ const detail = e.detail;
182
+ return resumeRefusalWithCard({
183
+ status: e.code === "checkpoint.not_found" ? 404 : 409,
184
+ body: refusalBody({
185
+ message: e.message,
186
+ errorCode: e.code === "checkpoint.invalid_outcome" ? (refusalCause !== undefined ? WIRE_CODE_OF_DECISION_REFUSAL[refusalCause] : UNCLASSIFIED_INVALID_OUTCOME_CODE) : e.code,
187
+ facts: {
188
+ ...(at.taskId !== undefined ? { taskId: at.taskId } : {}),
189
+ ...(detail?.code !== undefined ? { blockedBy: detail.code } : {}),
190
+ ...(detail?.field ? { field: detail.field } : {}),
191
+ ...(detail?.reason !== undefined ? { reason: detail.reason } : {}),
192
+ ...((e.code === "resume.usage_window_exhausted" || e.code === "resume.preflight_rejected") && typeof detail?.retryAfterMs === "number" && Number.isFinite(detail.retryAfterMs) && detail.retryAfterMs > 0
193
+ ? { retryAfterSec: Math.max(1, Math.ceil(detail.retryAfterMs / 1000)) }
194
+ : {}),
195
+ ...at.versionKeys,
196
+ },
197
+ }),
198
+ }, at.retriable);
163
199
  }
164
200
  export function createResumeLegs(ctx) {
165
201
  const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, isFleetWide, runOwnerOk, sessionOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
@@ -180,7 +216,7 @@ export function createResumeLegs(ctx) {
180
216
  }
181
217
  if (!token && !attributable && !seenUnreadable) {
182
218
  deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true });
183
- return { status: 404, body: { error: "approval not found", errorCode: "not_found.approval" } };
219
+ return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
184
220
  }
185
221
  let currentPending;
186
222
  if (token && decider !== undefined) {
@@ -193,7 +229,7 @@ export function createResumeLegs(ctx) {
193
229
  const scopeOk = cur !== null && (decider.explicitOperator || curOwner === undefined || curOwner === decider.principal);
194
230
  if (cur && cur.sessionId === sessionId && !scopeOk) {
195
231
  deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true, foreignCurrentSuppressed: true });
196
- return { status: 404, body: { error: "approval not found", errorCode: "not_found.approval" } };
232
+ return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
197
233
  }
198
234
  if (cur && cur.status === "pending" && cur.sessionId === sessionId && scopeOk && curPa?.kind === "tool_approval" && curPa.toolCallId) {
199
235
  currentPending = {
@@ -205,22 +241,24 @@ export function createResumeLegs(ctx) {
205
241
  }
206
242
  return {
207
243
  status: 409,
208
- body: {
209
- error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
244
+ body: refusalBody({
210
245
  errorCode: "approval_stale",
211
- ...(attributable ? { terminal: "resolved" } : {}),
212
- ...(currentPending ? { currentPending } : {}),
213
- },
246
+ message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
247
+ facts: {
248
+ ...(attributable ? { terminal: "resolved" } : {}),
249
+ ...(currentPending ? { currentPending } : {}),
250
+ },
251
+ }),
214
252
  };
215
253
  }
216
254
  if (!token) {
217
255
  deps.logger?.info?.("approval_decide_no_pending", { sessionId });
218
- return { status: 404, body: { error: "approval not found", errorCode: "not_found.approval" } };
256
+ return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
219
257
  }
220
258
  const cpPromise = cs.get(token);
221
259
  const ctxPromise = cs.getCtx(sessionId);
222
260
  void ctxPromise.catch(() => { });
223
- const noApproval = { status: 404, body: { error: "approval not found", errorCode: "not_found.approval" } };
261
+ const noApproval = { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
224
262
  let cp;
225
263
  try {
226
264
  cp = await cpPromise;
@@ -231,18 +269,18 @@ export function createResumeLegs(ctx) {
231
269
  deps.logger?.warn?.("approval_decide_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
232
270
  return noApproval;
233
271
  }
234
- return { status: 409, body: { error: e instanceof Error ? e.message : String(e), errorCode: "conflict.checkpoint_unreadable" } };
272
+ return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
235
273
  }
236
274
  if (!cp) {
237
275
  if (!(await mayDiscloseRowError(cs, token, decider)))
238
276
  return noApproval;
239
- return { status: 404, body: { error: "checkpoint not found", errorCode: "not_found.checkpoint" } };
277
+ return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
240
278
  }
241
279
  if (decider !== undefined && !decider.explicitOperator) {
242
280
  const cpOwner = decodeCheckpointScope(cp.scope);
243
281
  if (cpOwner !== undefined && cpOwner !== decider.principal) {
244
282
  deps.logger?.warn?.("approval_decide_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
245
- return { status: 404, body: { error: "approval not found", errorCode: "not_found.approval" } };
283
+ return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
246
284
  }
247
285
  }
248
286
  const decideGateKind = cp.gate?.kind;
@@ -255,21 +293,25 @@ export function createResumeLegs(ctx) {
255
293
  : null;
256
294
  return {
257
295
  status: 409,
258
- body: {
259
- error: `task is suspended on a '${decideGateKind ?? "unknown"}' gate, not a tool approval — a plan_review gate is resolved via POST /v1/assistant/tasks/:id/plan_review, a resource_limit suspension via POST /v1/assistant/tasks/:id/resume`,
296
+ body: refusalBody({
260
297
  errorCode: "gate_not_tool_approval",
261
- ...(decideGateKind ? { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } } : {}),
262
- },
298
+ message: `task is suspended on a '${decideGateKind ?? "unknown"}' gate, not a tool approval — a plan_review gate is resolved via POST /v1/assistant/tasks/:id/plan_review, a resource_limit suspension via POST /v1/assistant/tasks/:id/resume`,
299
+ facts: {
300
+ ...(decideGateKind ? { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } } : {}),
301
+ },
302
+ }),
263
303
  };
264
304
  }
265
305
  if (req !== undefined && !binding?.checkpointToken && !binding?.boundCallId) {
266
306
  return {
267
307
  status: 400,
268
- body: {
269
- error: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
308
+ body: refusalBody({
270
309
  errorCode: "decide_requires_binding",
271
- bindingEnforced: true,
272
- },
310
+ message: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
311
+ facts: {
312
+ bindingEnforced: true,
313
+ },
314
+ }),
273
315
  };
274
316
  }
275
317
  const s57RowCallId = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
@@ -284,11 +326,13 @@ export function createResumeLegs(ctx) {
284
326
  }
285
327
  return {
286
328
  status: 409,
287
- body: {
288
- error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
329
+ body: refusalBody({
289
330
  errorCode: "approval_stale",
290
- ...(s57CurrentPending ? { currentPending: s57CurrentPending } : {}),
291
- },
331
+ message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
332
+ facts: {
333
+ ...(s57CurrentPending ? { currentPending: s57CurrentPending } : {}),
334
+ },
335
+ }),
292
336
  };
293
337
  }
294
338
  const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
@@ -375,14 +419,48 @@ export function createResumeLegs(ctx) {
375
419
  const plan = planWorkflowParkedDecide(origin, parkedReq);
376
420
  if (!plan.ok)
377
421
  return plan.deny;
378
- const parkedResume = { workflowParkedResume: [{ runId: plan.runId, token, outcome: plan.outcome }] };
422
+ const decisions = deps.workflowParkDecisions;
423
+ const runStore = deps.runStore;
424
+ if (decisions === undefined || runStore === undefined) {
425
+ return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
426
+ }
427
+ const fingerprint = parkDecisionFingerprint(plan.outcome);
428
+ const liveReceiptOf = async (held) => {
429
+ if (held === null || held.fingerprint !== fingerprint)
430
+ return undefined;
431
+ const run = await runStore.getRun(held.deliveredTaskId);
432
+ return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId) : undefined;
433
+ };
434
+ const replay = await liveReceiptOf(await decisions.get(token));
435
+ if (replay !== undefined)
436
+ return replay;
437
+ let recordFailure;
438
+ const delivery = {
439
+ carried: [{ runId: plan.runId, token, outcome: plan.outcome }],
440
+ commitBeforeLaunch: async (deliveredTaskId) => {
441
+ try {
442
+ const recorded = await decisions.record({ token, runId: plan.runId, hostSessionId: plan.hostSessionId, outcome: plan.outcome, fingerprint, deliveredTaskId, acceptedAtMs: Date.now() });
443
+ if (!recorded)
444
+ throw new Error(DELIVERY_NOT_RECORDED);
445
+ }
446
+ catch (err) {
447
+ recordFailure = { err };
448
+ throw err;
449
+ }
450
+ },
451
+ };
379
452
  const hostToken = await cs
380
453
  .findPendingTokenBySession(plan.hostSessionId, undefined, { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" })
381
454
  .catch(() => undefined);
382
455
  const admittedPayer = decodeCheckpointScope(cp.scope);
383
- return hostToken
384
- ? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly, parkedResume.workflowParkedResume, admittedPayer)
385
- : await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req, parkedResume, plan.runId);
456
+ const out = hostToken
457
+ ? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly, delivery, admittedPayer)
458
+ : await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req, delivery, plan.runId);
459
+ if (recordFailure !== undefined)
460
+ throw recordFailure.err;
461
+ if (acceptedTaskIdOf(out) === undefined)
462
+ return (await liveReceiptOf(await decisions.get(token))) ?? out;
463
+ return out;
386
464
  }
387
465
  }
388
466
  const ctxRead0 = await readResumeCtx(ctxPromise);
@@ -390,7 +468,7 @@ export function createResumeLegs(ctx) {
390
468
  return resumeCtxCorruptAnswer(ctxRead0.where);
391
469
  const ctx = ctxRead0.kind === "ok" ? ctxRead0.ctx : null;
392
470
  if (!ctx)
393
- return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
471
+ return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
394
472
  const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
395
473
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
396
474
  if (!resolved.ok)
@@ -398,10 +476,10 @@ export function createResumeLegs(ctx) {
398
476
  const { spec, engineNotices } = resolved;
399
477
  const pendingTool = cp.pendingAction.toolName;
400
478
  if (decision === "approve" && pendingTool === ASK_USER_QUESTION_TOOL_NAME && !answer) {
401
- return { status: 400, body: { error: "pending action is AskUserQuestion — approve must carry body.answer ({ answers: [{ header, selected: string[], note? }] })", errorCode: "request.field_conflict" } };
479
+ 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"]) } }) };
402
480
  }
403
481
  if (answer && (decision !== "approve" || pendingTool !== ASK_USER_QUESTION_TOOL_NAME)) {
404
- return { status: 400, body: { error: `body.answer is only valid when approving a pending AskUserQuestion (decision: "${decision}", pending: "${pendingTool ?? "unknown"}")`, errorCode: "request.field_conflict" } };
482
+ 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"]) } }) };
405
483
  }
406
484
  const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
407
485
  const pa = cp.pendingAction;
@@ -422,7 +500,7 @@ export function createResumeLegs(ctx) {
422
500
  noteLateSettleIfAccepted(driven);
423
501
  return driven;
424
502
  }
425
- const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles", "userAttachments"];
503
+ const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles", "userAttachments", "images"];
426
504
  const ONE_SHOT_SUBMIT_STRIP = Object.fromEntries(ONE_SHOT_SUBMIT_KEYS.map((k) => [k, undefined]));
427
505
  const resolveResumeSpec = async (ctx, req, auth) => {
428
506
  const engineNotices = [];
@@ -435,7 +513,7 @@ export function createResumeLegs(ctx) {
435
513
  };
436
514
  const refuseStoredBodyReplay = (sessionId, refusal) => {
437
515
  deps.logger?.warn?.("resume_refused_stored_body", { sessionId, ...storedBodyReplayLogFields(refusal) });
438
- return storedBodyReplayAnswer(refusal, serviceVersion());
516
+ return storedBodyReplayAnswer(refusal, serviceVersion(), true);
439
517
  };
440
518
  const resolveResumeSpecOrBlocked = async (ctx, req, auth) => {
441
519
  const replay = storedBodyReplayRefusal(ctx, deps.config);
@@ -447,7 +525,7 @@ export function createResumeLegs(ctx) {
447
525
  }
448
526
  catch (e) {
449
527
  if (e instanceof HttpError) {
450
- return { ok: false, answer: resumeBlockedByPolicyAnswer(e.message, httpErrorCode(e.status, e.code), e.extra) };
528
+ return { ok: false, answer: resumeRefusalWithCard(resumeBlockedByPolicyAnswer(httpErrorRefusal(e)), true) };
451
529
  }
452
530
  throw e;
453
531
  }
@@ -501,6 +579,7 @@ export function createResumeLegs(ctx) {
501
579
  const ownerLeaseDenial = args.callerInitiated === true && args.ownerLeaseAdmitted !== true ? await ownerFleetLeaseDenied(principal) : undefined;
502
580
  if (ownerLeaseDenial)
503
581
  return ownerLeaseDenial;
582
+ const heldDecisions = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
504
583
  let taskId = await deps.runStore?.getActiveTaskId(sessionId);
505
584
  let freshRunClaimed = false;
506
585
  if (args.mintFreshRun && taskId && deps.runStore) {
@@ -622,6 +701,14 @@ export function createResumeLegs(ctx) {
622
701
  deps.modelUsage?.register(taskId);
623
702
  resumeManifestLease = deps.promptManifests?.register(taskId) ?? 0;
624
703
  }
704
+ if (args.workflowDecision !== undefined && taskId === undefined) {
705
+ throw new Error("workflow decision delivery: no run row was claimed for the delivering leg — refusing to start a delivery whose decision could not be recorded against it");
706
+ }
707
+ const workflowDecision = args.workflowDecision;
708
+ const launchInternals = {
709
+ ...heldDecisions,
710
+ ...(workflowDecision !== undefined && taskId !== undefined ? { afterCheckpointResolve: () => workflowDecision.commitBeforeLaunch(taskId) } : {}),
711
+ };
625
712
  commitSessionInheritEnv(deps, sessionId, resumeCtx.ctx.body);
626
713
  if (args.onResumeCommitted)
627
714
  await args.onResumeCommitted();
@@ -657,6 +744,7 @@ export function createResumeLegs(ctx) {
657
744
  cancelLedgerAppend = vAppend;
658
745
  const verifyTailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
659
746
  const verifyInternals = {
747
+ ...launchInternals,
660
748
  ...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
661
749
  onForwardEvent: (e) => {
662
750
  fleetPub?.onForwardEvent(e);
@@ -785,7 +873,7 @@ export function createResumeLegs(ctx) {
785
873
  resumeLegLive = true;
786
874
  const tailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
787
875
  const stream = await legRunner.resumeStream(token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, {
788
- ...(args.workflowParkedResume !== undefined ? { workflowParkedResume: args.workflowParkedResume } : {}),
876
+ ...launchInternals,
789
877
  onForwardEvent: (e) => {
790
878
  fleetPub?.onForwardEvent(e);
791
879
  {
@@ -909,6 +997,10 @@ export function createResumeLegs(ctx) {
909
997
  await flush();
910
998
  await append("compaction_outcome", compactionOutcomeEventData(ev));
911
999
  break;
1000
+ case "compaction_started":
1001
+ await flush();
1002
+ await append("compaction_started", compactionStartedEventData(ev));
1003
+ break;
912
1004
  case "workspace_changed":
913
1005
  await flush();
914
1006
  await append("workspace_changed", workspaceChangedEventData(ev));
@@ -1009,7 +1101,6 @@ export function createResumeLegs(ctx) {
1009
1101
  ? deps.toolApproval.runWithContext({
1010
1102
  taskId,
1011
1103
  owner: principal ?? null,
1012
- emit: approvalEmit,
1013
1104
  abortSignal: cancelCtrl.signal,
1014
1105
  sessionId,
1015
1106
  emitCard: createApprovalCardEmitter({ appendDurable: (f) => approvalEmit(f) }),
@@ -1028,7 +1119,6 @@ export function createResumeLegs(ctx) {
1028
1119
  }
1029
1120
  catch (e) {
1030
1121
  if (e instanceof CheckpointError) {
1031
- const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, outcome.gate) : undefined;
1032
1122
  const disposition = resumeRefusalDisposition(e);
1033
1123
  if (disposition === "cancelled") {
1034
1124
  await settleCardForCancel();
@@ -1061,21 +1151,7 @@ export function createResumeLegs(ctx) {
1061
1151
  settleFleet("failed");
1062
1152
  }
1063
1153
  }
1064
- const status = e.code === "checkpoint.not_found" ? 404 : 409;
1065
- return {
1066
- status,
1067
- body: {
1068
- ...(taskId !== undefined ? { taskId } : {}),
1069
- error: e.message,
1070
- errorCode: e.code === "checkpoint.invalid_outcome" ? (refusalCause !== undefined ? WIRE_CODE_OF_DECISION_REFUSAL[refusalCause] : UNCLASSIFIED_INVALID_OUTCOME_CODE) : e.code,
1071
- ...(e.detail?.field ? { field: e.detail.field } : {}),
1072
- ...((e.code === "resume.usage_window_exhausted" || e.code === "resume.preflight_rejected") && typeof e.detail?.retryAfterMs === "number" && Number.isFinite(e.detail.retryAfterMs) && e.detail.retryAfterMs > 0
1073
- ? { retryAfterSec: Math.max(1, Math.ceil(e.detail.retryAfterMs / 1000)) }
1074
- : {}),
1075
- ...(retriable ? { retriable: true } : {}),
1076
- ...(e.code === "checkpoint.unsupported_version" ? buildCheckpointVersionKeys(resumeCtx.ctx.writerVersion, serviceVersion()) : {}),
1077
- },
1078
- };
1154
+ return checkpointRefusalAnswer(e, { gate: outcome.gate, retriable, ...(taskId !== undefined ? { taskId } : {}), versionKeys: e.code === "checkpoint.unsupported_version" ? buildCheckpointVersionKeys(resumeCtx.ctx.writerVersion, serviceVersion()) : {} });
1079
1155
  }
1080
1156
  const thrownCause = cancelCtrl.cause();
1081
1157
  if (thrownCause !== undefined) {
@@ -1160,7 +1236,7 @@ export function createResumeLegs(ctx) {
1160
1236
  const RESUMABLE_GATE_KIND = "resource_limit";
1161
1237
  const preemptBinding = { gateKinds: [RESUMABLE_GATE_KIND] };
1162
1238
  const token = (await cs.findPendingTokenBySession(sessionId, undefined, preemptBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
1163
- const noSuspension = { status: 404, body: { error: "no resumable suspension for this task (already running, resumed, or expired)", errorCode: "not_found.suspension" } };
1239
+ const noSuspension = { status: 404, body: refusalBody({ errorCode: "not_found.suspension", message: "no resumable suspension for this task (already running, resumed, or expired)" }) };
1164
1240
  if (!token)
1165
1241
  return noSuspension;
1166
1242
  if (decider !== undefined && !decider.explicitOperator) {
@@ -1183,12 +1259,12 @@ export function createResumeLegs(ctx) {
1183
1259
  deps.logger?.warn?.("assistant_resume_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
1184
1260
  return noSuspension;
1185
1261
  }
1186
- return { status: 409, body: { error: e instanceof Error ? e.message : String(e), errorCode: "conflict.checkpoint_unreadable" } };
1262
+ return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
1187
1263
  }
1188
1264
  if (!cp) {
1189
1265
  if (!(await mayDiscloseRowError(cs, token, decider)))
1190
1266
  return noSuspension;
1191
- return { status: 404, body: { error: "checkpoint not found", errorCode: "not_found.checkpoint" } };
1267
+ return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
1192
1268
  }
1193
1269
  if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
1194
1270
  deps.logger?.warn?.("assistant_resume_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
@@ -1196,13 +1272,13 @@ export function createResumeLegs(ctx) {
1196
1272
  }
1197
1273
  const gateKind = cp.gate?.kind;
1198
1274
  if (gateKind !== RESUMABLE_GATE_KIND) {
1199
- return { status: 409, body: { error: `task is suspended on a '${gateKind ?? "unknown"}' gate, not a resumable resource/preempt suspension — an approval gate must be decided via POST /v1/approvals/:id/decide`, errorCode: "gate_not_resumable" } };
1275
+ return { status: 409, body: refusalBody({ errorCode: "gate_not_resumable", message: `task is suspended on a '${gateKind ?? "unknown"}' gate, not a resumable resource/preempt suspension — an approval gate must be decided via POST /v1/approvals/:id/decide` }) };
1200
1276
  }
1201
1277
  const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
1202
1278
  if (ctxRead.kind === "corrupt")
1203
1279
  return resumeCtxCorruptAnswer(ctxRead.where);
1204
1280
  if (ctxRead.kind === "absent")
1205
- return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
1281
+ return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
1206
1282
  const ctx = ctxRead.ctx;
1207
1283
  const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
1208
1284
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
@@ -1213,10 +1289,10 @@ export function createResumeLegs(ctx) {
1213
1289
  const verifyRounds = verifyRoundsFromBody(ctx.body);
1214
1290
  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 } : {}) });
1215
1291
  }
1216
- async function startFreshHostTurn(sessionId, objective, principal, req, trustedInternals, runId) {
1292
+ async function startFreshHostTurn(sessionId, objective, principal, req, workflowDecision, runId) {
1217
1293
  const cs = deps.checkpointStore;
1218
1294
  if (!deps.runStore) {
1219
- return { status: 501, body: { error: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)", errorCode: "capability.run_store_required" } };
1295
+ return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
1220
1296
  }
1221
1297
  const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
1222
1298
  if (ctxRead.kind === "corrupt")
@@ -1225,12 +1301,14 @@ export function createResumeLegs(ctx) {
1225
1301
  if (!ctx) {
1226
1302
  return {
1227
1303
  status: 409,
1228
- body: {
1229
- error: "this approval waited too long to be delivered: the session this run was started from no longer keeps what a new turn would need, because that state is dropped once the session has been idle past this deployment's retention window (REAP_RUN_STALE_SEC — its current value is staleAfterSec below). Nothing was decided and the approval is still pending: resume the run yourself with the runId below, or ask whoever operates this deployment to raise REAP_RUN_STALE_SEC so approvals can sit longer before someone decides them",
1304
+ body: refusalBody({
1230
1305
  errorCode: "conflict.resume_context_unavailable",
1231
- runId,
1232
- staleAfterSec: deps.config.runStaleSec,
1233
- },
1306
+ message: "this approval waited too long to be delivered: the session this run was started from no longer keeps what a new turn would need, because that state is dropped once the session has been idle past this deployment's retention window (REAP_RUN_STALE_SEC — its current value is staleAfterSec below). Nothing was decided and the approval is still pending: resume the run yourself with the runId below, or ask whoever operates this deployment to raise REAP_RUN_STALE_SEC so approvals can sit longer before someone decides them",
1307
+ facts: {
1308
+ runId,
1309
+ staleAfterSec: deps.config.runStaleSec,
1310
+ },
1311
+ }),
1234
1312
  };
1235
1313
  }
1236
1314
  const windowDenial = await usageWindowDenialFor(principal || GLOBAL_USAGE_KEY);
@@ -1247,7 +1325,7 @@ export function createResumeLegs(ctx) {
1247
1325
  fleetScope: resumeFleetScope(req, auth),
1248
1326
  elicitOwner: principal ?? null,
1249
1327
  source: "workflow_parked_decide",
1250
- trustedInternals,
1328
+ workflowDecision,
1251
1329
  });
1252
1330
  if (out.existing) {
1253
1331
  throw new Error(`decide host turn: createRun observed an existing row ${out.existing.taskId} for a freshly minted taskId (no idempotency key was sent)`);
@@ -1257,18 +1335,18 @@ export function createResumeLegs(ctx) {
1257
1335
  const minted = out.body;
1258
1336
  return acceptedResumingReceipt(minted.taskId, minted.sessionId);
1259
1337
  }
1260
- async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowParkedResume, ownerLeaseAdmittedFor) {
1338
+ async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowDecision, ownerLeaseAdmittedFor) {
1261
1339
  const cs = deps.checkpointStore;
1262
1340
  const wakeBinding = { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" };
1263
1341
  const token = (await cs.findPendingTokenBySession(sessionId, undefined, wakeBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
1264
1342
  if (!token)
1265
- return { status: 404, body: { error: "no parked checkpoint for this session (nothing to wake)", errorCode: "not_found.parked_checkpoint" } };
1266
- const operator = explicitOperatorOk(caller, deps.config.operatorPrincipals);
1343
+ return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
1344
+ const operator = explicitOperatorOk(caller, deps.config);
1267
1345
  if (!operator) {
1268
1346
  const scope = await cs.peekPendingScope(sessionId, wakeBinding).then((s) => (s === undefined ? cs.peekPendingScope(sessionId) : s)).catch(() => undefined);
1269
1347
  const decodedScope = scope != null ? decodeCheckpointScope(scope) : undefined;
1270
1348
  if (decodedScope !== undefined && decodedScope !== caller) {
1271
- return { status: 404, body: { error: "no parked checkpoint for this session (nothing to wake)", errorCode: "not_found.parked_checkpoint" } };
1349
+ return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
1272
1350
  }
1273
1351
  }
1274
1352
  let cp;
@@ -1278,22 +1356,22 @@ export function createResumeLegs(ctx) {
1278
1356
  catch (e) {
1279
1357
  if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
1280
1358
  deps.logger?.warn?.("wake_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
1281
- return { status: 404, body: { error: "no parked checkpoint for this session (nothing to wake)", errorCode: "not_found.parked_checkpoint" } };
1359
+ return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
1282
1360
  }
1283
- return { status: 409, body: { error: e instanceof Error ? e.message : String(e), errorCode: "conflict.checkpoint_unreadable" } };
1361
+ return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
1284
1362
  }
1285
1363
  if (!cp) {
1286
1364
  if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
1287
- return { status: 404, body: { error: "no parked checkpoint for this session (nothing to wake)", errorCode: "not_found.parked_checkpoint" } };
1365
+ return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
1288
1366
  }
1289
- return { status: 404, body: { error: "checkpoint not found", errorCode: "not_found.checkpoint" } };
1367
+ return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
1290
1368
  }
1291
1369
  if (!operator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== caller) {
1292
- return { status: 404, body: { error: "no parked checkpoint for this session (nothing to wake)", errorCode: "not_found.parked_checkpoint" } };
1370
+ return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
1293
1371
  }
1294
1372
  const gateKind = cp.gate?.kind;
1295
1373
  if (gateKind !== "task_done") {
1296
- return { status: 409, body: { error: `cannot wake: this session is waiting on a pending gate decision (gate "${gateKind ?? "unknown"}") — resolve it through its own decide entry`, errorCode: "wake.gate_pending" } };
1374
+ return { status: 409, body: refusalBody({ errorCode: "wake.gate_pending", message: `cannot wake: this session is waiting on a pending gate decision (gate "${gateKind ?? "unknown"}") — resolve it through its own decide entry` }) };
1297
1375
  }
1298
1376
  const trusted = operator;
1299
1377
  if (message !== undefined) {
@@ -1302,7 +1380,7 @@ export function createResumeLegs(ctx) {
1302
1380
  }
1303
1381
  catch (e) {
1304
1382
  if (e instanceof CheckpointError && e.code === "steering.invalid_content") {
1305
- return { status: 422, body: { errorCode: "steering.invalid_content", error: e.message } };
1383
+ return { status: 422, body: refusalBody({ errorCode: "steering.invalid_content", message: e.message }) };
1306
1384
  }
1307
1385
  throw e;
1308
1386
  }
@@ -1311,7 +1389,7 @@ export function createResumeLegs(ctx) {
1311
1389
  if (ctxRead.kind === "corrupt")
1312
1390
  return resumeCtxCorruptAnswer(ctxRead.where);
1313
1391
  if (ctxRead.kind === "absent")
1314
- return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
1392
+ return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
1315
1393
  const ctx = ctxRead.ctx;
1316
1394
  const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
1317
1395
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
@@ -1331,10 +1409,10 @@ export function createResumeLegs(ctx) {
1331
1409
  verifyRounds: undefined,
1332
1410
  resumeCtx: { sessionId, ctx },
1333
1411
  mintFreshRun: { source: "wake" },
1412
+ ...(workflowDecision !== undefined ? { workflowDecision } : {}),
1334
1413
  ...(engineNotices.length > 0 ? { engineNotices } : {}),
1335
1414
  ...(acceptEarly === true ? { acceptEarly: true } : {}),
1336
1415
  ...(req !== undefined ? { callerInitiated: true } : {}),
1337
- ...(workflowParkedResume !== undefined ? { workflowParkedResume } : {}),
1338
1416
  ...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
1339
1417
  }));
1340
1418
  }
@@ -1349,7 +1427,7 @@ export function createResumeLegs(ctx) {
1349
1427
  const PLAN_REVIEW_GATE_KIND = "plan_review";
1350
1428
  const planReviewBinding = { gateKinds: [PLAN_REVIEW_GATE_KIND] };
1351
1429
  const token = (await cs.findPendingTokenBySession(sessionId, undefined, planReviewBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
1352
- const noPending = { status: 404, body: { error: "no pending plan_review for this session (already decided or expired)", errorCode: "not_found.plan_review" } };
1430
+ const noPending = { status: 404, body: refusalBody({ errorCode: "not_found.plan_review", message: "no pending plan_review for this session (already decided or expired)" }) };
1353
1431
  if (!token) {
1354
1432
  log404("no-pending-checkpoint");
1355
1433
  return refuse(noPending);
@@ -1374,13 +1452,13 @@ export function createResumeLegs(ctx) {
1374
1452
  deps.logger?.warn?.("plan_review_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
1375
1453
  return refuse(noPending);
1376
1454
  }
1377
- return refuse({ status: 409, body: { error: e instanceof Error ? e.message : String(e), errorCode: "conflict.checkpoint_unreadable" } });
1455
+ return refuse({ status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) });
1378
1456
  }
1379
1457
  if (!cp) {
1380
1458
  log404("checkpoint-missing");
1381
1459
  if (!(await mayDiscloseRowError(cs, token, decider)))
1382
1460
  return refuse(noPending);
1383
- return refuse({ status: 404, body: { error: "checkpoint not found", errorCode: "not_found.checkpoint" } });
1461
+ return refuse({ status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) });
1384
1462
  }
1385
1463
  if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
1386
1464
  deps.logger?.warn?.("plan_review_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
@@ -1390,17 +1468,17 @@ export function createResumeLegs(ctx) {
1390
1468
  if (gateKind !== PLAN_REVIEW_GATE_KIND) {
1391
1469
  return refuse({
1392
1470
  status: 409,
1393
- body: {
1394
- error: `task is suspended on a '${gateKind ?? "unknown"}' gate, not a plan_review — a tool approval must be decided via POST /v1/approvals/:id/decide`,
1471
+ body: refusalBody({
1395
1472
  errorCode: "gate_not_plan_review",
1396
- },
1473
+ message: `task is suspended on a '${gateKind ?? "unknown"}' gate, not a plan_review — a tool approval must be decided via POST /v1/approvals/:id/decide`,
1474
+ }),
1397
1475
  });
1398
1476
  }
1399
1477
  const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
1400
1478
  if (ctxRead.kind === "corrupt")
1401
1479
  return refuse(resumeCtxCorruptAnswer(ctxRead.where));
1402
1480
  if (ctxRead.kind === "absent")
1403
- return refuse({ status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } });
1481
+ return refuse({ status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) });
1404
1482
  const replay = storedBodyReplayRefusal(ctxRead.ctx, deps.config);
1405
1483
  if (replay !== undefined)
1406
1484
  return refuse(refuseStoredBodyReplay(sessionId, replay));