@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
@@ -11,13 +11,14 @@ import { spaceCommitAlive } from "../workspace-space-commit.js";
11
11
  import { incarnationLogPrefix, redactIncarnations } from "../session-incarnation.js";
12
12
  import { WORKSPACE_CACHE_SPACE_LABEL, workspaceCacheClaimOf, workspaceCacheEnv } from "../workspace-cache.js";
13
13
  import { recordFailOpen } from "../observability/fail-open.js";
14
- import { commandBoundsOf, laneLivenessOf, livenessCutMessage, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
14
+ import { buildLivenessCut, commandBoundsOf, laneLivenessOf, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
15
15
  import { SANDBOX_IPV6_DEFAULT, sandboxEgressGaps, sandboxPodSysctls } from "../sandbox-egress-policy.js";
16
16
  import { FileError, ExecutionError, RemoteExecutionError, withRetry, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
17
17
  import { fileErrorFromExec, classifyFsStderr } from "./remote-env-file-error.js";
18
18
  import { createPosixShellFs } from "./posix-shell-fs.js";
19
19
  import { randomUUID } from "node:crypto";
20
20
  import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
21
+ import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
21
22
  const PROVIDER = "k8s";
22
23
  const K8S_BG_DIR_ROOT = "/tmp/.sema-bg";
23
24
  const K8S_BG_MAX_CONCURRENT = numEnvOr("K8S_BG_MAX_CONCURRENT", 8, 1);
@@ -26,8 +27,8 @@ const K8S_BG_MAX_TIMEOUT_SEC = numEnvOr("K8S_BG_MAX_TIMEOUT_SEC", 1800, K8S_BG_D
26
27
  const K8S_BG_READ_CAP = numEnvOr("K8S_BG_READ_CAP", 1024 * 1024, 64 * 1024);
27
28
  const K8S_BG_FILE_CAP = numEnvOr("K8S_BG_FILE_CAP", 64 * 1024 * 1024, K8S_BG_READ_CAP);
28
29
  const K8S_BG_MEM_CAP = numEnvOr("K8S_BG_MEM_CAP", 8 * 1024 * 1024, 64 * 1024);
29
- import { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, parseBgPollOutput, } from "./k8s-bg-scripts.js";
30
- export { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, parseBgPollOutput, } from "./k8s-bg-scripts.js";
30
+ import { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, buildKillAllScript, KILL_ALL_RECHECK_MS, parseBgPollOutput, } from "./k8s-bg-scripts.js";
31
+ export { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, buildKillAllScript, parseBgPollOutput, } from "./k8s-bg-scripts.js";
31
32
  const ok = (value) => ({ ok: true, value });
32
33
  const unsupported = (op) => ({
33
34
  ok: false,
@@ -116,6 +117,45 @@ class FenceCallAborted extends Error {
116
117
  function dataTimeoutMsOf(config) {
117
118
  return config.dataTimeoutMs ?? WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT;
118
119
  }
120
+ const RPC_TIMEOUT_MS_DEFAULT = 30_000;
121
+ export function rpcTimeoutMsOf(config) {
122
+ return config.rpcTimeoutMs ?? RPC_TIMEOUT_MS_DEFAULT;
123
+ }
124
+ export function boundedStoreCall(op, bound) {
125
+ const signal = bound.abort?.signal;
126
+ return new Promise((resolve, reject) => {
127
+ if (signal?.aborted)
128
+ return reject(bound.abort.error());
129
+ const settle = () => {
130
+ clearTimeout(timer);
131
+ signal?.removeEventListener("abort", onAbort);
132
+ };
133
+ const onAbort = () => {
134
+ settle();
135
+ reject(bound.abort.error());
136
+ };
137
+ const timer = setTimeout(() => {
138
+ settle();
139
+ reject(bound.onTimeout());
140
+ }, bound.ms);
141
+ signal?.addEventListener("abort", onAbort, { once: true });
142
+ let pending;
143
+ try {
144
+ pending = op();
145
+ }
146
+ catch (e) {
147
+ settle();
148
+ return reject(e);
149
+ }
150
+ pending.then((v) => {
151
+ settle();
152
+ resolve(v);
153
+ }, (e) => {
154
+ settle();
155
+ reject(e);
156
+ });
157
+ });
158
+ }
119
159
  import { execSocketTlsOptions, exitCodeFromStatus } from "./k8s-exec-protocol.js";
120
160
  export { execSocketTlsOptions } from "./k8s-exec-protocol.js";
121
161
  export function errorMessageOf(e) {
@@ -134,10 +174,14 @@ export class RemoteK8sExecutionEnv {
134
174
  podIP;
135
175
  destroyed = false;
136
176
  handleSnapshotId;
137
- inFlightExecs = 0;
138
- connectPromise;
139
- restoreRefusal;
140
- homeDir;
177
+ inFlight = new Set();
178
+ idleWaiters = [];
179
+ connectInFlight;
180
+ connectRefusal;
181
+ get homeDir() {
182
+ return this.homeSeat.home;
183
+ }
184
+ homeSeat;
141
185
  cdPath = "unknown";
142
186
  cfg;
143
187
  deps;
@@ -150,6 +194,7 @@ export class RemoteK8sExecutionEnv {
150
194
  cacheUnavailableSaid = false;
151
195
  logger;
152
196
  onWorkspaceSnapshot;
197
+ cacheMisses;
153
198
  constructor(config, deps) {
154
199
  this.cfg = {
155
200
  apiUrl: config.apiUrl,
@@ -162,7 +207,7 @@ export class RemoteK8sExecutionEnv {
162
207
  mountPath: config.mountPath ?? K8S_WORKSPACE_MOUNT_DEFAULT,
163
208
  timeoutMs: config.timeoutMs ?? 30 * 60_000,
164
209
  readyTimeoutMs: config.readyTimeoutMs ?? 120_000,
165
- rpcTimeoutMs: config.rpcTimeoutMs ?? 30_000,
210
+ rpcTimeoutMs: rpcTimeoutMsOf(config),
166
211
  livenessMs: config.livenessMs ?? LIVENESS_MS_DEFAULT,
167
212
  dataTimeoutMs: dataTimeoutMsOf(config),
168
213
  cpu: config.cpu ?? "1",
@@ -176,7 +221,7 @@ export class RemoteK8sExecutionEnv {
176
221
  ...(config.snapshotExclude && config.snapshotExclude.length > 0 ? { snapshotExclude: config.snapshotExclude } : {}),
177
222
  };
178
223
  this.cwd = this.cfg.mountPath;
179
- this.homeDir = config.homeDir;
224
+ this.homeSeat = new LaneHomeSeat({ lane: PROVIDER, declared: config.homeDir, verifiesDeclared: true, onAbsent: config.onHomeAbsent });
180
225
  this.deps = deps ?? {};
181
226
  this.laneLiveness = laneLivenessOf(this.cfg.livenessMs, "K8S_LIVENESS_MS");
182
227
  const occupant = occupantKeyOf(config.spaceOccupant);
@@ -185,6 +230,7 @@ export class RemoteK8sExecutionEnv {
185
230
  this.cacheLane = cacheLaneOf(config.cache, config.spaceOccupant);
186
231
  this.logger = config.logger;
187
232
  this.onWorkspaceSnapshot = config.onWorkspaceSnapshot;
233
+ this.cacheMisses = config.cacheMisses;
188
234
  this.capabilities = { isolation: true, suspendable: !!this.cfg.s3Snapshot, canonicalPathAuthoritative: true };
189
235
  const bgMax = Math.max(1, Math.min(K8S_BG_MAX_TIMEOUT_SEC, Math.floor(this.cfg.timeoutMs / 1000)));
190
236
  this.backgroundCapabilities = {
@@ -283,7 +329,15 @@ export class RemoteK8sExecutionEnv {
283
329
  ...(url.startsWith("wss:") ? execSocketTlsOptions(ca, this.cfg.insecureTls) : {}),
284
330
  });
285
331
  }
286
- async connect(config) {
332
+ connect(config) {
333
+ this.connectInFlight ??= this.establish(config).finally(() => {
334
+ this.connectInFlight = undefined;
335
+ });
336
+ return this.connectInFlight;
337
+ }
338
+ async establish(config) {
339
+ if (this.connectRefusal)
340
+ return this.connectRefusal;
287
341
  const signal = config?.abortSignal;
288
342
  const lane = this.spaceLane;
289
343
  switch (lane.state) {
@@ -295,8 +349,6 @@ export class RemoteK8sExecutionEnv {
295
349
  case "unreadable":
296
350
  return this.provision(signal);
297
351
  case "open": {
298
- if (this.restoreRefusal)
299
- return this.restoreRefusal;
300
352
  const settled = await this.awaitSpaceCommit(lane.space, signal);
301
353
  if (!settled.ok)
302
354
  return settled;
@@ -322,27 +374,10 @@ export class RemoteK8sExecutionEnv {
322
374
  await this.fenceCall(() => space.fence.end(), "delete").catch(() => undefined);
323
375
  }
324
376
  fenceCall(op, what, signal) {
325
- return new Promise((resolve, reject) => {
326
- if (signal?.aborted)
327
- return reject(new FenceCallAborted());
328
- const onAbort = () => {
329
- clearTimeout(timer);
330
- reject(new FenceCallAborted());
331
- };
332
- const timer = setTimeout(() => {
333
- signal?.removeEventListener("abort", onAbort);
334
- reject(new Error(`workspace space commit state ${what} did not answer within ${this.cfg.rpcTimeoutMs}ms`));
335
- }, this.cfg.rpcTimeoutMs);
336
- signal?.addEventListener("abort", onAbort, { once: true });
337
- op().then((v) => {
338
- clearTimeout(timer);
339
- signal?.removeEventListener("abort", onAbort);
340
- resolve(v);
341
- }, (e) => {
342
- clearTimeout(timer);
343
- signal?.removeEventListener("abort", onAbort);
344
- reject(e);
345
- });
377
+ return boundedStoreCall(op, {
378
+ ms: this.cfg.rpcTimeoutMs,
379
+ onTimeout: () => new Error(`workspace space commit state ${what} did not answer within ${this.cfg.rpcTimeoutMs}ms`),
380
+ ...(signal ? { abort: { signal, error: () => new FenceCallAborted() } } : {}),
346
381
  });
347
382
  }
348
383
  sayUnavailable(reason) {
@@ -355,6 +390,21 @@ export class RemoteK8sExecutionEnv {
355
390
  effect: "WORKSPACE_PERSIST=space, but the session store has no occupant (incarnation) for this run's session — its /workspace is neither restored nor saved (it is never keyed by the session id instead)",
356
391
  });
357
392
  }
393
+ holdRefusal(error) {
394
+ this.connectRefusal = { ok: false, error };
395
+ return this.connectRefusal;
396
+ }
397
+ refusePod(pod, phase, reason, message) {
398
+ this.logger?.warn("sandbox_pod_refused", {
399
+ pod,
400
+ phase,
401
+ reason: reason ?? "?",
402
+ ...(message ? { detail: message.slice(0, 300) } : {}),
403
+ ...(this.cfg.sessionId ? { sessionId: this.cfg.sessionId } : {}),
404
+ effect: "the cluster created this run's sandbox pod and it ended before it ran — this run's operations answer the same refusal without creating another pod; a retry of the run builds a fresh sandbox",
405
+ });
406
+ return this.holdRefusal(new RemoteExecutionError("connect_failed", `pod entered ${phase} before Running (${reason ?? "?"}) — the cluster refused this run's sandbox pod; the refusal stands for the run (no pod is created again), a retry of the run asks the cluster afresh`));
407
+ }
358
408
  refuseUnreadableOccupant(code) {
359
409
  const ways = [...(this.spaceLane.state === "unreadable" ? ["WORKSPACE_PERSIST=off"] : []), ...(this.cacheLane.state === "unreadable" ? ["WORKSPACE_CACHE=off"] : [])].join(" and ");
360
410
  if (!this.unreadableOccupantSaid) {
@@ -374,6 +424,8 @@ export class RemoteK8sExecutionEnv {
374
424
  provision(signal) {
375
425
  const space = this.spaceLane;
376
426
  const cache = this.cacheLane;
427
+ if (this.connectRefusal)
428
+ return Promise.resolve(this.connectRefusal);
377
429
  if (space.state === "unreadable")
378
430
  return Promise.resolve(this.refuseUnreadableOccupant(space.code));
379
431
  if (cache.state === "unreadable")
@@ -384,19 +436,23 @@ export class RemoteK8sExecutionEnv {
384
436
  backoffMs: (failed) => this.cfg.retry.backoffMs * failed,
385
437
  };
386
438
  const once = this.deps.connectOnce ?? ((s) => this.connectOnce(cache, s));
387
- return withRetry(() => once(signal), policy, { signal });
439
+ const refused = new AbortController();
440
+ const halt = signal ? AbortSignal.any([signal, refused.signal]) : refused.signal;
441
+ return withRetry(async () => {
442
+ const r = await once(signal);
443
+ if (!r.ok && this.connectRefusal)
444
+ refused.abort();
445
+ return r;
446
+ }, policy, { signal: halt });
388
447
  }
389
448
  async ensureConnected() {
390
- if (this.podName)
391
- return ok(undefined);
392
449
  if (this.destroyed)
393
450
  return { ok: false, error: new ExecutionError("shell_unavailable", "k8s sandbox destroyed") };
394
- this.connectPromise ??= this.connect();
395
- const r = await this.connectPromise;
396
- if (!r.ok) {
397
- this.connectPromise = undefined;
451
+ if (this.podName && this.connectInFlight === undefined)
452
+ return ok(undefined);
453
+ const r = await this.connect();
454
+ if (!r.ok)
398
455
  return { ok: false, error: new ExecutionError("shell_unavailable", `k8s sandbox connect failed: ${r.error.message}`, r.error) };
399
- }
400
456
  return ok(undefined);
401
457
  }
402
458
  async connectOnce(cacheLane, signal) {
@@ -409,6 +465,8 @@ export class RemoteK8sExecutionEnv {
409
465
  if (!("cacheMiss" in first))
410
466
  return first;
411
467
  this.cacheUnavailable(first.cacheMiss, first.detail, cache);
468
+ if (cache)
469
+ this.cacheMisses?.remember(cache.spaceKey, { reason: first.cacheMiss, detail: first.detail }, this.cfg.dataTimeoutMs);
412
470
  const second = await this.createPod(signal, undefined);
413
471
  return "cacheMiss" in second ? { ok: false, error: new RemoteExecutionError("connect_failed", "unreachable: a pod without the cache volume was given up for it") } : second;
414
472
  }
@@ -420,6 +478,11 @@ export class RemoteK8sExecutionEnv {
420
478
  this.cacheUnavailable(lane.reason, "the session store has no occupant (incarnation) for this run's session — no space key to find its cache volume by (never keyed by the session id instead)");
421
479
  return undefined;
422
480
  case "open": {
481
+ const remembered = this.cacheMisses?.recall(lane.cache.spaceKey);
482
+ if (remembered !== undefined) {
483
+ this.cacheUnavailable(remembered.reason, `${remembered.detail} — remembered from an earlier run of this space; not asked for again within ${this.cfg.dataTimeoutMs}ms`, lane.cache);
484
+ return undefined;
485
+ }
423
486
  const ensured = await this.ensureCacheClaim(lane.cache, signal);
424
487
  if (!ensured.ok) {
425
488
  this.cacheUnavailable("claim_failed", ensured.detail, lane.cache);
@@ -586,7 +649,7 @@ export class RemoteK8sExecutionEnv {
586
649
  }
587
650
  if (phase === "Failed" || phase === "Succeeded") {
588
651
  await this.deletePod(name).catch(() => { });
589
- return { ok: false, error: new RemoteExecutionError("connect_failed", `pod entered ${phase} before Running (${status?.reason ?? "?"})`) };
652
+ return this.refusePod(name, phase, status?.reason, status?.message);
590
653
  }
591
654
  const waiting = status?.containerStatuses?.[0]?.state?.waiting;
592
655
  if (waiting?.reason === "ImagePullBackOff" || waiting?.reason === "ErrImagePull") {
@@ -616,13 +679,24 @@ export class RemoteK8sExecutionEnv {
616
679
  }
617
680
  this.podName = name;
618
681
  this.podIP = podIP;
619
- const mk = await this.runExec(`mkdir -p ${shellQuote(this.cfg.mountPath)}`);
682
+ const askHome = this.homeSeat.asks;
683
+ const mk = await this.runExec(`mkdir -p ${shellQuote(this.cfg.mountPath)}${askHome ? ` && { ${LANE_HOME_PROBE_COMMAND} || true; }` : ""}`);
620
684
  if (!mk.ok || mk.value.exitCode !== 0) {
621
685
  await this.deletePod(name).catch(() => { });
622
686
  this.podName = undefined;
623
687
  this.podIP = undefined;
624
688
  return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace mkdir failed: ${mk.ok ? mk.value.stderr : mk.error.message}`) };
625
689
  }
690
+ if (askHome) {
691
+ const verdict = this.homeSeat.settle({ ok: true, stdout: mk.value.stdout });
692
+ if (verdict !== undefined) {
693
+ await this.deletePod(name).catch(() => { });
694
+ this.podName = undefined;
695
+ this.podIP = undefined;
696
+ const error = new RemoteExecutionError("connect_failed", verdict.refusal);
697
+ return verdict.answered ? this.holdRefusal(error) : { ok: false, error };
698
+ }
699
+ }
626
700
  if (cache?.fresh)
627
701
  this.logger?.warn("workspace_cache_fresh", { space: incarnationLogPrefix(cache.spaceKey), claim: cache.claim.name, size: cache.size });
628
702
  return ok(this.workspaceHandle());
@@ -864,12 +938,15 @@ export class RemoteK8sExecutionEnv {
864
938
  await this.endSpace(this.spaceLane.space);
865
939
  return r;
866
940
  }
867
- async uploadSnapshot(target) {
868
- if (this.inFlightExecs > 0)
941
+ async uploadSnapshot(target, boundMs = this.cfg.dataTimeoutMs) {
942
+ if (target.lane === "park" && this.inFlight.size > 0)
869
943
  return { ok: false, error: new RemoteExecutionError("command_in_flight", "cannot suspend while a command is in flight (would snapshot a torn workspace)") };
870
944
  const snapshotId = mintSnapshotId(this.occupantIncarnation);
871
- if (!this.podName)
872
- return ok(snapshotId);
945
+ if (!this.podName) {
946
+ return target.lane === "space"
947
+ ? { ok: false, error: new RemoteExecutionError("connect_failed", "the sandbox pod was gone when the run-end snapshot reached the upload — nothing was saved (the workspace could not be read), and `latest` keeps naming the previous snapshot") }
948
+ : ok(snapshotId);
949
+ }
873
950
  const presigned = this.presignSnapshot({ ...target, method: "PUT", snapshotId });
874
951
  if (!presigned.ok)
875
952
  return presigned;
@@ -878,7 +955,7 @@ export class RemoteK8sExecutionEnv {
878
955
  const excludes = (this.cfg.snapshotExclude ?? []).map((item) => ` --exclude=${shellQuote(`./${item}`)}`).join("");
879
956
  const cmd = `tar czf ${tmp}${excludes} -C ${shellQuote(this.cfg.mountPath)} . && curl -fsS -X PUT -T ${tmp} ${shellQuote(url)}; ` +
880
957
  `rc=$?; rm -f ${tmp}; exit $rc`;
881
- const r = await this.runExec(cmd, { timeoutMs: this.cfg.dataTimeoutMs });
958
+ const r = await this.runExec(cmd, { timeoutMs: boundMs });
882
959
  if (!r.ok)
883
960
  return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace snapshot upload failed: ${this.redactUrl(r.error.message, url)}`, r.error) };
884
961
  if (r.value.exitCode !== 0)
@@ -941,13 +1018,9 @@ export class RemoteK8sExecutionEnv {
941
1018
  case "space": {
942
1019
  const tries = attempt === 1 ? "1 attempt" : `${attempt} attempts`;
943
1020
  this.spaceLine("workspace_restore_failed", target.space, { snapshotId, class: failure.cls, attempts: attempt, boundMs: this.cfg.dataTimeoutMs, error: failure.detail });
944
- this.restoreRefusal = {
945
- ok: false,
946
- error: new RemoteExecutionError("connect_failed", `${failure.detail} — the workspace's latest snapshot (${snapshotId}) is in the store but did not come back (${failure.cls}, ${tries}, bound ${this.cfg.dataTimeoutMs}ms each), ` +
947
- "so this run does not start on an empty /workspace. Ways out: raise WORKSPACE_SNAPSHOT_TIMEOUT_MS if the archive is large, " +
948
- "or set WORKSPACE_PERSIST=off for one run (it starts from an empty /workspace and deletes no snapshot)", failure.cause),
949
- };
950
- return this.restoreRefusal;
1021
+ return this.holdRefusal(new RemoteExecutionError("connect_failed", `${failure.detail} — the workspace's latest snapshot (${snapshotId}) is in the store but did not come back (${failure.cls}, ${tries}, bound ${this.cfg.dataTimeoutMs}ms each), ` +
1022
+ "so this run does not start on an empty /workspace. Ways out: raise WORKSPACE_SNAPSHOT_TIMEOUT_MS if the archive is large, " +
1023
+ "or set WORKSPACE_PERSIST=off for one run (it starts from an empty /workspace and deletes no snapshot)", failure.cause));
951
1024
  }
952
1025
  default: {
953
1026
  const unreachable = target;
@@ -987,6 +1060,8 @@ export class RemoteK8sExecutionEnv {
987
1060
  return ok(undefined);
988
1061
  }
989
1062
  async reconnect(_sessionToken) {
1063
+ if (this.connectInFlight)
1064
+ return this.connectInFlight;
990
1065
  if (this.podName) {
991
1066
  const alive = await this.request("GET", `/api/v1/namespaces/${this.cfg.namespace}/pods/${this.podName}`).catch(() => undefined);
992
1067
  const phase = alive?.json?.status?.phase;
@@ -1032,6 +1107,63 @@ export class RemoteK8sExecutionEnv {
1032
1107
  async cleanup() {
1033
1108
  return this.destroy();
1034
1109
  }
1110
+ beginExec() {
1111
+ const ac = new AbortController();
1112
+ this.inFlight.add(ac);
1113
+ return ac;
1114
+ }
1115
+ endExec(ac) {
1116
+ this.inFlight.delete(ac);
1117
+ if (this.inFlight.size > 0)
1118
+ return;
1119
+ for (const wake of this.idleWaiters.splice(0))
1120
+ wake();
1121
+ }
1122
+ whenIdle(ms) {
1123
+ if (this.inFlight.size === 0)
1124
+ return Promise.resolve(true);
1125
+ return new Promise((resolve) => {
1126
+ const idle = () => {
1127
+ clearTimeout(timer);
1128
+ resolve(true);
1129
+ };
1130
+ const timer = setTimeout(() => {
1131
+ this.idleWaiters = this.idleWaiters.filter((w) => w !== idle);
1132
+ resolve(false);
1133
+ }, ms);
1134
+ this.idleWaiters.push(idle);
1135
+ });
1136
+ }
1137
+ async quiesce(space) {
1138
+ const inFlight = this.inFlight.size;
1139
+ if (inFlight === 0 && !this.podName)
1140
+ return { ok: true, waitedMs: 0 };
1141
+ const startedAt = Date.now();
1142
+ const ended = await this.whenIdle(this.cfg.rpcTimeoutMs);
1143
+ const killed = await this.killPodProcesses();
1144
+ for (const ac of [...this.inFlight])
1145
+ ac.abort();
1146
+ const waitedMs = Date.now() - startedAt;
1147
+ if (!ended) {
1148
+ this.spaceLine("workspace_snapshot_commands_cut", space, {
1149
+ inFlight,
1150
+ waitedMs,
1151
+ killed,
1152
+ effect: killed
1153
+ ? "the run is over but its commands were still running after the wait — their processes were killed (the sandbox is deleted right after) so the run-end snapshot reads a still tree; the workspace is saved as it stood"
1154
+ : "the run is over but its commands were still running after the wait and the pod's processes could not be confirmed stopped — the workspace is NOT saved (it may still be changing); `latest` keeps naming the previous snapshot",
1155
+ });
1156
+ }
1157
+ return killed
1158
+ ? { ok: true, waitedMs }
1159
+ : { ok: false, error: new RemoteExecutionError("command_in_flight", "the pod's processes could not be confirmed stopped when the run ended — not saving a workspace that may still be changing") };
1160
+ }
1161
+ async killPodProcesses() {
1162
+ if (!this.podName)
1163
+ return false;
1164
+ const r = await this.runExec(buildKillAllScript(), { timeoutMs: this.cfg.rpcTimeoutMs + KILL_ALL_RECHECK_MS });
1165
+ return r.ok && r.value.exitCode === 0;
1166
+ }
1035
1167
  async persistWorkspace(space) {
1036
1168
  const failed = (stage, error) => {
1037
1169
  this.spaceLine("workspace_snapshot_failed", space, { stage, code: error.code, error: error.message });
@@ -1052,7 +1184,10 @@ export class RemoteK8sExecutionEnv {
1052
1184
  try {
1053
1185
  let snap;
1054
1186
  try {
1055
- snap = await this.uploadSnapshot({ lane: "space", space });
1187
+ const quiet = await this.quiesce(space);
1188
+ if (!quiet.ok)
1189
+ return failed("upload", quiet.error);
1190
+ snap = await this.uploadSnapshot({ lane: "space", space }, Math.max(1, this.cfg.dataTimeoutMs - quiet.waitedMs));
1056
1191
  }
1057
1192
  catch (e) {
1058
1193
  return failed("upload", new RemoteExecutionError("unknown", errorMessageOf(e)));
@@ -1142,7 +1277,8 @@ export class RemoteK8sExecutionEnv {
1142
1277
  clearTimeout(livenessTimer);
1143
1278
  const idle = Date.now() - lastProgress;
1144
1279
  if (idle >= liveness.ms) {
1145
- finish({ ok: false, error: new ExecutionError("timeout", livenessCutMessage(liveness)) });
1280
+ const cut = buildLivenessCut(liveness);
1281
+ finish({ ok: false, error: new ExecutionError(cut.code, cut.message, undefined, undefined, cut.noOutputSec) });
1146
1282
  return;
1147
1283
  }
1148
1284
  livenessTimer = setTimeout(armLiveness, liveness.ms - idle);
@@ -1276,11 +1412,12 @@ export class RemoteK8sExecutionEnv {
1276
1412
  if (!c.ok)
1277
1413
  return c;
1278
1414
  const detachSignal = options?.detachSignal;
1279
- this.inFlightExecs++;
1415
+ const inflight = this.beginExec();
1416
+ const abortSignal = options?.abortSignal ? AbortSignal.any([options.abortSignal, inflight.signal]) : inflight.signal;
1280
1417
  try {
1281
1418
  if (!detachSignal) {
1282
1419
  return call.settle(await this.runExec(call.command, {
1283
- signal: options?.abortSignal,
1420
+ signal: abortSignal,
1284
1421
  timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
1285
1422
  boundOutput: true,
1286
1423
  onStdout: options?.onStdout,
@@ -1292,7 +1429,7 @@ export class RemoteK8sExecutionEnv {
1292
1429
  const dir = `${K8S_BG_DIR_ROOT}/exec-${adoptId}`;
1293
1430
  const tee = { out: seedMemStream(""), err: seedMemStream("") };
1294
1431
  return call.settle(await this.runExec(buildDetachCapableExec(dir, call.command), {
1295
- signal: options?.abortSignal,
1432
+ signal: abortSignal,
1296
1433
  timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
1297
1434
  boundOutput: true,
1298
1435
  onStdout: options?.onStdout,
@@ -1316,7 +1453,7 @@ export class RemoteK8sExecutionEnv {
1316
1453
  }));
1317
1454
  }
1318
1455
  finally {
1319
- this.inFlightExecs--;
1456
+ this.endExec(inflight);
1320
1457
  }
1321
1458
  }
1322
1459
  async getHost(port) {
@@ -1337,7 +1474,7 @@ export class RemoteK8sExecutionEnv {
1337
1474
  const c = await this.ensureConnected();
1338
1475
  if (!c.ok)
1339
1476
  throw new RemoteExecutionError("connect_failed", c.error.message, c.error);
1340
- this.inFlightExecs++;
1477
+ const ac = this.beginExec();
1341
1478
  const queue = [];
1342
1479
  let finished = false;
1343
1480
  let failure;
@@ -1350,7 +1487,6 @@ export class RemoteK8sExecutionEnv {
1350
1487
  };
1351
1488
  const cap = options?.maxOutputBytes;
1352
1489
  let bytes = 0;
1353
- const ac = new AbortController();
1354
1490
  const signal = options?.signal ? AbortSignal.any([options.signal, ac.signal]) : ac.signal;
1355
1491
  const done = this.runExec(buildCwdEnvCommand(command, this.workingDirOf(options?.cwd), options?.env, "raw").command, {
1356
1492
  signal,
@@ -1375,9 +1511,11 @@ export class RemoteK8sExecutionEnv {
1375
1511
  ? "transport_lost"
1376
1512
  : r.error.code === "timeout"
1377
1513
  ? "timeout"
1378
- : r.error.code === "spawn_error" || r.error.code === "shell_unavailable"
1379
- ? "unknown"
1380
- : "connect_failed";
1514
+ : r.error.code === "liveness_no_output"
1515
+ ? "liveness_no_output"
1516
+ : r.error.code === "spawn_error" || r.error.code === "shell_unavailable"
1517
+ ? "unknown"
1518
+ : "connect_failed";
1381
1519
  failure = new RemoteExecutionError(code, r.error.message, r.error);
1382
1520
  }
1383
1521
  finished = true;
@@ -1409,7 +1547,7 @@ export class RemoteK8sExecutionEnv {
1409
1547
  finally {
1410
1548
  ac.abort();
1411
1549
  await done.catch(() => { });
1412
- this.inFlightExecs--;
1550
+ this.endExec(ac);
1413
1551
  }
1414
1552
  }
1415
1553
  backgroundCapabilities;
@@ -1626,42 +1764,97 @@ export class RemoteK8sExecutionEnv {
1626
1764
  return dir ? this.resolve(dir) : undefined;
1627
1765
  }
1628
1766
  }
1767
+ const SPACE_RECLAIM_CONCURRENCY = 4;
1768
+ function createGate(limit) {
1769
+ let active = 0;
1770
+ const waiting = [];
1771
+ const release = () => {
1772
+ const next = waiting.shift();
1773
+ if (next)
1774
+ next();
1775
+ else
1776
+ active--;
1777
+ };
1778
+ return async (task) => {
1779
+ if (active < limit)
1780
+ active++;
1781
+ else
1782
+ await new Promise((resolve) => waiting.push(resolve));
1783
+ try {
1784
+ return await task();
1785
+ }
1786
+ finally {
1787
+ release();
1788
+ }
1789
+ };
1790
+ }
1791
+ function tallyFailure(classes, key, at, pos, sample) {
1792
+ const seen = classes.get(key);
1793
+ if (seen === undefined)
1794
+ classes.set(key, { count: 1, at, pos, sample });
1795
+ else {
1796
+ seen.count++;
1797
+ if (at < seen.at || (at === seen.at && pos < seen.pos)) {
1798
+ seen.at = at;
1799
+ seen.pos = pos;
1800
+ seen.sample = sample;
1801
+ }
1802
+ }
1803
+ }
1804
+ function failureClassesInOrder(classes) {
1805
+ return [...classes.values()].sort((a, b) => a.at - b.at || a.pos - b.pos);
1806
+ }
1629
1807
  export function createSpaceReclaimer(config, deps) {
1808
+ const gate = createGate(SPACE_RECLAIM_CONCURRENCY);
1630
1809
  return {
1631
1810
  async reclaim(spaces) {
1632
1811
  const deadline = AbortSignal.timeout(dataTimeoutMsOf(config));
1633
- for (const { sessionId, spaceKey } of spaces) {
1634
- if (spaceKey === "")
1635
- continue;
1636
- const env = new RemoteK8sExecutionEnv({ ...config, sessionId }, deps);
1637
- if (config.cache !== undefined) {
1638
- const r = await env.reclaimSpaceCache(spaceKey, deadline).catch((e) => ({ ok: false, reason: "delete_failed", detail: errorMessageOf(e) }));
1639
- if (!r.ok) {
1640
- recordFailOpen("server.workspace-cache.reclaim-failed", `reason=${r.reason} label=${workspaceCacheClaimOf(spaceKey).label}`);
1641
- config.logger?.warn("workspace_cache_reclaim_failed", {
1642
- reason: r.reason,
1643
- space: incarnationLogPrefix(spaceKey),
1644
- label: workspaceCacheClaimOf(spaceKey).label,
1645
- error: redactIncarnations(r.detail),
1646
- effect: `the ended space's cache volume stays — delete it by label ${WORKSPACE_CACHE_SPACE_LABEL}=<label> (the deletion it belongs to went ahead)`,
1647
- });
1812
+ const volumeFailures = new Map();
1813
+ const objectFailures = new Map();
1814
+ const settled = await Promise.allSettled(spaces.map(({ sessionId, spaceKey }, at) => spaceKey === ""
1815
+ ? Promise.resolve()
1816
+ : gate(async () => {
1817
+ const env = new RemoteK8sExecutionEnv({ ...config, sessionId }, deps);
1818
+ if (config.cache !== undefined) {
1819
+ const r = await env.reclaimSpaceCache(spaceKey, deadline).catch((e) => ({ ok: false, reason: "delete_failed", detail: errorMessageOf(e) }));
1820
+ if (!r.ok) {
1821
+ recordFailOpen("server.workspace-cache.reclaim-failed", `reason=${r.reason} label=${workspaceCacheClaimOf(spaceKey).label}`);
1822
+ tallyFailure(volumeFailures, r.reason, at, 0, { spaceKey, failure: r });
1823
+ }
1648
1824
  }
1649
- }
1650
- if (config.s3Snapshot !== undefined) {
1651
- const failures = await env.reclaimSpaceSnapshots(spaceKey, deadline).catch((e) => [{ lane: "space", stage: "delete", detail: errorMessageOf(e) }]);
1652
- for (const f of failures) {
1653
- recordFailOpen("server.workspace-space.reclaim-failed", `lane=${f.lane} stage=${f.stage}`);
1654
- config.logger?.warn("workspace_space_reclaim_failed", {
1655
- space: incarnationLogPrefix(spaceKey),
1656
- sessionId,
1657
- lane: f.lane,
1658
- stage: f.stage,
1659
- error: redactIncarnations(f.detail),
1660
- effect: `the ended space's ${f.lane === "space" ? "workspace snapshots" : "park snapshots"} may stay in the bucket (unreadable through the API; the bucket's lifecycle rule is the fallback) — the deletion it belongs to went ahead`,
1825
+ if (config.s3Snapshot !== undefined) {
1826
+ const failures = await env.reclaimSpaceSnapshots(spaceKey, deadline).catch((e) => [{ lane: "space", stage: "delete", detail: errorMessageOf(e) }]);
1827
+ failures.forEach((f, pos) => {
1828
+ recordFailOpen("server.workspace-space.reclaim-failed", `lane=${f.lane} stage=${f.stage}`);
1829
+ tallyFailure(objectFailures, `${f.lane}/${f.stage}`, at, pos, { sessionId, spaceKey, failure: f });
1661
1830
  });
1662
1831
  }
1663
- }
1832
+ })));
1833
+ for (const { count, sample } of failureClassesInOrder(volumeFailures)) {
1834
+ config.logger?.warn("workspace_cache_reclaim_failed", {
1835
+ reason: sample.failure.reason,
1836
+ count,
1837
+ space: incarnationLogPrefix(sample.spaceKey),
1838
+ label: workspaceCacheClaimOf(sample.spaceKey).label,
1839
+ error: redactIncarnations(sample.failure.detail),
1840
+ effect: `the ended space's cache volume stays — delete it by label ${WORKSPACE_CACHE_SPACE_LABEL}=<label> (the deletion it belongs to went ahead)${count > 1 ? `; ${count} spaces of this call met the same failure, the line names the first` : ""}`,
1841
+ });
1842
+ }
1843
+ for (const { count, sample } of failureClassesInOrder(objectFailures)) {
1844
+ const { sessionId, spaceKey, failure: f } = sample;
1845
+ config.logger?.warn("workspace_space_reclaim_failed", {
1846
+ space: incarnationLogPrefix(spaceKey),
1847
+ sessionId,
1848
+ lane: f.lane,
1849
+ stage: f.stage,
1850
+ count,
1851
+ error: redactIncarnations(f.detail),
1852
+ effect: `the ended space's ${f.lane === "space" ? "workspace snapshots" : "park snapshots"} may stay in the bucket (unreadable through the API; the bucket's lifecycle rule is the fallback) — the deletion it belongs to went ahead${count > 1 ? `; ${count} spaces of this call met the same failure, the line names the first` : ""}`,
1853
+ });
1664
1854
  }
1855
+ const threw = settled.find((r) => r.status === "rejected");
1856
+ if (threw)
1857
+ throw threw.reason;
1665
1858
  },
1666
1859
  };
1667
1860
  }