@sema-agent/server 7.132.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 (402) hide show
  1. package/MIGRATION.md +266 -0
  2. package/README.md +8 -9
  3. package/README.zh-CN.md +7 -6
  4. package/USAGE.md +174 -55
  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 +79 -19
  24. package/dist/approval-card.js +44 -13
  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 +27 -10
  29. package/dist/audit.js +12 -16
  30. package/dist/bake-runner/main.js +3 -2
  31. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  32. package/dist/boot/execution-env.js +9 -7
  33. package/dist/boot/execution-lane.js +11 -0
  34. package/dist/boot/leader.js +2 -0
  35. package/dist/boot/memory-boundary.js +2 -2
  36. package/dist/boot/memory-consolidation.d.ts +5 -4
  37. package/dist/boot/memory-consolidation.js +7 -6
  38. package/dist/boot/org-delivery.d.ts +28 -0
  39. package/dist/boot/org-delivery.js +20 -0
  40. package/dist/boot/reapers.d.ts +3 -0
  41. package/dist/boot/reapers.js +21 -1
  42. package/dist/boot/resolve-spec.d.ts +3 -0
  43. package/dist/boot/resolve-spec.js +60 -81
  44. package/dist/boot/runner-deps.js +1 -0
  45. package/dist/boot/session-faces.d.ts +1 -0
  46. package/dist/boot/session-faces.js +4 -2
  47. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  48. package/dist/boot/stage-06-runners.d.ts +2 -0
  49. package/dist/boot/stage-06-runners.js +6 -3
  50. package/dist/boot/stage-07-capability-layer.d.ts +2 -0
  51. package/dist/boot/stage-07-capability-layer.js +24 -3
  52. package/dist/boot/stage-08-reapers.d.ts +2 -0
  53. package/dist/boot/stage-08-reapers.js +2 -2
  54. package/dist/boot/stage-09-leader.d.ts +3 -0
  55. package/dist/boot/stage-10-http-server.d.ts +3 -0
  56. package/dist/boot/stage-10-http-server.js +5 -6
  57. package/dist/boot/stores.js +2 -2
  58. package/dist/boot/web-admin.d.ts +4 -0
  59. package/dist/boot/web-admin.js +151 -63
  60. package/dist/boot/workflow-orchestration.d.ts +2 -0
  61. package/dist/boot/workflow-orchestration.js +2 -1
  62. package/dist/brain.js +8 -3
  63. package/dist/breaker-key.d.ts +6 -0
  64. package/dist/breaker-key.js +29 -0
  65. package/dist/caller-text-echo.d.ts +9 -6
  66. package/dist/caller-text-echo.js +8 -11
  67. package/dist/capabilities/center-plugins.d.ts +7 -0
  68. package/dist/capabilities/center-plugins.js +1 -1
  69. package/dist/capabilities/composite-members.d.ts +1 -1
  70. package/dist/capabilities/composite-members.js +1 -1
  71. package/dist/capabilities/execution-lane.js +2 -2
  72. package/dist/capabilities/hands-lane.d.ts +21 -0
  73. package/dist/capabilities/hands-lane.js +10 -0
  74. package/dist/capabilities/mcp-probe.d.ts +36 -4
  75. package/dist/capabilities/mcp-probe.js +27 -4
  76. package/dist/capabilities/scenarios.d.ts +63 -7
  77. package/dist/capabilities/scenarios.js +39 -7
  78. package/dist/capability-facts.d.ts +34 -4
  79. package/dist/capability-facts.js +28 -2
  80. package/dist/center-config-tier.d.ts +69 -0
  81. package/dist/center-config-tier.js +42 -0
  82. package/dist/checkpoint-identity.d.ts +16 -0
  83. package/dist/checkpoint-identity.js +19 -0
  84. package/dist/config-catalog.d.ts +17 -10
  85. package/dist/config-catalog.js +43 -28
  86. package/dist/config-d-migration.d.ts +3 -1
  87. package/dist/config-d-migration.js +2 -2
  88. package/dist/config-dir-trust.d.ts +114 -0
  89. package/dist/config-dir-trust.js +388 -0
  90. package/dist/config-invariants.d.ts +18 -5
  91. package/dist/config-invariants.js +35 -3
  92. package/dist/config-provider.d.ts +6 -1
  93. package/dist/config-provider.js +6 -2
  94. package/dist/config-types.d.ts +71 -14
  95. package/dist/config-types.js +8 -0
  96. package/dist/config.d.ts +13 -0
  97. package/dist/config.js +121 -59
  98. package/dist/delegated-session-owner.d.ts +185 -0
  99. package/dist/delegated-session-owner.js +119 -0
  100. package/dist/device-protocol.d.ts +5 -3
  101. package/dist/device-protocol.js +1 -1
  102. package/dist/device-ws-hub.js +21 -2
  103. package/dist/device-ws-protocol.d.ts +70 -6
  104. package/dist/device-ws-protocol.js +50 -23
  105. package/dist/elicitation.js +3 -2
  106. package/dist/entry-problems.d.ts +34 -0
  107. package/dist/entry-problems.js +14 -0
  108. package/dist/execution-lane-caps.d.ts +49 -10
  109. package/dist/execution-lane-caps.js +43 -4
  110. package/dist/field-conflict.d.ts +43 -0
  111. package/dist/field-conflict.js +20 -0
  112. package/dist/fleet/fleet-bus.d.ts +80 -6
  113. package/dist/fleet/fleet-bus.js +41 -9
  114. package/dist/hooks/classic-stations.d.ts +10 -5
  115. package/dist/hooks/classic-stations.js +4 -4
  116. package/dist/hooks/command-spawn.d.ts +57 -0
  117. package/dist/hooks/command-spawn.js +139 -0
  118. package/dist/hooks/hook-llm.js +2 -2
  119. package/dist/hooks/hook-runner.d.ts +127 -25
  120. package/dist/hooks/hook-runner.js +668 -287
  121. package/dist/hooks/hook-source.d.ts +44 -0
  122. package/dist/hooks/hook-source.js +40 -0
  123. package/dist/http/active-run-conflict.d.ts +5 -13
  124. package/dist/http/active-run-conflict.js +17 -27
  125. package/dist/http/admission.js +103 -60
  126. package/dist/http/approval-sweeps.js +55 -18
  127. package/dist/http/caller-trust.d.ts +77 -0
  128. package/dist/http/caller-trust.js +143 -0
  129. package/dist/http/checkpoint-ctx.d.ts +16 -16
  130. package/dist/http/checkpoint-ctx.js +13 -10
  131. package/dist/http/claimed-run.d.ts +4 -3
  132. package/dist/http/device-placement.d.ts +2 -4
  133. package/dist/http/device-placement.js +2 -1
  134. package/dist/http/dispatch.js +10 -4
  135. package/dist/http/durable-run-leg.d.ts +4 -32
  136. package/dist/http/durable-run-leg.js +34 -10
  137. package/dist/http/owner-access.d.ts +56 -0
  138. package/dist/http/owner-access.js +42 -0
  139. package/dist/http/principal-gate.d.ts +48 -23
  140. package/dist/http/principal-gate.js +39 -12
  141. package/dist/http/readiness.d.ts +11 -5
  142. package/dist/http/readiness.js +10 -3
  143. package/dist/http/request-guards.d.ts +16 -23
  144. package/dist/http/request-guards.js +30 -15
  145. package/dist/http/resume-ctx-read.d.ts +2 -5
  146. package/dist/http/resume-ctx-read.js +7 -4
  147. package/dist/http/resume-legs.d.ts +3 -4
  148. package/dist/http/resume-legs.js +126 -69
  149. package/dist/http/retired-service-deps.d.ts +43 -0
  150. package/dist/http/retired-service-deps.js +18 -0
  151. package/dist/http/route-ctx.d.ts +44 -28
  152. package/dist/http/route-ctx.js +4 -0
  153. package/dist/http/route-table.js +2 -0
  154. package/dist/http/routes/a2a-serve.js +3 -4
  155. package/dist/http/routes/admin-config-refresh.js +1 -1
  156. package/dist/http/routes/admin-drain.js +1 -1
  157. package/dist/http/routes/admin-memory-optout.js +5 -1
  158. package/dist/http/routes/adoption.js +2 -9
  159. package/dist/http/routes/approvals-assistant.js +49 -48
  160. package/dist/http/routes/background.js +3 -2
  161. package/dist/http/routes/capabilities.js +20 -4
  162. package/dist/http/routes/config-catalog.d.ts +3 -3
  163. package/dist/http/routes/config-catalog.js +1 -1
  164. package/dist/http/routes/devices.js +3 -17
  165. package/dist/http/routes/diagnostics.js +5 -1
  166. package/dist/http/routes/images.js +6 -7
  167. package/dist/http/routes/leader.js +1 -8
  168. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  169. package/dist/http/routes/mcp-reconnect.js +3 -2
  170. package/dist/http/routes/memory-bundle.js +1 -1
  171. package/dist/http/routes/memory-compliance.js +1 -1
  172. package/dist/http/routes/memory-consolidation.js +1 -1
  173. package/dist/http/routes/memory-origin.js +1 -1
  174. package/dist/http/routes/memory-policy.d.ts +1 -1
  175. package/dist/http/routes/memory-policy.js +3 -2
  176. package/dist/http/routes/notify-wake.js +8 -21
  177. package/dist/http/routes/observability.js +2 -2
  178. package/dist/http/routes/principal-inbox.d.ts +39 -0
  179. package/dist/http/routes/principal-inbox.js +76 -0
  180. package/dist/http/routes/retention-ops.js +2 -9
  181. package/dist/http/routes/rules.js +5 -18
  182. package/dist/http/routes/runs.d.ts +5 -0
  183. package/dist/http/routes/runs.js +33 -106
  184. package/dist/http/routes/session-sync.js +15 -28
  185. package/dist/http/routes/sessions.js +30 -40
  186. package/dist/http/routes/side-query.js +6 -4
  187. package/dist/http/routes/tasks.js +50 -14
  188. package/dist/http/routes/workflows.d.ts +351 -0
  189. package/dist/http/routes/workflows.js +153 -119
  190. package/dist/http/send.d.ts +24 -3
  191. package/dist/http/send.js +14 -2
  192. package/dist/http/server.d.ts +17 -18
  193. package/dist/http/server.js +15 -13
  194. package/dist/http/stored-body-replay.js +17 -4
  195. package/dist/http/submit-ctx.js +4 -3
  196. package/dist/http/submit-replay.d.ts +4 -3
  197. package/dist/http/submit-replay.js +5 -4
  198. package/dist/http/wire-types.d.ts +10 -7
  199. package/dist/index.d.ts +1 -1
  200. package/dist/index.js +0 -1
  201. package/dist/lane-home-probe.d.ts +78 -9
  202. package/dist/lane-home-probe.js +78 -12
  203. package/dist/leader/endpoint.d.ts +4 -1
  204. package/dist/leader/endpoint.js +11 -10
  205. package/dist/limit-refusal.d.ts +17 -5
  206. package/dist/limit-refusal.js +4 -1
  207. package/dist/mcp-assertion-header.d.ts +31 -10
  208. package/dist/mcp-assertion-header.js +10 -3
  209. package/dist/mcp-tool-faces.js +1 -3
  210. package/dist/memory-project-layer.d.ts +37 -0
  211. package/dist/memory-project-layer.js +46 -0
  212. package/dist/memory-scope.d.ts +23 -16
  213. package/dist/memory-scope.js +24 -8
  214. package/dist/memory-sync-client.d.ts +27 -13
  215. package/dist/memory-sync-client.js +107 -18
  216. package/dist/model-entry-refusal.d.ts +9 -1
  217. package/dist/model-entry-refusal.js +6 -1
  218. package/dist/model-plane-face.d.ts +51 -0
  219. package/dist/model-plane-face.js +24 -0
  220. package/dist/model-provider.d.ts +5 -0
  221. package/dist/model-provider.js +4 -2
  222. package/dist/model-route-endpoint.d.ts +8 -11
  223. package/dist/model-route-endpoint.js +4 -7
  224. package/dist/model-route-env.d.ts +22 -0
  225. package/dist/model-route-env.js +2 -0
  226. package/dist/model-route-gap.d.ts +60 -30
  227. package/dist/model-route-gap.js +21 -15
  228. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  229. package/dist/observability/corrupt-read-seat.js +9 -0
  230. package/dist/observability/err-text.d.ts +6 -0
  231. package/dist/observability/err-text.js +18 -4
  232. package/dist/observability/fail-open.d.ts +35 -9
  233. package/dist/observability/fail-open.js +35 -9
  234. package/dist/observability/metrics.js +1 -0
  235. package/dist/observability/prompt-manifest.d.ts +3 -17
  236. package/dist/observability/prompt-manifest.js +45 -42
  237. package/dist/observability/tool-trace.js +9 -9
  238. package/dist/operator-ask.d.ts +10 -0
  239. package/dist/operator-ask.js +3 -0
  240. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  241. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  242. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  243. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  244. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  245. package/dist/orchestration/workflow-notify-journal.js +27 -34
  246. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  247. package/dist/orchestration/workflow-park-decision.js +118 -0
  248. package/dist/org-delivery-gate.d.ts +81 -0
  249. package/dist/org-delivery-gate.js +59 -0
  250. package/dist/panel-tool-names.d.ts +23 -0
  251. package/dist/panel-tool-names.js +8 -0
  252. package/dist/parked-decide.d.ts +39 -6
  253. package/dist/parked-decide.js +98 -51
  254. package/dist/parked-revive-surface.d.ts +9 -2
  255. package/dist/parked-revive-surface.js +11 -8
  256. package/dist/permission-rule-list.d.ts +1 -1
  257. package/dist/permission-rule-list.js +2 -2
  258. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  259. package/dist/plugins/approval-ask-store-file.js +21 -0
  260. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  261. package/dist/plugins/approval-ask-store-memory.js +34 -2
  262. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  263. package/dist/plugins/approval-ask-store-sql.js +37 -1
  264. package/dist/plugins/blob-backend.d.ts +38 -25
  265. package/dist/plugins/blob-backend.js +29 -19
  266. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  267. package/dist/plugins/breaker-state-sql.js +4 -4
  268. package/dist/plugins/caching-session-store.d.ts +3 -0
  269. package/dist/plugins/caching-session-store.js +3 -0
  270. package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
  271. package/dist/plugins/checkpoint-store-sql.js +20 -2
  272. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  273. package/dist/plugins/file-history-store-sql.js +3 -1
  274. package/dist/plugins/file-run-store.d.ts +5 -13
  275. package/dist/plugins/file-run-store.js +9 -0
  276. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  277. package/dist/plugins/fork-routing-session-store.js +12 -1
  278. package/dist/plugins/host-platform.d.ts +2 -0
  279. package/dist/plugins/host-platform.js +2 -1
  280. package/dist/plugins/local-checkpoint-store.d.ts +2 -0
  281. package/dist/plugins/local-checkpoint-store.js +6 -1
  282. package/dist/plugins/local-session-store.d.ts +1 -1
  283. package/dist/plugins/local-session-window.d.ts +39 -0
  284. package/dist/plugins/local-session-window.js +54 -0
  285. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  286. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  287. package/dist/plugins/mailbox-store-sql.js +77 -52
  288. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  289. package/dist/plugins/memory-run-store.d.ts +5 -13
  290. package/dist/plugins/memory-run-store.js +8 -0
  291. package/dist/plugins/one-time-migrations.d.ts +110 -5
  292. package/dist/plugins/one-time-migrations.js +320 -8
  293. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  294. package/dist/plugins/org-delivery-quota-store.js +228 -0
  295. package/dist/plugins/pg-pool.js +39 -7
  296. package/dist/plugins/pg-session-storage.d.ts +17 -4
  297. package/dist/plugins/pg-session-storage.js +29 -6
  298. package/dist/plugins/remote-env-adb.d.ts +6 -12
  299. package/dist/plugins/remote-env-adb.js +12 -2
  300. package/dist/plugins/remote-env-k8s.d.ts +8 -12
  301. package/dist/plugins/remote-env-k8s.js +23 -5
  302. package/dist/plugins/remote-env-local-docker.d.ts +10 -15
  303. package/dist/plugins/remote-env-local-docker.js +12 -9
  304. package/dist/plugins/remote-env-ssh.d.ts +10 -16
  305. package/dist/plugins/remote-env-ssh.js +17 -11
  306. package/dist/plugins/run-store-sql.d.ts +16 -14
  307. package/dist/plugins/run-store-sql.js +22 -2
  308. package/dist/plugins/schema-indexes.js +2 -0
  309. package/dist/plugins/send-user-file.js +2 -2
  310. package/dist/plugins/session-epoch-anchor.d.ts +32 -6
  311. package/dist/plugins/session-epoch-anchor.js +2 -3
  312. package/dist/plugins/session-store.d.ts +18 -8
  313. package/dist/plugins/session-store.js +15 -4
  314. package/dist/plugins/sql-driver.d.ts +74 -11
  315. package/dist/plugins/sql-driver.js +210 -12
  316. package/dist/plugins/sql-errors.d.ts +23 -0
  317. package/dist/plugins/sql-errors.js +21 -0
  318. package/dist/plugins/store-backend.d.ts +36 -1
  319. package/dist/plugins/store-backend.js +40 -14
  320. package/dist/plugins/store-contracts.d.ts +42 -0
  321. package/dist/plugins/store-contracts.js +1 -0
  322. package/dist/plugins/tidb-pool.d.ts +6 -1
  323. package/dist/plugins/tidb-pool.js +44 -10
  324. package/dist/plugins/tidb-session-storage.d.ts +3 -2
  325. package/dist/plugins/tidb-session-storage.js +2 -2
  326. package/dist/plugins/tidb-session-store.d.ts +24 -4
  327. package/dist/plugins/tidb-session-store.js +28 -4
  328. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  329. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  330. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  331. package/dist/plugins/workflow-run-store-sql.js +50 -9
  332. package/dist/posture-source.d.ts +18 -10
  333. package/dist/posture-source.js +4 -1
  334. package/dist/project-identity.d.ts +45 -6
  335. package/dist/project-identity.js +162 -8
  336. package/dist/project-layer-key.d.ts +28 -0
  337. package/dist/project-layer-key.js +29 -0
  338. package/dist/question.js +3 -2
  339. package/dist/read-deny-entry.d.ts +25 -0
  340. package/dist/read-deny-entry.js +12 -0
  341. package/dist/read-face-posture.d.ts +37 -11
  342. package/dist/read-face-posture.js +32 -3
  343. package/dist/request-key-closure.d.ts +7 -0
  344. package/dist/request-key-closure.js +9 -1
  345. package/dist/request-value-closure.d.ts +5 -0
  346. package/dist/request-value-closure.js +11 -3
  347. package/dist/rule-entry-list.d.ts +26 -2
  348. package/dist/rule-entry-list.js +72 -12
  349. package/dist/rules-consent.js +3 -4
  350. package/dist/run-acceptance.d.ts +19 -3
  351. package/dist/run-acceptance.js +13 -1
  352. package/dist/run-local.js +36 -6
  353. package/dist/runs.d.ts +10 -9
  354. package/dist/runs.js +21 -3
  355. package/dist/runtime-caps-resolver.d.ts +16 -6
  356. package/dist/runtime-caps-resolver.js +18 -5
  357. package/dist/sandbox-pkg-source.d.ts +19 -1
  358. package/dist/sandbox-pkg-source.js +29 -7
  359. package/dist/security.d.ts +87 -9
  360. package/dist/security.js +52 -9
  361. package/dist/session-error-wire.d.ts +19 -2
  362. package/dist/session-error-wire.js +5 -1
  363. package/dist/session-titled-frame.d.ts +33 -0
  364. package/dist/session-titled-frame.js +4 -0
  365. package/dist/session-titler.d.ts +9 -1
  366. package/dist/session-titler.js +15 -4
  367. package/dist/spec-fields.js +21 -2
  368. package/dist/sql-engine-posture.d.ts +3 -1
  369. package/dist/task-cwd.d.ts +1 -1
  370. package/dist/task-mcp.d.ts +3 -3
  371. package/dist/task-mcp.js +7 -66
  372. package/dist/task-settings.d.ts +89 -88
  373. package/dist/task-settings.js +71 -63
  374. package/dist/text-bidi.d.ts +0 -4
  375. package/dist/text-bidi.js +1 -3
  376. package/dist/tighten-only-seats.d.ts +150 -0
  377. package/dist/tighten-only-seats.js +196 -0
  378. package/dist/tool-approval.d.ts +303 -87
  379. package/dist/tool-approval.js +623 -239
  380. package/dist/trace/core-keyset-guard.d.ts +104 -22
  381. package/dist/trace/exit-seat.d.ts +28 -0
  382. package/dist/trace/exit-seat.js +76 -0
  383. package/dist/trace/ledger-events.d.ts +3 -2
  384. package/dist/trace/project.d.ts +80 -12
  385. package/dist/trace/project.js +41 -28
  386. package/dist/trace/projection-table.d.ts +100 -0
  387. package/dist/trace/projection-table.js +130 -0
  388. package/dist/trace/redact.d.ts +54 -0
  389. package/dist/trace/redact.js +778 -152
  390. package/dist/trace/self-minted-wire.d.ts +345 -1
  391. package/dist/trace/self-minted-wire.js +117 -1
  392. package/dist/trace/wire-projection-faces.d.ts +298 -2
  393. package/dist/trace/wire-projection-faces.js +154 -13
  394. package/dist/url-refusal.js +2 -0
  395. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  396. package/dist/web-admin-client/apply-effective.js +56 -5
  397. package/dist/web-admin-client/facade.d.ts +1 -1
  398. package/dist/web-admin-client/facade.js +1 -1
  399. package/dist/web-admin-client/read-face.js +5 -9
  400. package/dist/web-admin-client/skills-mcp.js +4 -5
  401. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
  402. package/package.json +7 -3
@@ -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";
@@ -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";
@@ -50,6 +53,7 @@ import { refusalBody } from "./refusal-body.js";
50
53
  import { storedBodyReplayRefusal } from "./stored-body-replay.js";
51
54
  import { httpErrorRefusal } from "./send.js";
52
55
  import { createRunAcceptance } from "../run-acceptance.js";
56
+ import { fieldConflictFacts } from "../field-conflict.js";
53
57
  function legMayHaveReopenedCheckpoint(r) {
54
58
  return r.terminal.kind === "failed" && nestedPauseOf(r.terminal) === undefined;
55
59
  }
@@ -170,7 +174,7 @@ function decisionRefusalCauseOf(e, gate) {
170
174
  return role === undefined ? undefined : FIELD_ROLE_CAUSE[role];
171
175
  }
172
176
  function notResumableAnswer() {
173
- 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)" }) };
174
178
  }
175
179
  export function checkpointRefusalAnswer(e, at) {
176
180
  const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, at.gate) : undefined;
@@ -212,7 +216,7 @@ export function createResumeLegs(ctx) {
212
216
  }
213
217
  if (!token && !attributable && !seenUnreadable) {
214
218
  deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true });
215
- 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" }) };
216
220
  }
217
221
  let currentPending;
218
222
  if (token && decider !== undefined) {
@@ -225,7 +229,7 @@ export function createResumeLegs(ctx) {
225
229
  const scopeOk = cur !== null && (decider.explicitOperator || curOwner === undefined || curOwner === decider.principal);
226
230
  if (cur && cur.sessionId === sessionId && !scopeOk) {
227
231
  deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true, foreignCurrentSuppressed: true });
228
- 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" }) };
229
233
  }
230
234
  if (cur && cur.status === "pending" && cur.sessionId === sessionId && scopeOk && curPa?.kind === "tool_approval" && curPa.toolCallId) {
231
235
  currentPending = {
@@ -237,22 +241,24 @@ export function createResumeLegs(ctx) {
237
241
  }
238
242
  return {
239
243
  status: 409,
240
- body: {
241
- error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
244
+ body: refusalBody({
242
245
  errorCode: "approval_stale",
243
- ...(attributable ? { terminal: "resolved" } : {}),
244
- ...(currentPending ? { currentPending } : {}),
245
- },
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
+ }),
246
252
  };
247
253
  }
248
254
  if (!token) {
249
255
  deps.logger?.info?.("approval_decide_no_pending", { sessionId });
250
- 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" }) };
251
257
  }
252
258
  const cpPromise = cs.get(token);
253
259
  const ctxPromise = cs.getCtx(sessionId);
254
260
  void ctxPromise.catch(() => { });
255
- 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" }) };
256
262
  let cp;
257
263
  try {
258
264
  cp = await cpPromise;
@@ -263,18 +269,18 @@ export function createResumeLegs(ctx) {
263
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" });
264
270
  return noApproval;
265
271
  }
266
- 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) }) };
267
273
  }
268
274
  if (!cp) {
269
275
  if (!(await mayDiscloseRowError(cs, token, decider)))
270
276
  return noApproval;
271
- 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" }) };
272
278
  }
273
279
  if (decider !== undefined && !decider.explicitOperator) {
274
280
  const cpOwner = decodeCheckpointScope(cp.scope);
275
281
  if (cpOwner !== undefined && cpOwner !== decider.principal) {
276
282
  deps.logger?.warn?.("approval_decide_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
277
- 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" }) };
278
284
  }
279
285
  }
280
286
  const decideGateKind = cp.gate?.kind;
@@ -287,21 +293,25 @@ export function createResumeLegs(ctx) {
287
293
  : null;
288
294
  return {
289
295
  status: 409,
290
- body: {
291
- 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({
292
297
  errorCode: "gate_not_tool_approval",
293
- ...(decideGateKind ? { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } } : {}),
294
- },
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
+ }),
295
303
  };
296
304
  }
297
305
  if (req !== undefined && !binding?.checkpointToken && !binding?.boundCallId) {
298
306
  return {
299
307
  status: 400,
300
- body: {
301
- error: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
308
+ body: refusalBody({
302
309
  errorCode: "decide_requires_binding",
303
- bindingEnforced: true,
304
- },
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
+ }),
305
315
  };
306
316
  }
307
317
  const s57RowCallId = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
@@ -316,11 +326,13 @@ export function createResumeLegs(ctx) {
316
326
  }
317
327
  return {
318
328
  status: 409,
319
- body: {
320
- error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
329
+ body: refusalBody({
321
330
  errorCode: "approval_stale",
322
- ...(s57CurrentPending ? { currentPending: s57CurrentPending } : {}),
323
- },
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
+ }),
324
336
  };
325
337
  }
326
338
  const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
@@ -407,14 +419,48 @@ export function createResumeLegs(ctx) {
407
419
  const plan = planWorkflowParkedDecide(origin, parkedReq);
408
420
  if (!plan.ok)
409
421
  return plan.deny;
410
- 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
+ };
411
452
  const hostToken = await cs
412
453
  .findPendingTokenBySession(plan.hostSessionId, undefined, { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" })
413
454
  .catch(() => undefined);
414
455
  const admittedPayer = decodeCheckpointScope(cp.scope);
415
- return hostToken
416
- ? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly, parkedResume.workflowParkedResume, admittedPayer)
417
- : 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;
418
464
  }
419
465
  }
420
466
  const ctxRead0 = await readResumeCtx(ctxPromise);
@@ -422,7 +468,7 @@ export function createResumeLegs(ctx) {
422
468
  return resumeCtxCorruptAnswer(ctxRead0.where);
423
469
  const ctx = ctxRead0.kind === "ok" ? ctxRead0.ctx : null;
424
470
  if (!ctx)
425
- 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" }) };
426
472
  const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
427
473
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
428
474
  if (!resolved.ok)
@@ -430,10 +476,10 @@ export function createResumeLegs(ctx) {
430
476
  const { spec, engineNotices } = resolved;
431
477
  const pendingTool = cp.pendingAction.toolName;
432
478
  if (decision === "approve" && pendingTool === ASK_USER_QUESTION_TOOL_NAME && !answer) {
433
- 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"]) } }) };
434
480
  }
435
481
  if (answer && (decision !== "approve" || pendingTool !== ASK_USER_QUESTION_TOOL_NAME)) {
436
- 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"]) } }) };
437
483
  }
438
484
  const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
439
485
  const pa = cp.pendingAction;
@@ -533,6 +579,7 @@ export function createResumeLegs(ctx) {
533
579
  const ownerLeaseDenial = args.callerInitiated === true && args.ownerLeaseAdmitted !== true ? await ownerFleetLeaseDenied(principal) : undefined;
534
580
  if (ownerLeaseDenial)
535
581
  return ownerLeaseDenial;
582
+ const heldDecisions = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
536
583
  let taskId = await deps.runStore?.getActiveTaskId(sessionId);
537
584
  let freshRunClaimed = false;
538
585
  if (args.mintFreshRun && taskId && deps.runStore) {
@@ -654,6 +701,14 @@ export function createResumeLegs(ctx) {
654
701
  deps.modelUsage?.register(taskId);
655
702
  resumeManifestLease = deps.promptManifests?.register(taskId) ?? 0;
656
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
+ };
657
712
  commitSessionInheritEnv(deps, sessionId, resumeCtx.ctx.body);
658
713
  if (args.onResumeCommitted)
659
714
  await args.onResumeCommitted();
@@ -689,6 +744,7 @@ export function createResumeLegs(ctx) {
689
744
  cancelLedgerAppend = vAppend;
690
745
  const verifyTailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
691
746
  const verifyInternals = {
747
+ ...launchInternals,
692
748
  ...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
693
749
  onForwardEvent: (e) => {
694
750
  fleetPub?.onForwardEvent(e);
@@ -817,7 +873,7 @@ export function createResumeLegs(ctx) {
817
873
  resumeLegLive = true;
818
874
  const tailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
819
875
  const stream = await legRunner.resumeStream(token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, {
820
- ...(args.workflowParkedResume !== undefined ? { workflowParkedResume: args.workflowParkedResume } : {}),
876
+ ...launchInternals,
821
877
  onForwardEvent: (e) => {
822
878
  fleetPub?.onForwardEvent(e);
823
879
  {
@@ -1045,7 +1101,6 @@ export function createResumeLegs(ctx) {
1045
1101
  ? deps.toolApproval.runWithContext({
1046
1102
  taskId,
1047
1103
  owner: principal ?? null,
1048
- emit: approvalEmit,
1049
1104
  abortSignal: cancelCtrl.signal,
1050
1105
  sessionId,
1051
1106
  emitCard: createApprovalCardEmitter({ appendDurable: (f) => approvalEmit(f) }),
@@ -1181,7 +1236,7 @@ export function createResumeLegs(ctx) {
1181
1236
  const RESUMABLE_GATE_KIND = "resource_limit";
1182
1237
  const preemptBinding = { gateKinds: [RESUMABLE_GATE_KIND] };
1183
1238
  const token = (await cs.findPendingTokenBySession(sessionId, undefined, preemptBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
1184
- 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)" }) };
1185
1240
  if (!token)
1186
1241
  return noSuspension;
1187
1242
  if (decider !== undefined && !decider.explicitOperator) {
@@ -1204,12 +1259,12 @@ export function createResumeLegs(ctx) {
1204
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" });
1205
1260
  return noSuspension;
1206
1261
  }
1207
- 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) }) };
1208
1263
  }
1209
1264
  if (!cp) {
1210
1265
  if (!(await mayDiscloseRowError(cs, token, decider)))
1211
1266
  return noSuspension;
1212
- 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" }) };
1213
1268
  }
1214
1269
  if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
1215
1270
  deps.logger?.warn?.("assistant_resume_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
@@ -1217,13 +1272,13 @@ export function createResumeLegs(ctx) {
1217
1272
  }
1218
1273
  const gateKind = cp.gate?.kind;
1219
1274
  if (gateKind !== RESUMABLE_GATE_KIND) {
1220
- 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` }) };
1221
1276
  }
1222
1277
  const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
1223
1278
  if (ctxRead.kind === "corrupt")
1224
1279
  return resumeCtxCorruptAnswer(ctxRead.where);
1225
1280
  if (ctxRead.kind === "absent")
1226
- 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" }) };
1227
1282
  const ctx = ctxRead.ctx;
1228
1283
  const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
1229
1284
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
@@ -1234,10 +1289,10 @@ export function createResumeLegs(ctx) {
1234
1289
  const verifyRounds = verifyRoundsFromBody(ctx.body);
1235
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 } : {}) });
1236
1291
  }
1237
- async function startFreshHostTurn(sessionId, objective, principal, req, trustedInternals, runId) {
1292
+ async function startFreshHostTurn(sessionId, objective, principal, req, workflowDecision, runId) {
1238
1293
  const cs = deps.checkpointStore;
1239
1294
  if (!deps.runStore) {
1240
- 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)" }) };
1241
1296
  }
1242
1297
  const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
1243
1298
  if (ctxRead.kind === "corrupt")
@@ -1246,12 +1301,14 @@ export function createResumeLegs(ctx) {
1246
1301
  if (!ctx) {
1247
1302
  return {
1248
1303
  status: 409,
1249
- body: {
1250
- 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({
1251
1305
  errorCode: "conflict.resume_context_unavailable",
1252
- runId,
1253
- staleAfterSec: deps.config.runStaleSec,
1254
- },
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
+ }),
1255
1312
  };
1256
1313
  }
1257
1314
  const windowDenial = await usageWindowDenialFor(principal || GLOBAL_USAGE_KEY);
@@ -1268,7 +1325,7 @@ export function createResumeLegs(ctx) {
1268
1325
  fleetScope: resumeFleetScope(req, auth),
1269
1326
  elicitOwner: principal ?? null,
1270
1327
  source: "workflow_parked_decide",
1271
- trustedInternals,
1328
+ workflowDecision,
1272
1329
  });
1273
1330
  if (out.existing) {
1274
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)`);
@@ -1278,18 +1335,18 @@ export function createResumeLegs(ctx) {
1278
1335
  const minted = out.body;
1279
1336
  return acceptedResumingReceipt(minted.taskId, minted.sessionId);
1280
1337
  }
1281
- async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowParkedResume, ownerLeaseAdmittedFor) {
1338
+ async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowDecision, ownerLeaseAdmittedFor) {
1282
1339
  const cs = deps.checkpointStore;
1283
1340
  const wakeBinding = { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" };
1284
1341
  const token = (await cs.findPendingTokenBySession(sessionId, undefined, wakeBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
1285
1342
  if (!token)
1286
- return { status: 404, body: { error: "no parked checkpoint for this session (nothing to wake)", errorCode: "not_found.parked_checkpoint" } };
1287
- 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);
1288
1345
  if (!operator) {
1289
1346
  const scope = await cs.peekPendingScope(sessionId, wakeBinding).then((s) => (s === undefined ? cs.peekPendingScope(sessionId) : s)).catch(() => undefined);
1290
1347
  const decodedScope = scope != null ? decodeCheckpointScope(scope) : undefined;
1291
1348
  if (decodedScope !== undefined && decodedScope !== caller) {
1292
- 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)" }) };
1293
1350
  }
1294
1351
  }
1295
1352
  let cp;
@@ -1299,22 +1356,22 @@ export function createResumeLegs(ctx) {
1299
1356
  catch (e) {
1300
1357
  if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
1301
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" });
1302
- 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)" }) };
1303
1360
  }
1304
- 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) }) };
1305
1362
  }
1306
1363
  if (!cp) {
1307
1364
  if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
1308
- 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)" }) };
1309
1366
  }
1310
- 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" }) };
1311
1368
  }
1312
1369
  if (!operator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== caller) {
1313
- 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)" }) };
1314
1371
  }
1315
1372
  const gateKind = cp.gate?.kind;
1316
1373
  if (gateKind !== "task_done") {
1317
- 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` }) };
1318
1375
  }
1319
1376
  const trusted = operator;
1320
1377
  if (message !== undefined) {
@@ -1323,7 +1380,7 @@ export function createResumeLegs(ctx) {
1323
1380
  }
1324
1381
  catch (e) {
1325
1382
  if (e instanceof CheckpointError && e.code === "steering.invalid_content") {
1326
- return { status: 422, body: { errorCode: "steering.invalid_content", error: e.message } };
1383
+ return { status: 422, body: refusalBody({ errorCode: "steering.invalid_content", message: e.message }) };
1327
1384
  }
1328
1385
  throw e;
1329
1386
  }
@@ -1332,7 +1389,7 @@ export function createResumeLegs(ctx) {
1332
1389
  if (ctxRead.kind === "corrupt")
1333
1390
  return resumeCtxCorruptAnswer(ctxRead.where);
1334
1391
  if (ctxRead.kind === "absent")
1335
- 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" }) };
1336
1393
  const ctx = ctxRead.ctx;
1337
1394
  const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
1338
1395
  const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
@@ -1352,10 +1409,10 @@ export function createResumeLegs(ctx) {
1352
1409
  verifyRounds: undefined,
1353
1410
  resumeCtx: { sessionId, ctx },
1354
1411
  mintFreshRun: { source: "wake" },
1412
+ ...(workflowDecision !== undefined ? { workflowDecision } : {}),
1355
1413
  ...(engineNotices.length > 0 ? { engineNotices } : {}),
1356
1414
  ...(acceptEarly === true ? { acceptEarly: true } : {}),
1357
1415
  ...(req !== undefined ? { callerInitiated: true } : {}),
1358
- ...(workflowParkedResume !== undefined ? { workflowParkedResume } : {}),
1359
1416
  ...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
1360
1417
  }));
1361
1418
  }
@@ -1370,7 +1427,7 @@ export function createResumeLegs(ctx) {
1370
1427
  const PLAN_REVIEW_GATE_KIND = "plan_review";
1371
1428
  const planReviewBinding = { gateKinds: [PLAN_REVIEW_GATE_KIND] };
1372
1429
  const token = (await cs.findPendingTokenBySession(sessionId, undefined, planReviewBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
1373
- 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)" }) };
1374
1431
  if (!token) {
1375
1432
  log404("no-pending-checkpoint");
1376
1433
  return refuse(noPending);
@@ -1395,13 +1452,13 @@ export function createResumeLegs(ctx) {
1395
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" });
1396
1453
  return refuse(noPending);
1397
1454
  }
1398
- 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) }) });
1399
1456
  }
1400
1457
  if (!cp) {
1401
1458
  log404("checkpoint-missing");
1402
1459
  if (!(await mayDiscloseRowError(cs, token, decider)))
1403
1460
  return refuse(noPending);
1404
- 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" }) });
1405
1462
  }
1406
1463
  if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
1407
1464
  deps.logger?.warn?.("plan_review_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
@@ -1411,17 +1468,17 @@ export function createResumeLegs(ctx) {
1411
1468
  if (gateKind !== PLAN_REVIEW_GATE_KIND) {
1412
1469
  return refuse({
1413
1470
  status: 409,
1414
- body: {
1415
- 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({
1416
1472
  errorCode: "gate_not_plan_review",
1417
- },
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
+ }),
1418
1475
  });
1419
1476
  }
1420
1477
  const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
1421
1478
  if (ctxRead.kind === "corrupt")
1422
1479
  return refuse(resumeCtxCorruptAnswer(ctxRead.where));
1423
1480
  if (ctxRead.kind === "absent")
1424
- 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" }) });
1425
1482
  const replay = storedBodyReplayRefusal(ctxRead.ctx, deps.config);
1426
1483
  if (replay !== undefined)
1427
1484
  return refuse(refuseStoredBodyReplay(sessionId, replay));
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 已退役的 `ServiceDeps` 座 —— 「嵌入方照传旧键」的**唯一判据**(闭集表 + 一只判官)。
3
+ *
4
+ * 病:一个座退役后,类型上它不在了(TypeScript 嵌入方 tsc 红即通告),但类型之外的嵌入方(JS / `as` 硬转)照传旧键 ⇒ 值被装配无声丢掉,
5
+ * 「传了没用」是无声失效 —— 而旧键背后的那道门(受理口的墙、blob 的帽)仍在别处判着,调用方以为自己的值在生效。
6
+ *
7
+ * 律:退役的座在 `createHttpServer` 装配期**响亮拒**,不静默忽略。座的名字只登记在 {@link RETIRED_SERVICE_DEPS} 这一张表里(键 = 旧座名,
8
+ * 值 = 退役版本 + 出路),判官 {@link retiredServiceDepsRefusal} 扫一次摊平后的装配入参(`flattenServiceDeps` 之后:顶层键与各组成员同在,
9
+ * 旧座曾住在 `knobs` 组里,分组形与平铺形同判),命中 ⇒ 交回一句拒话,点名「键 · 退役版本 · 出路」,一次把所有命中的键说全。
10
+ *
11
+ * 「传了」= 自有键在场且值不是 `undefined`(条件装配写 `k: cond ? v : undefined` 的嵌入方什么都没传,不拒)。
12
+ * 一个座新退役时,在表里加一行(同一提交里 CHANGELOG / MIGRATION 的升级条点名它);窗口过后的删表行是单独的一步,不在这里留别名。
13
+ */
14
+ /** 一个退役座的登记:哪一版退的、嵌入方现在该怎么做。 */
15
+ export interface RetiredServiceDep {
16
+ /** 退役所在的版本(拒句里原样说出)。 */
17
+ readonly retiredIn: string;
18
+ /** 出路,接在「was retired in <版本> — 」之后的半句(英文,与起服拒的其余句子同口径)。 */
19
+ readonly wayOut: string;
20
+ }
21
+ /** 闭集:`ServiceDeps` 上已退役的座名。 */
22
+ export declare const RETIRED_SERVICE_DEPS: {
23
+ readonly sqlEngineFacts: {
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";
26
+ };
27
+ readonly snapshotBlobSqlCapBytes: {
28
+ readonly retiredIn: "7.132.0";
29
+ readonly wayOut: "the HTTP surface no longer holds a blob cap of its own — the SQL snapshot store judges the cap on the connection that executes the write; set SNAPSHOT_BLOB_SQL_MAX_BYTES for a fixed cap, and a self-built file-history store answers an oversized putBlob with `{ ok: false, error: { code \"blob_too_large_for_sql\", message, wallSource } }`";
30
+ };
31
+ readonly snapshotBlobSqlCap: {
32
+ readonly retiredIn: "7.133.0";
33
+ readonly wayOut: "the HTTP surface no longer holds a blob cap of its own — the SQL snapshot store judges the cap on the connection that executes the write; set SNAPSHOT_BLOB_SQL_MAX_BYTES for a fixed cap, and a self-built file-history store answers an oversized putBlob with `{ ok: false, error: { code \"blob_too_large_for_sql\", message, wallSource } }`";
34
+ };
35
+ };
36
+ /** 退役座名的闭集。 */
37
+ export type RetiredServiceDepName = keyof typeof RETIRED_SERVICE_DEPS;
38
+ /**
39
+ * 装配入参(已摊平)里有没有传已退役的座。没有 ⇒ `undefined`;有 ⇒ 一句拒话(按表序把所有命中的键说全:键、退役版本、出路)。
40
+ * 调用方原样抛出 —— 判官只判不抛,抛的位置(装配期)在 `createHttpServer`。
41
+ */
42
+ export declare function retiredServiceDepsRefusal(flatDeps: object): string | undefined;
43
+ //# sourceMappingURL=retired-service-deps.d.ts.map
@@ -0,0 +1,18 @@
1
+ const BLOB_CAP_WAY_OUT = "the HTTP surface no longer holds a blob cap of its own — the SQL snapshot store judges the cap on the connection that executes the write; set SNAPSHOT_BLOB_SQL_MAX_BYTES for a fixed cap, and a self-built file-history store answers an oversized putBlob with `{ ok: false, error: { code \"blob_too_large_for_sql\", message, wallSource } }`";
2
+ export const RETIRED_SERVICE_DEPS = {
3
+ sqlEngineFacts: {
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",
6
+ },
7
+ snapshotBlobSqlCapBytes: { retiredIn: "7.132.0", wayOut: BLOB_CAP_WAY_OUT },
8
+ snapshotBlobSqlCap: { retiredIn: "7.133.0", wayOut: BLOB_CAP_WAY_OUT },
9
+ };
10
+ const RETIRED_NAMES = Object.keys(RETIRED_SERVICE_DEPS);
11
+ export function retiredServiceDepsRefusal(flatDeps) {
12
+ const hits = RETIRED_NAMES.filter((name) => Object.hasOwn(flatDeps, name) && flatDeps[name] !== undefined);
13
+ if (hits.length === 0)
14
+ return undefined;
15
+ const said = hits.map((name) => `${name} was retired in ${RETIRED_SERVICE_DEPS[name].retiredIn} — ${RETIRED_SERVICE_DEPS[name].wayOut}`).join("; ");
16
+ return `createHttpServer: ${said}; refusing to start rather than silently ignore a retired ServiceDeps seat (the value would no longer do anything)`;
17
+ }
18
+ //# sourceMappingURL=retired-service-deps.js.map