@sema-agent/server 7.147.0 → 7.148.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 (725) hide show
  1. package/MIGRATION.md +1039 -24
  2. package/README.md +20 -7
  3. package/README.zh-CN.md +14 -8
  4. package/USAGE.md +380 -163
  5. package/deploy/sema-up/README.md +16 -5
  6. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  7. package/deploy/sema-up/chart/templates/_helpers.tpl +20 -4
  8. package/deploy/sema-up/chart/values.yaml +3 -2
  9. package/deploy/sema-up/cluster-up.sh +74 -176
  10. package/deploy/sema-up/kube-up.sh +29 -49
  11. package/deploy/sema-up/lib/chart-run.sh +23 -0
  12. package/deploy/sema-up/lib/dataroot.sh +3 -3
  13. package/deploy/sema-up/lib/health.sh +62 -13
  14. package/deploy/sema-up/lib/helm-values-cluster.py +18 -0
  15. package/deploy/sema-up/lib/helm-values-kube.py +35 -0
  16. package/deploy/sema-up/lib/regcred.py +8 -0
  17. package/deploy/sema-up/lib/remote/helm-install.sh +3 -0
  18. package/deploy/sema-up/lib/remote/image-import.sh +6 -0
  19. package/deploy/sema-up/lib/remote/machine-precheck.sh +11 -0
  20. package/deploy/sema-up/lib/remote/region-probe.sh +3 -0
  21. package/deploy/sema-up/lib/remote/stack-ready.sh +26 -0
  22. package/deploy/sema-up/lib/remote/vxlan-guard.sh +8 -0
  23. package/deploy/sema-up/lib/render-stack.py +17 -0
  24. package/deploy/sema-up/lib/shell-text.sh +87 -0
  25. package/deploy/sema-up/lib/usage-flags.txt +10 -0
  26. package/deploy/sema-up/lib/usage-head.txt +1 -0
  27. package/deploy/sema-up/lib/usage-tail.txt +13 -0
  28. package/deploy/sema-up/preflight.sh +10 -8
  29. package/deploy/sema-up/sema-up.sh +14 -39
  30. package/deploy/sema-up/smoke.sh +7 -6
  31. package/deploy/sema-up/stack/compose.dataroot.yaml +3 -0
  32. package/deploy/sema-up/stack/compose.yaml +13 -1
  33. package/deploy/sema-up/stack/sema-stack-cluster.yaml +21 -0
  34. package/dist/adoption/plan.d.ts +24 -0
  35. package/dist/adoption/plan.js +53 -1
  36. package/dist/adoption/runner.d.ts +10 -1
  37. package/dist/adoption/runner.js +36 -16
  38. package/dist/adoption/wire.d.ts +3 -3
  39. package/dist/approval-card.d.ts +28 -30
  40. package/dist/approval-gate-currency.js +3 -2
  41. package/dist/approval-reconciler.d.ts +4 -13
  42. package/dist/approval-reconciler.js +2 -2
  43. package/dist/approval.d.ts +13 -11
  44. package/dist/approval.js +3 -3
  45. package/dist/audit.d.ts +10 -3
  46. package/dist/audit.js +15 -7
  47. package/dist/auth-bridge.d.ts +0 -14
  48. package/dist/auth-bridge.js +0 -7
  49. package/dist/auto-mode-face.d.ts +10 -7
  50. package/dist/auto-mode-face.js +7 -4
  51. package/dist/background-agent-name.d.ts +19 -1
  52. package/dist/background-agent-name.js +12 -2
  53. package/dist/background-session-reap.d.ts +1 -1
  54. package/dist/bake-runner/main.js +9 -6
  55. package/dist/bench/s1/arms.js +6 -5
  56. package/dist/bench/s1/live-deps.js +5 -2
  57. package/dist/bench/s1/run-firm.js +6 -5
  58. package/dist/bind-host.d.ts +25 -0
  59. package/dist/bind-host.js +25 -0
  60. package/dist/boot/adoption.d.ts +3 -0
  61. package/dist/boot/adoption.js +1 -0
  62. package/dist/boot/deployment-mods.d.ts +1 -1
  63. package/dist/boot/execution-env.d.ts +17 -7
  64. package/dist/boot/execution-env.js +37 -31
  65. package/dist/boot/exit-boundary.d.ts +2 -2
  66. package/dist/boot/exit-boundary.js +6 -5
  67. package/dist/boot/fatal-exit.d.ts +3 -5
  68. package/dist/boot/fatal-exit.js +2 -2
  69. package/dist/boot/leader.d.ts +15 -0
  70. package/dist/boot/leader.js +17 -15
  71. package/dist/boot/memory-consolidation.d.ts +37 -29
  72. package/dist/boot/memory-consolidation.js +11 -5
  73. package/dist/boot/org-memory.js +1 -1
  74. package/dist/boot/parked-revive-gate.js +2 -2
  75. package/dist/boot/reapers.d.ts +4 -3
  76. package/dist/boot/reapers.js +19 -16
  77. package/dist/boot/resolve-spec.d.ts +5 -3
  78. package/dist/boot/resolve-spec.js +55 -67
  79. package/dist/boot/retention-lane.d.ts +4 -4
  80. package/dist/boot/retention-lane.js +2 -2
  81. package/dist/boot/runner-deps.d.ts +28 -7
  82. package/dist/boot/runner-deps.js +11 -4
  83. package/dist/boot/runtime-caps.d.ts +2 -2
  84. package/dist/boot/runtime-caps.js +16 -2
  85. package/dist/boot/session-faces.d.ts +16 -7
  86. package/dist/boot/session-faces.js +114 -94
  87. package/dist/boot/shutdown.d.ts +5 -4
  88. package/dist/boot/shutdown.js +6 -4
  89. package/dist/boot/stage-01-config.js +9 -1
  90. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
  91. package/dist/boot/stage-02-web-admin-pull.js +6 -1
  92. package/dist/boot/stage-03-store-backend.d.ts +3 -0
  93. package/dist/boot/stage-03-store-backend.js +4 -3
  94. package/dist/boot/stage-04-stores.d.ts +2 -0
  95. package/dist/boot/stage-04-stores.js +2 -2
  96. package/dist/boot/stage-05-execution-env.d.ts +7 -3
  97. package/dist/boot/stage-05-execution-env.js +2 -2
  98. package/dist/boot/stage-06-runners.d.ts +10 -12
  99. package/dist/boot/stage-06-runners.js +23 -15
  100. package/dist/boot/stage-07-capability-layer.d.ts +11 -8
  101. package/dist/boot/stage-07-capability-layer.js +5 -3
  102. package/dist/boot/stage-08-reapers.d.ts +11 -8
  103. package/dist/boot/stage-08-reapers.js +3 -3
  104. package/dist/boot/stage-09-leader.d.ts +12 -15
  105. package/dist/boot/stage-09-leader.js +12 -4
  106. package/dist/boot/stage-10-http-server.d.ts +12 -15
  107. package/dist/boot/stage-10-http-server.js +7 -4
  108. package/dist/boot/store-evidence.d.ts +20 -0
  109. package/dist/boot/store-evidence.js +13 -0
  110. package/dist/boot/stores.d.ts +3 -0
  111. package/dist/boot/stores.js +21 -10
  112. package/dist/boot/task-list-lane.d.ts +4 -4
  113. package/dist/boot/web-admin.d.ts +4 -2
  114. package/dist/boot/web-admin.js +55 -35
  115. package/dist/boot/webfetch-summarize-lane.d.ts +1 -1
  116. package/dist/boot-reclaim.d.ts +3 -1
  117. package/dist/boot-reclaim.js +1 -1
  118. package/dist/brain.d.ts +7 -18
  119. package/dist/brain.js +5 -17
  120. package/dist/budget.js +6 -6
  121. package/dist/capabilities/center-plugins.d.ts +7 -1
  122. package/dist/capabilities/center-plugins.js +32 -10
  123. package/dist/capabilities/center-wiring.js +1 -1
  124. package/dist/capabilities/composite-members.d.ts +2 -2
  125. package/dist/capabilities/composite-members.js +2 -2
  126. package/dist/capabilities/repo-tools.d.ts +2 -0
  127. package/dist/capabilities/repo-tools.js +14 -12
  128. package/dist/capabilities/scenarios.d.ts +6 -15
  129. package/dist/capabilities/scenarios.js +11 -16
  130. package/dist/capabilities/skills.d.ts +1 -1
  131. package/dist/capabilities/tool-defer.d.ts +1 -1
  132. package/dist/capability-facts.d.ts +38 -11
  133. package/dist/capability-facts.js +47 -8
  134. package/dist/card-void.d.ts +4 -4
  135. package/dist/card-void.js +1 -1
  136. package/dist/center-credential-renewer.d.ts +6 -2
  137. package/dist/center-credential-renewer.js +7 -3
  138. package/dist/config-catalog.d.ts +16 -1
  139. package/dist/config-catalog.js +62 -56
  140. package/dist/config-dir-trust.d.ts +37 -4
  141. package/dist/config-dir-trust.js +88 -44
  142. package/dist/config-invariants.d.ts +42 -3
  143. package/dist/config-invariants.js +69 -5
  144. package/dist/config-lkg.d.ts +23 -1
  145. package/dist/config-lkg.js +17 -3
  146. package/dist/config-provider.d.ts +9 -9
  147. package/dist/config-provider.js +25 -110
  148. package/dist/config-pull-failure.d.ts +50 -0
  149. package/dist/config-pull-failure.js +48 -0
  150. package/dist/config-types.d.ts +26 -17
  151. package/dist/config.d.ts +8 -16
  152. package/dist/config.js +113 -47
  153. package/dist/delegated-session-owner.d.ts +1 -1
  154. package/dist/deleted-session-ids.d.ts +3 -2
  155. package/dist/deployment-governance.d.ts +3 -3
  156. package/dist/deployment-shape.d.ts +30 -4
  157. package/dist/deployment-shape.js +7 -0
  158. package/dist/egress-redirect.d.ts +25 -7
  159. package/dist/egress-redirect.js +20 -2
  160. package/dist/elicitation.d.ts +6 -5
  161. package/dist/elicitation.js +23 -22
  162. package/dist/engine-identity.d.ts +28 -0
  163. package/dist/engine-identity.js +10 -0
  164. package/dist/entry-problems.d.ts +5 -7
  165. package/dist/entry-problems.js +1 -1
  166. package/dist/env-facts.js +2 -1
  167. package/dist/env-witness.d.ts +7 -0
  168. package/dist/env-witness.js +16 -0
  169. package/dist/execution-lane-caps.d.ts +5 -18
  170. package/dist/execution-lane-caps.js +0 -22
  171. package/dist/execution-placement.d.ts +58 -18
  172. package/dist/execution-placement.js +41 -2
  173. package/dist/field-conflict.d.ts +7 -11
  174. package/dist/field-conflict.js +3 -3
  175. package/dist/file-history-capture.d.ts +3 -3
  176. package/dist/file-history-disclosure.js +1 -0
  177. package/dist/fleet/fleet-bus.d.ts +21 -3
  178. package/dist/fleet/fleet-bus.js +35 -25
  179. package/dist/fleet/fleet-reconciler.js +2 -2
  180. package/dist/fleet/serving-lease.js +3 -3
  181. package/dist/fleet-client.js +3 -3
  182. package/dist/fleet-lease.d.ts +0 -1
  183. package/dist/fleet-lease.js +4 -5
  184. package/dist/history-anchor.d.ts +23 -3
  185. package/dist/history-anchor.js +24 -4
  186. package/dist/hitl-delivery.d.ts +90 -0
  187. package/dist/hitl-delivery.js +98 -0
  188. package/dist/hooks/classic-stations.d.ts +8 -11
  189. package/dist/hooks/classic-stations.js +0 -7
  190. package/dist/hooks/hook-llm.d.ts +5 -1
  191. package/dist/hooks/hook-llm.js +24 -15
  192. package/dist/hooks/hook-runner.d.ts +67 -57
  193. package/dist/hooks/hook-runner.js +260 -186
  194. package/dist/host-lsp-manager.d.ts +22 -8
  195. package/dist/host-lsp-manager.js +4 -1
  196. package/dist/http/active-run-conflict.d.ts +2 -43
  197. package/dist/http/active-run-conflict.js +3 -7
  198. package/dist/http/admission.d.ts +4 -18
  199. package/dist/http/admission.js +75 -78
  200. package/dist/http/approval-sweeps.d.ts +3 -3
  201. package/dist/http/approval-sweeps.js +54 -30
  202. package/dist/http/caller-trust.d.ts +8 -10
  203. package/dist/http/caller-trust.js +1 -2
  204. package/dist/http/claim-heartbeat.d.ts +49 -0
  205. package/dist/http/claim-heartbeat.js +46 -0
  206. package/dist/http/claimed-run.d.ts +4 -3
  207. package/dist/http/dispatch.d.ts +8 -1
  208. package/dist/http/dispatch.js +76 -30
  209. package/dist/http/durable-run-leg.d.ts +3 -3
  210. package/dist/http/durable-run-leg.js +17 -12
  211. package/dist/http/execution-context.d.ts +17 -43
  212. package/dist/http/execution-context.js +7 -5
  213. package/dist/http/health-reader.d.ts +36 -0
  214. package/dist/http/health-reader.js +21 -0
  215. package/dist/http/health-self-report.d.ts +9 -9
  216. package/dist/http/health-self-report.js +4 -4
  217. package/dist/http/idempotency.d.ts +9 -5
  218. package/dist/http/idempotency.js +4 -4
  219. package/dist/http/memory-compliance-failure-reply.d.ts +15 -16
  220. package/dist/http/memory-compliance-failure-reply.js +4 -2
  221. package/dist/http/memory-face-fault-reply.d.ts +67 -0
  222. package/dist/http/memory-face-fault-reply.js +71 -0
  223. package/dist/http/memory-store-refusal-reply.d.ts +1 -1
  224. package/dist/http/memory-store-refusal-reply.js +3 -0
  225. package/dist/http/owner-access.d.ts +391 -67
  226. package/dist/http/owner-access.js +371 -35
  227. package/dist/http/principal-gate.d.ts +24 -81
  228. package/dist/http/principal-gate.js +24 -68
  229. package/dist/http/refusal-body.d.ts +35 -2
  230. package/dist/http/refusal-body.js +34 -0
  231. package/dist/http/request-guards.d.ts +9 -13
  232. package/dist/http/request-guards.js +34 -68
  233. package/dist/http/resume-composition.d.ts +37 -0
  234. package/dist/http/resume-composition.js +18 -0
  235. package/dist/http/resume-ctx-read.d.ts +35 -12
  236. package/dist/http/resume-ctx-read.js +25 -6
  237. package/dist/http/resume-legs.d.ts +21 -18
  238. package/dist/http/resume-legs.js +189 -129
  239. package/dist/http/retired-skill-manifest.d.ts +1 -1
  240. package/dist/http/route-ctx.d.ts +25 -32
  241. package/dist/http/routes/a2a-serve.d.ts +43 -5
  242. package/dist/http/routes/a2a-serve.js +58 -37
  243. package/dist/http/routes/admin-config-refresh.js +2 -2
  244. package/dist/http/routes/admin-drain.js +2 -2
  245. package/dist/http/routes/admin-memory-optout.js +4 -3
  246. package/dist/http/routes/adoption.d.ts +3 -0
  247. package/dist/http/routes/adoption.js +17 -10
  248. package/dist/http/routes/agents-roster.js +13 -7
  249. package/dist/http/routes/approvals-assistant.js +70 -65
  250. package/dist/http/routes/attachments.js +2 -2
  251. package/dist/http/routes/background.d.ts +3 -3
  252. package/dist/http/routes/background.js +9 -17
  253. package/dist/http/routes/capabilities.js +15 -18
  254. package/dist/http/routes/config-catalog.js +2 -2
  255. package/dist/http/routes/devices.d.ts +3 -3
  256. package/dist/http/routes/devices.js +4 -4
  257. package/dist/http/routes/diagnostics.d.ts +27 -3
  258. package/dist/http/routes/diagnostics.js +27 -21
  259. package/dist/http/routes/engine.js +2 -2
  260. package/dist/http/routes/fleet.js +5 -3
  261. package/dist/http/routes/images.js +10 -6
  262. package/dist/http/routes/leader.d.ts +7 -2
  263. package/dist/http/routes/leader.js +12 -3
  264. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  265. package/dist/http/routes/mcp-reconnect.js +7 -15
  266. package/dist/http/routes/memory-bundle.js +4 -7
  267. package/dist/http/routes/memory-compliance.js +4 -4
  268. package/dist/http/routes/memory-consolidation.js +6 -9
  269. package/dist/http/routes/memory-origin.d.ts +2 -0
  270. package/dist/http/routes/memory-origin.js +7 -8
  271. package/dist/http/routes/memory-policy.js +17 -12
  272. package/dist/http/routes/notify-wake.js +10 -19
  273. package/dist/http/routes/observability.js +3 -3
  274. package/dist/http/routes/principal-inbox.d.ts +1 -1
  275. package/dist/http/routes/principal-inbox.js +2 -2
  276. package/dist/http/routes/projects.d.ts +1 -1
  277. package/dist/http/routes/projects.js +3 -2
  278. package/dist/http/routes/retention-ops.js +6 -9
  279. package/dist/http/routes/rules.js +20 -5
  280. package/dist/http/routes/run-memory-capture.d.ts +2 -2
  281. package/dist/http/routes/run-memory-capture.js +6 -12
  282. package/dist/http/routes/run-permission-mode.d.ts +2 -2
  283. package/dist/http/routes/run-permission-mode.js +10 -17
  284. package/dist/http/routes/runs.js +146 -231
  285. package/dist/http/routes/session-memory-sync.d.ts +2 -2
  286. package/dist/http/routes/session-memory-sync.js +7 -13
  287. package/dist/http/routes/session-sync.d.ts +4 -2
  288. package/dist/http/routes/session-sync.js +21 -20
  289. package/dist/http/routes/sessions-list.d.ts +3 -3
  290. package/dist/http/routes/sessions-list.js +9 -9
  291. package/dist/http/routes/sessions.js +87 -124
  292. package/dist/http/routes/shared-memory.js +6 -6
  293. package/dist/http/routes/side-query.js +2 -2
  294. package/dist/http/routes/tasks.js +136 -120
  295. package/dist/http/routes/trace-usage.d.ts +0 -12
  296. package/dist/http/routes/trace-usage.js +40 -46
  297. package/dist/http/routes/workflows.d.ts +39 -4
  298. package/dist/http/routes/workflows.js +42 -14
  299. package/dist/http/run-concurrency-cap.d.ts +20 -12
  300. package/dist/http/run-concurrency-cap.js +39 -26
  301. package/dist/http/run-read-face.d.ts +3 -5
  302. package/dist/http/run-read-face.js +8 -13
  303. package/dist/http/send.d.ts +12 -3
  304. package/dist/http/send.js +42 -6
  305. package/dist/http/server.d.ts +39 -40
  306. package/dist/http/server.js +36 -11
  307. package/dist/http/sse-log.d.ts +17 -10
  308. package/dist/http/sse-log.js +7 -7
  309. package/dist/http/stored-body-replay.d.ts +12 -0
  310. package/dist/http/stored-body-replay.js +2 -2
  311. package/dist/http/submit-ctx.d.ts +16 -8
  312. package/dist/http/submit-ctx.js +6 -2
  313. package/dist/http/submit-replay.d.ts +19 -6
  314. package/dist/http/submit-replay.js +18 -6
  315. package/dist/http/sync-submit-body.d.ts +3 -4
  316. package/dist/http/sync-submit-body.js +4 -5
  317. package/dist/http/wire-types.d.ts +4 -3
  318. package/dist/identity-source.d.ts +36 -5
  319. package/dist/identity-source.js +27 -0
  320. package/dist/index.d.ts +1 -0
  321. package/dist/index.js +1 -0
  322. package/dist/key-resolver.d.ts +32 -0
  323. package/dist/key-resolver.js +23 -9
  324. package/dist/leader/diffup.js +2 -1
  325. package/dist/leader/endpoint.d.ts +26 -5
  326. package/dist/leader/endpoint.js +154 -14
  327. package/dist/leader/fanout.d.ts +3 -0
  328. package/dist/leader/fanout.js +78 -63
  329. package/dist/leader/leader.d.ts +16 -0
  330. package/dist/leader/leader.js +43 -3
  331. package/dist/leader/merge.d.ts +9 -1
  332. package/dist/leader/merge.js +16 -1
  333. package/dist/leader/repair-wire.js +1 -1
  334. package/dist/leader/wire.d.ts +36 -3
  335. package/dist/leader/wire.js +59 -18
  336. package/dist/lsp-evict.d.ts +6 -4
  337. package/dist/lsp-evict.js +2 -3
  338. package/dist/memory-operator-faces.d.ts +5 -4
  339. package/dist/memory-operator-faces.js +1 -1
  340. package/dist/memory-scope.d.ts +29 -2
  341. package/dist/memory-scope.js +64 -15
  342. package/dist/memory-store-refusal.d.ts +5 -2
  343. package/dist/memory-sync-client.d.ts +3 -2
  344. package/dist/memory-sync-client.js +6 -3
  345. package/dist/model-compat.d.ts +17 -8
  346. package/dist/model-compat.js +6 -2
  347. package/dist/model-context-window.d.ts +18 -0
  348. package/dist/model-context-window.js +2 -0
  349. package/dist/model-entry-headers.d.ts +46 -4
  350. package/dist/model-entry-headers.js +2 -2
  351. package/dist/model-entry-refusal.d.ts +35 -16
  352. package/dist/model-entry-refusal.js +40 -27
  353. package/dist/model-plane-face.d.ts +19 -11
  354. package/dist/model-plane-face.js +9 -1
  355. package/dist/model-plane-probe.d.ts +2 -0
  356. package/dist/model-plane-probe.js +17 -0
  357. package/dist/model-provider.d.ts +3 -3
  358. package/dist/model-route-endpoint.d.ts +9 -5
  359. package/dist/model-route-endpoint.js +29 -7
  360. package/dist/model-select.d.ts +16 -0
  361. package/dist/model-select.js +4 -0
  362. package/dist/mountinfo.d.ts +37 -0
  363. package/dist/mountinfo.js +47 -0
  364. package/dist/namespaced-rule-name.d.ts +23 -14
  365. package/dist/namespaced-rule-name.js +8 -11
  366. package/dist/net-address.d.ts +1 -1
  367. package/dist/observability/corrupt-read-seat.d.ts +4 -4
  368. package/dist/observability/corrupt-read-seat.js +5 -3
  369. package/dist/observability/fail-open.d.ts +58 -38
  370. package/dist/observability/fail-open.js +60 -38
  371. package/dist/observability/logger.d.ts +22 -1
  372. package/dist/observability/logger.js +89 -12
  373. package/dist/observability/metrics.js +3 -4
  374. package/dist/observability/otel-exporter.js +2 -1
  375. package/dist/observability/principal-context.d.ts +14 -7
  376. package/dist/observability/principal-context.js +8 -3
  377. package/dist/observability/run-terminal.d.ts +7 -1
  378. package/dist/observability/run-terminal.js +5 -2
  379. package/dist/orchestration/hardened-vm-runner.d.ts +1 -0
  380. package/dist/orchestration/hardened-vm-runner.js +11 -5
  381. package/dist/orchestration/hardened-vm-worker-runner.d.ts +0 -14
  382. package/dist/orchestration/hardened-vm-worker-runner.js +5 -0
  383. package/dist/orchestration/hardened-vm-worker.js +2 -1
  384. package/dist/orchestration/workflow-completion-inbox.d.ts +3 -3
  385. package/dist/orchestration/workflow-completion-inbox.js +3 -2
  386. package/dist/org-delivery-gate.d.ts +5 -4
  387. package/dist/org-delivery-gate.js +7 -6
  388. package/dist/outbound-dispatcher.d.ts +1 -1
  389. package/dist/outbound-dispatcher.js +6 -12
  390. package/dist/owner-brands.d.ts +81 -0
  391. package/dist/owner-brands.js +42 -0
  392. package/dist/owner-faces.d.ts +531 -0
  393. package/dist/owner-faces.js +126 -0
  394. package/dist/parked-decide.d.ts +67 -5
  395. package/dist/parked-decide.js +127 -5
  396. package/dist/parked-revive-surface.d.ts +14 -4
  397. package/dist/parked-revive-surface.js +5 -4
  398. package/dist/peer-directory.d.ts +6 -8
  399. package/dist/peer-directory.js +3 -2
  400. package/dist/permission-rule-vocab.d.ts +53 -8
  401. package/dist/permission-rule-vocab.js +30 -2
  402. package/dist/plugins/adoption-log-sql.d.ts +0 -22
  403. package/dist/plugins/adoption-log-sql.js +5 -3
  404. package/dist/plugins/approval-ask-retired-delete-by-task-m50.d.ts +7 -0
  405. package/dist/plugins/approval-ask-retired-delete-by-task-m50.js +17 -0
  406. package/dist/plugins/approval-ask-store-file.d.ts +18 -7
  407. package/dist/plugins/approval-ask-store-file.js +37 -14
  408. package/dist/plugins/approval-ask-store-memory.d.ts +12 -4
  409. package/dist/plugins/approval-ask-store-memory.js +24 -10
  410. package/dist/plugins/approval-ask-store-sql.d.ts +39 -20
  411. package/dist/plugins/approval-ask-store-sql.js +41 -10
  412. package/dist/plugins/approval-exemption-skill-m46.d.ts +20 -0
  413. package/dist/plugins/approval-exemption-skill-m46.js +25 -0
  414. package/dist/plugins/approval-exemption-store.d.ts +6 -1
  415. package/dist/plugins/approval-exemption-store.js +14 -0
  416. package/dist/plugins/approval-nonce-store.d.ts +39 -12
  417. package/dist/plugins/approval-nonce-store.js +72 -23
  418. package/dist/plugins/blob-backend.d.ts +2 -0
  419. package/dist/plugins/blob-backend.js +9 -6
  420. package/dist/plugins/caching-session-store.d.ts +13 -1
  421. package/dist/plugins/caching-session-store.js +43 -4
  422. package/dist/plugins/checkpoint-index-columns.d.ts +32 -5
  423. package/dist/plugins/checkpoint-index-columns.js +36 -1
  424. package/dist/plugins/checkpoint-store-face.d.ts +83 -10
  425. package/dist/plugins/checkpoint-store-face.js +18 -1
  426. package/dist/plugins/device-store-sql.js +1 -0
  427. package/dist/plugins/ensure-index.d.ts +13 -1
  428. package/dist/plugins/ensure-index.js +154 -21
  429. package/dist/plugins/ensure-schema-lock-policy.d.ts +0 -15
  430. package/dist/plugins/ensure-schema-lock-policy.js +3 -2
  431. package/dist/plugins/file-run-store.d.ts +63 -23
  432. package/dist/plugins/file-run-store.js +138 -83
  433. package/dist/plugins/host-platform.d.ts +24 -3
  434. package/dist/plugins/host-platform.js +15 -1
  435. package/dist/plugins/image-bake-store-sql.js +2 -0
  436. package/dist/plugins/imported-layer-root-retirement.d.ts +2 -2
  437. package/dist/plugins/k8s-pod-reaper.d.ts +1 -3
  438. package/dist/plugins/k8s-pod-reaper.js +0 -1
  439. package/dist/plugins/leader-run-store-sql.d.ts +22 -0
  440. package/dist/plugins/leader-run-store-sql.js +34 -4
  441. package/dist/plugins/local-checkpoint-store.d.ts +58 -23
  442. package/dist/plugins/local-checkpoint-store.js +195 -76
  443. package/dist/plugins/local-data-dir-modes-m39.js +2 -1
  444. package/dist/plugins/local-docker-capacity.d.ts +93 -0
  445. package/dist/plugins/local-docker-capacity.js +194 -0
  446. package/dist/plugins/local-docker-reaper.d.ts +92 -0
  447. package/dist/plugins/local-docker-reaper.js +102 -0
  448. package/dist/plugins/local-engine-high-water.d.ts +4 -2
  449. package/dist/plugins/local-engine-high-water.js +4 -1
  450. package/dist/plugins/local-session-store.d.ts +358 -125
  451. package/dist/plugins/local-session-store.js +824 -251
  452. package/dist/plugins/local-session-window.d.ts +3 -2
  453. package/dist/plugins/local-session-window.js +3 -2
  454. package/dist/plugins/local-sidecar-quarantine.js +3 -2
  455. package/dist/plugins/local-task-attachment-store.d.ts +18 -1
  456. package/dist/plugins/local-task-attachment-store.js +22 -1
  457. package/dist/plugins/memory-embedder.js +9 -7
  458. package/dist/plugins/memory-engine-pg.d.ts +3 -2
  459. package/dist/plugins/memory-engine-pg.js +14 -5
  460. package/dist/plugins/memory-engine-sql-apply.d.ts +8 -4
  461. package/dist/plugins/memory-engine-sql-apply.js +6 -8
  462. package/dist/plugins/memory-engine-tidb.d.ts +4 -2
  463. package/dist/plugins/memory-engine-tidb.js +11 -2
  464. package/dist/plugins/memory-optout-grant-store-sql.d.ts +1 -1
  465. package/dist/plugins/memory-optout-grant-store-sql.js +3 -3
  466. package/dist/plugins/memory-run-store.d.ts +39 -27
  467. package/dist/plugins/memory-run-store.js +72 -70
  468. package/dist/plugins/one-time-migration-marker.d.ts +10 -0
  469. package/dist/plugins/one-time-migration-marker.js +7 -0
  470. package/dist/plugins/one-time-migrations.d.ts +186 -45
  471. package/dist/plugins/one-time-migrations.js +442 -119
  472. package/dist/plugins/org-delivery-quota-store.js +2 -1
  473. package/dist/plugins/permission-rule-store-file.d.ts +3 -3
  474. package/dist/plugins/permission-rule-store-sql.d.ts +4 -63
  475. package/dist/plugins/permission-rule-store-sql.js +6 -86
  476. package/dist/plugins/pg-pool.d.ts +1 -31
  477. package/dist/plugins/pg-pool.js +16 -7
  478. package/dist/plugins/remote-env-e2b.d.ts +7 -25
  479. package/dist/plugins/remote-env-e2b.js +34 -25
  480. package/dist/plugins/remote-env-host.d.ts +2 -9
  481. package/dist/plugins/remote-env-host.js +34 -22
  482. package/dist/plugins/remote-env-k8s.d.ts +40 -45
  483. package/dist/plugins/remote-env-k8s.js +107 -58
  484. package/dist/plugins/remote-env-local-docker.d.ts +148 -14
  485. package/dist/plugins/remote-env-local-docker.js +367 -72
  486. package/dist/plugins/remote-scratchpad.js +4 -0
  487. package/dist/plugins/retention-deployment.d.ts +49 -17
  488. package/dist/plugins/retention-deployment.js +39 -8
  489. package/dist/plugins/run-preview-m45.d.ts +11 -0
  490. package/dist/plugins/run-preview-m45.js +19 -0
  491. package/dist/plugins/run-store-sql.d.ts +56 -68
  492. package/dist/plugins/run-store-sql.js +127 -116
  493. package/dist/plugins/s3-answer.d.ts +2 -1
  494. package/dist/plugins/sandbox-reaper.d.ts +26 -0
  495. package/dist/plugins/sandbox-reaper.js +2 -0
  496. package/dist/plugins/scenario-prefix-switch-m42.d.ts +54 -0
  497. package/dist/plugins/scenario-prefix-switch-m42.js +55 -0
  498. package/dist/plugins/scheduler-support.d.ts +16 -1
  499. package/dist/plugins/scheduler-support.js +22 -2
  500. package/dist/plugins/schema-consistency.d.ts +42 -9
  501. package/dist/plugins/schema-consistency.js +212 -11
  502. package/dist/plugins/schema-drift-sentence.d.ts +4 -1
  503. package/dist/plugins/schema-drift-sentence.js +4 -4
  504. package/dist/plugins/schema-ensure-legs.d.ts +9 -0
  505. package/dist/plugins/schema-ensure-legs.js +1 -1
  506. package/dist/plugins/schema-indexes.js +3 -0
  507. package/dist/plugins/schema-live-columns.d.ts +14 -5
  508. package/dist/plugins/schema-live-columns.js +10 -10
  509. package/dist/plugins/send-user-file.js +2 -1
  510. package/dist/plugins/session-birth.d.ts +37 -0
  511. package/dist/plugins/session-birth.js +38 -0
  512. package/dist/plugins/session-epoch-anchor.d.ts +40 -12
  513. package/dist/plugins/session-epoch-anchor.js +33 -11
  514. package/dist/plugins/session-ext-table.d.ts +27 -3
  515. package/dist/plugins/session-ext-table.js +19 -6
  516. package/dist/plugins/session-fork.d.ts +15 -1
  517. package/dist/plugins/session-fork.js +3 -0
  518. package/dist/plugins/session-ownership.d.ts +1 -1
  519. package/dist/plugins/session-placement.d.ts +22 -16
  520. package/dist/plugins/session-placement.js +15 -12
  521. package/dist/plugins/session-row-sql.d.ts +12 -0
  522. package/dist/plugins/session-row-sql.js +3 -3
  523. package/dist/plugins/session-run-rollup.d.ts +29 -0
  524. package/dist/plugins/session-run-rollup.js +40 -0
  525. package/dist/plugins/session-store-sql.d.ts +38 -12
  526. package/dist/plugins/session-store-sql.js +130 -30
  527. package/dist/plugins/session-withdraw-sql.d.ts +36 -0
  528. package/dist/plugins/session-withdraw-sql.js +42 -0
  529. package/dist/plugins/shared-memory-store-sql.d.ts +0 -69
  530. package/dist/plugins/shared-memory-store-sql.js +2 -1
  531. package/dist/plugins/shell-sentinel-owner-m48.d.ts +20 -0
  532. package/dist/plugins/shell-sentinel-owner-m48.js +3 -0
  533. package/dist/plugins/snapshot-bucket-presence.d.ts +2 -1
  534. package/dist/plugins/sql-driver.d.ts +1 -1
  535. package/dist/plugins/sql-driver.js +2 -1
  536. package/dist/plugins/sql-errors.d.ts +32 -5
  537. package/dist/plugins/sql-errors.js +55 -7
  538. package/dist/plugins/sql-json-column.d.ts +8 -7
  539. package/dist/plugins/sql-json-column.js +10 -6
  540. package/dist/plugins/store-backend.d.ts +84 -15
  541. package/dist/plugins/store-backend.js +186 -55
  542. package/dist/plugins/store-contracts.d.ts +90 -30
  543. package/dist/plugins/store-contracts.js +22 -1
  544. package/dist/plugins/store-fault.d.ts +28 -2
  545. package/dist/plugins/store-fault.js +20 -2
  546. package/dist/plugins/task-attachment-store.d.ts +15 -3
  547. package/dist/plugins/task-attachment-store.js +22 -9
  548. package/dist/plugins/tidb-pool.js +23 -8
  549. package/dist/plugins/web-search.js +5 -3
  550. package/dist/plugins/workflow-run-store-sql.d.ts +6 -1
  551. package/dist/plugins/workflow-run-store-sql.js +11 -5
  552. package/dist/principal-jwt.d.ts +4 -4
  553. package/dist/principal-jwt.js +20 -5
  554. package/dist/principal-shape.d.ts +41 -0
  555. package/dist/principal-shape.js +19 -0
  556. package/dist/question.d.ts +13 -23
  557. package/dist/question.js +21 -39
  558. package/dist/request-declaration-refusal.d.ts +35 -12
  559. package/dist/request-declaration-refusal.js +21 -9
  560. package/dist/request-form.d.ts +58 -0
  561. package/dist/request-form.js +72 -0
  562. package/dist/request-image-urls.d.ts +8 -2
  563. package/dist/request-image-urls.js +2 -1
  564. package/dist/request-images.d.ts +20 -0
  565. package/dist/request-images.js +59 -0
  566. package/dist/request-key-closure.d.ts +6 -7
  567. package/dist/request-key-closure.js +15 -2
  568. package/dist/resource-suspend.d.ts +7 -16
  569. package/dist/resource-suspend.js +1 -1
  570. package/dist/retired-name-host-notice.d.ts +21 -0
  571. package/dist/retired-name-host-notice.js +12 -0
  572. package/dist/rule-entry-list.d.ts +2 -2
  573. package/dist/rule-entry-list.js +3 -4
  574. package/dist/rule-issue-reading.d.ts +58 -0
  575. package/dist/rule-issue-reading.js +14 -0
  576. package/dist/rules-consent.d.ts +5 -3
  577. package/dist/rules-consent.js +1 -0
  578. package/dist/run-acceptance.d.ts +3 -1
  579. package/dist/run-acceptance.js +3 -0
  580. package/dist/run-local.d.ts +14 -6
  581. package/dist/run-local.js +92 -80
  582. package/dist/run-stale-default.d.ts +7 -0
  583. package/dist/run-stale-default.js +2 -0
  584. package/dist/runs.d.ts +26 -15
  585. package/dist/runs.js +39 -27
  586. package/dist/runtime-caps-resolver.d.ts +21 -6
  587. package/dist/runtime-caps-resolver.js +17 -4
  588. package/dist/runtime-governance.d.ts +2 -2
  589. package/dist/runtime-governance.js +5 -3
  590. package/dist/sealed-key-layout.d.ts +12 -5
  591. package/dist/sealed-key-layout.js +5 -0
  592. package/dist/sealed-key.d.ts +12 -2
  593. package/dist/sealed-key.js +115 -27
  594. package/dist/security.d.ts +128 -158
  595. package/dist/security.js +163 -59
  596. package/dist/sema-config-dir.d.ts +20 -0
  597. package/dist/sema-config-dir.js +25 -0
  598. package/dist/server-secret-env.d.ts +21 -5
  599. package/dist/server-secret-env.js +14 -4
  600. package/dist/session-children-retire.d.ts +7 -5
  601. package/dist/session-children-retire.js +7 -2
  602. package/dist/session-deletion.d.ts +88 -0
  603. package/dist/session-deletion.js +8 -0
  604. package/dist/session-erase.d.ts +145 -0
  605. package/dist/session-erase.js +115 -0
  606. package/dist/session-error-wire.d.ts +5 -5
  607. package/dist/session-error-wire.js +1 -1
  608. package/dist/session-incarnation.d.ts +52 -5
  609. package/dist/session-incarnation.js +19 -2
  610. package/dist/session-policy-wire.d.ts +6 -3
  611. package/dist/session-policy-wire.js +11 -1
  612. package/dist/session-resident-background.d.ts +0 -71
  613. package/dist/session-resident-background.js +4 -3
  614. package/dist/session-row.d.ts +2 -1
  615. package/dist/session-rules-seat.d.ts +12 -0
  616. package/dist/session-rules-seat.js +19 -0
  617. package/dist/session-titler.d.ts +7 -15
  618. package/dist/session-titler.js +1 -1
  619. package/dist/spec-fields.d.ts +57 -3
  620. package/dist/spec-fields.js +72 -51
  621. package/dist/store-posture.d.ts +136 -20
  622. package/dist/store-posture.js +188 -3
  623. package/dist/stored-request-hooks-m20.js +2 -2
  624. package/dist/stores-sql/background-agent-store-sql.d.ts +11 -2
  625. package/dist/stores-sql/background-agent-store-sql.js +73 -20
  626. package/dist/stores-sql/checkpoint-store-sql.d.ts +33 -28
  627. package/dist/stores-sql/checkpoint-store-sql.js +75 -44
  628. package/dist/stores-sql/mailbox-store-sql.d.ts +7 -4
  629. package/dist/stores-sql/mailbox-store-sql.js +31 -32
  630. package/dist/stores-sql/pg-session-storage.d.ts +7 -5
  631. package/dist/stores-sql/pg-session-storage.js +54 -46
  632. package/dist/stores-sql/retention-store-sql.d.ts +87 -19
  633. package/dist/stores-sql/retention-store-sql.js +92 -27
  634. package/dist/stores-sql/session-policy-store-sql.d.ts +32 -10
  635. package/dist/stores-sql/session-policy-store-sql.js +44 -36
  636. package/dist/stores-sql/session-write-hooks.d.ts +12 -6
  637. package/dist/stores-sql/tidb-session-storage.d.ts +4 -4
  638. package/dist/stores-sql/tidb-session-storage.js +13 -8
  639. package/dist/stores-sql/tidb-session-store.d.ts +36 -17
  640. package/dist/stores-sql/tidb-session-store.js +69 -39
  641. package/dist/stores-sql/windowed-session-storage.d.ts +5 -0
  642. package/dist/stores-sql/windowed-session-storage.js +9 -0
  643. package/dist/submission-exit.d.ts +50 -0
  644. package/dist/submission-exit.js +108 -0
  645. package/dist/task-a2a.d.ts +1 -1
  646. package/dist/task-a2a.js +3 -3
  647. package/dist/task-cwd.d.ts +20 -0
  648. package/dist/task-cwd.js +8 -0
  649. package/dist/task-mcp.d.ts +1 -1
  650. package/dist/task-mcp.js +3 -3
  651. package/dist/task-settings.d.ts +14 -10
  652. package/dist/task-settings.js +18 -7
  653. package/dist/terminal.d.ts +25 -43
  654. package/dist/terminal.js +31 -11
  655. package/dist/tool-approval.d.ts +131 -129
  656. package/dist/tool-approval.js +189 -193
  657. package/dist/trace/core-keyset-guard.d.ts +13 -6
  658. package/dist/trace/core-vocab-wire.d.ts +95 -0
  659. package/dist/trace/core-vocab-wire.js +15 -0
  660. package/dist/trace/exit-seat.d.ts +30 -4
  661. package/dist/trace/exit-seat.js +40 -36
  662. package/dist/trace/extension-call-face.d.ts +3 -2
  663. package/dist/trace/extension-call-face.js +3 -1
  664. package/dist/trace/ledger-events.d.ts +4 -7
  665. package/dist/trace/ledger-sink.d.ts +1 -30
  666. package/dist/trace/ledger-sink.js +0 -11
  667. package/dist/trace/model-segment.d.ts +5 -5
  668. package/dist/trace/model-segment.js +5 -5
  669. package/dist/trace/project.d.ts +55 -42
  670. package/dist/trace/project.js +52 -19
  671. package/dist/trace/redact.d.ts +123 -27
  672. package/dist/trace/redact.js +880 -260
  673. package/dist/trace/self-minted-wire.d.ts +487 -19
  674. package/dist/trace/self-minted-wire.js +172 -23
  675. package/dist/trace/wire-projection-faces.d.ts +5 -6
  676. package/dist/trace/wire-projection-faces.js +2 -2
  677. package/dist/upstream-body.d.ts +23 -0
  678. package/dist/upstream-body.js +16 -0
  679. package/dist/url-refusal.d.ts +3 -3
  680. package/dist/url-refusal.js +11 -4
  681. package/dist/user-authored-text.d.ts +1 -1
  682. package/dist/user-input-ledger.d.ts +7 -8
  683. package/dist/user-input-ledger.js +1 -1
  684. package/dist/web-admin-client/apply-effective.d.ts +6 -3
  685. package/dist/web-admin-client/apply-effective.js +13 -16
  686. package/dist/web-admin-client/apply-ledger.d.ts +16 -7
  687. package/dist/web-admin-client/apply-ledger.js +5 -2
  688. package/dist/web-admin-client/center-request.d.ts +2 -2
  689. package/dist/web-admin-client/center-request.js +15 -15
  690. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  691. package/dist/web-admin-client/effective-keys.js +1 -1
  692. package/dist/web-admin-client/entry-metadata.d.ts +8 -0
  693. package/dist/web-admin-client/entry-metadata.js +13 -0
  694. package/dist/web-admin-client/http-client.d.ts +1 -1
  695. package/dist/web-admin-client/http-client.js +23 -4
  696. package/dist/web-admin-client/mcp-revocation.d.ts +1 -1
  697. package/dist/web-admin-client/mcp-revocation.js +2 -1
  698. package/dist/web-admin-client/read-warnings.d.ts +1 -0
  699. package/dist/web-admin-client/read-warnings.js +1 -0
  700. package/dist/web-admin-client/restart-signal.js +8 -4
  701. package/dist/web-admin-client/skills-mcp.d.ts +2 -2
  702. package/dist/web-admin-client/types.d.ts +1 -2
  703. package/dist/workspace-space.d.ts +3 -2
  704. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +4 -4
  705. package/package.json +6 -3
  706. package/scripts/drain-check-local.mjs +118 -147
  707. package/scripts/drain-check.mysql.sql +22 -2
  708. package/scripts/drain-check.pg.sql +22 -2
  709. package/wire-changes/server@7.148.0.json +1517 -0
  710. package/dist/http/memory-control-plane-corrupt-reply.d.ts +0 -25
  711. package/dist/http/memory-control-plane-corrupt-reply.js +0 -19
  712. package/dist/http/memory-layer-reply.d.ts +0 -30
  713. package/dist/http/memory-layer-reply.js +0 -24
  714. package/dist/lsp/e2b-bridge.d.ts +0 -57
  715. package/dist/lsp/e2b-bridge.js +0 -139
  716. package/dist/lsp/e2b-manager.d.ts +0 -93
  717. package/dist/lsp/e2b-manager.js +0 -178
  718. package/dist/lsp/lsp-frames.d.ts +0 -14
  719. package/dist/lsp/lsp-frames.js +0 -25
  720. package/dist/lsp/manager.d.ts +0 -129
  721. package/dist/lsp/manager.js +0 -127
  722. package/dist/lsp/types.d.ts +0 -12
  723. package/dist/lsp/types.js +0 -2
  724. package/dist/lsp/ws-transport.d.ts +0 -40
  725. package/dist/lsp/ws-transport.js +0 -210
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
  import { spawn } from "node:child_process";
3
3
  import { hostSubprocessEnv } from "../server-secret-env.js";
4
4
  import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
5
- import { randomBytes } from "node:crypto";
5
+ import { createHash, randomBytes } from "node:crypto";
6
6
  import { shellQuote, armPipeDestroyGrace, appendFileStaged, readyUpload, writeFileStaged } from "./remote-shell.js";
7
7
  import { signalNumber } from "./host-platform.js";
8
8
  import { buildLivenessCut, commandBoundsOf } from "../exec-bounds.js";
@@ -16,6 +16,12 @@ const PROVIDER = "local-docker";
16
16
  const DEFAULT_MOUNT_PATH = "/workspace";
17
17
  const DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60_000;
18
18
  const DEFAULT_CONTROL_TIMEOUT_MS = 60_000;
19
+ const INSPECT_ID_STATE_AND_LABELS = "{{.Id}}\t{{.State.Status}}\t{{json .Config.Labels}}";
20
+ const DOCKER_STATES = ["created", "running", "paused", "restarting", "removing", "exited", "dead"];
21
+ const NO_CUSTODY = Object.freeze({ kind: "none" });
22
+ function isNameConflict(stderr, name) {
23
+ return stderr.includes(`The container name "/${name}" is already in use`);
24
+ }
19
25
  const ok = (value) => ({ ok: true, value });
20
26
  const unsupported = (op) => ({
21
27
  ok: false,
@@ -33,12 +39,18 @@ export class RemoteLocalDockerExecutionEnv {
33
39
  cfg;
34
40
  spawnFn;
35
41
  containerName;
42
+ custody;
36
43
  containerId;
44
+ removedAny = false;
45
+ teardown;
46
+ teardownSettled = false;
37
47
  handle;
38
48
  destroyed = false;
39
49
  connecting;
40
50
  connectRefusal;
41
51
  homeSeat;
52
+ lifecycle = new AbortController();
53
+ capacityDeadline;
42
54
  constructor(config, deps = {}) {
43
55
  if (!config.image || !config.image.trim()) {
44
56
  config = { ...config, image: config.image ?? "" };
@@ -56,9 +68,21 @@ export class RemoteLocalDockerExecutionEnv {
56
68
  this.spawnFn = deps.spawn ?? spawn;
57
69
  this.floor = deps.readFloor ?? createSandboxReadFloor({ lane: PROVIDER, knob: config.sandboxReadFloor });
58
70
  this.capabilities = capabilitiesFor({ remoteExec: { provider: PROVIDER }, sandboxReadFloor: config.sandboxReadFloor });
59
- const id = config.id ?? randomBytes(6).toString("hex");
60
- const suffix = randomBytes(4).toString("hex");
61
- this.containerName = `sema-docker-${sanitizeId(id)}-${suffix}`;
71
+ this.containerName = localDockerContainerNameOf(config.workerLabel, config.id ?? randomBytes(6).toString("hex"));
72
+ this.custody = config.leftover ?? NO_CUSTODY;
73
+ }
74
+ get instance() {
75
+ return this.custody.kind === "held" ? this.custody.id : undefined;
76
+ }
77
+ get leftover() {
78
+ const c = this.custody;
79
+ return c.kind === "doomed" || c.kind === "doomed-unknown" ? c : undefined;
80
+ }
81
+ get releasing() {
82
+ return this.teardown;
83
+ }
84
+ get released() {
85
+ return this.teardownSettled;
62
86
  }
63
87
  resolve(p) {
64
88
  return path.posix.isAbsolute(p) ? path.posix.normalize(p) : path.posix.normalize(path.posix.join(this.cwd, p));
@@ -77,18 +101,142 @@ export class RemoteLocalDockerExecutionEnv {
77
101
  return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted") };
78
102
  if (this.containerId)
79
103
  return ok(this.workspaceHandle());
104
+ return this.connectOnce(config, "start-or-attach");
105
+ }
106
+ async connectOnce(config, mode) {
80
107
  if (this.connecting)
81
108
  return this.connecting;
82
- this.connecting = this.doConnect(config);
109
+ this.connecting = this.doConnect(config, mode);
83
110
  const r = await this.connecting;
84
111
  if (!r.ok)
85
112
  this.connecting = undefined;
86
113
  return r;
87
114
  }
88
- async doConnect(config) {
89
- if (!this.cfg.image.trim()) {
115
+ async doConnect(config, mode) {
116
+ if (mode === "start-or-attach" && !this.cfg.image.trim()) {
90
117
  return { ok: false, error: new RemoteExecutionError("connect_failed", "local-docker: no base image configured (the image is required — no docker.io default)") };
91
118
  }
119
+ const owed = this.custody;
120
+ if (owed.kind === "doomed" && !(await this.removeInstance(owed.id)))
121
+ return this.stillOwed(owed.id);
122
+ let seen = await this.sightContainer(config?.abortSignal);
123
+ if (this.custody.kind === "doomed-unknown") {
124
+ switch (seen.kind) {
125
+ case "unreadable":
126
+ return { ok: false, error: seen.error };
127
+ case "ours":
128
+ if (!(await this.removeInstance(seen.id)))
129
+ return this.stillOwed(seen.id);
130
+ seen = { kind: "absent" };
131
+ break;
132
+ case "absent":
133
+ this.custody = NO_CUSTODY;
134
+ this.cfg.capacity?.removed(this.containerName);
135
+ break;
136
+ case "foreign":
137
+ this.custody = NO_CUSTODY;
138
+ break;
139
+ }
140
+ }
141
+ switch (seen.kind) {
142
+ case "unreadable":
143
+ return { ok: false, error: seen.error };
144
+ case "foreign":
145
+ return { ok: false, error: new RemoteExecutionError("connect_failed", `a container named '${this.containerName}' exists on the docker daemon but this worker did not start it (it does not carry the label ${this.cfg.workerLabel}) — nothing was started or removed; remove or rename that container`) };
146
+ case "absent":
147
+ return mode === "attach-only" ? this.notResumable("gone") : this.startContainer(config);
148
+ case "ours":
149
+ switch (seen.state) {
150
+ case "running":
151
+ return this.attachContainer(seen.id, config?.abortSignal);
152
+ case "exited": {
153
+ const admitted = await this.admitStart(config);
154
+ if (!admitted.ok)
155
+ return admitted;
156
+ this.custody = { kind: "held", id: seen.id };
157
+ const started = await this.docker(["start", seen.id], { signal: config?.abortSignal, timeoutMs: this.cfg.controlTimeoutMs });
158
+ admitted.ticket?.settle(this.startOutcomeOf(started));
159
+ if (!started.ok)
160
+ return { ok: false, error: this.dockerToRemoteError(started.error, "connect_failed") };
161
+ if (started.value.exitCode !== 0)
162
+ return { ok: false, error: new RemoteExecutionError("connect_failed", `container '${this.containerName}' of this worker is exited and docker start failed: ${started.value.stderr.trim() || `exit ${started.value.exitCode}`} — nothing was removed`) };
163
+ return this.attachContainer(seen.id, config?.abortSignal);
164
+ }
165
+ case "created": {
166
+ if (mode === "attach-only")
167
+ return this.notResumable("created, never started");
168
+ if (!(await this.removeInstance(seen.id))) {
169
+ return { ok: false, error: new RemoteExecutionError("connect_failed", `container '${this.containerName}' of this worker was created but never started and could not be removed — nothing was started; remove it and retry`) };
170
+ }
171
+ return this.startContainer(config);
172
+ }
173
+ case "paused":
174
+ case "restarting":
175
+ case "removing":
176
+ case "dead":
177
+ return { ok: false, error: new RemoteExecutionError("connect_failed", `container '${this.containerName}' of this worker is ${seen.state} — it was neither attached nor removed (its workspace may still be there); resolve it with docker and retry`) };
178
+ default: {
179
+ const unhandled = seen.state;
180
+ throw new Error(`local-docker: no ruling for container state ${String(unhandled)}`);
181
+ }
182
+ }
183
+ }
184
+ }
185
+ stillOwed(id) {
186
+ return { ok: false, error: new RemoteExecutionError("connect_failed", `container ${id.slice(0, 12)} of this worker ('${this.containerName}') is due for removal and docker rm could not confirm it — it is never attached; nothing was started; remove it and retry`) };
187
+ }
188
+ notResumable(why) {
189
+ return { ok: false, error: new RemoteExecutionError("connect_failed", `container '${this.containerName}' is not running (${why}) — no managed snapshot to resume`) };
190
+ }
191
+ async sightContainer(signal) {
192
+ const r = await this.docker(["inspect", "--type", "container", "--format", INSPECT_ID_STATE_AND_LABELS, this.containerName], { signal, timeoutMs: this.cfg.controlTimeoutMs });
193
+ if (!r.ok)
194
+ return { kind: "unreadable", error: this.dockerToRemoteError(r.error, "connect_failed") };
195
+ if (r.value.exitCode !== 0) {
196
+ return /no such (object|container)/i.test(r.value.stderr)
197
+ ? { kind: "absent" }
198
+ : { kind: "unreadable", error: new RemoteExecutionError("connect_failed", `docker inspect of '${this.containerName}' failed: ${r.value.stderr.trim() || `exit ${r.value.exitCode}`}`) };
199
+ }
200
+ const line = r.value.stdout.trim();
201
+ const [id = "", state = "", ...rest] = line.split("\t");
202
+ let labels;
203
+ try {
204
+ labels = rest.length === 1 ? JSON.parse(rest[0]) : undefined;
205
+ }
206
+ catch {
207
+ labels = undefined;
208
+ }
209
+ const known = DOCKER_STATES.find((s) => s === state);
210
+ if (!/^[0-9a-f]{12,64}$/.test(id) || known === undefined || labels === undefined || (labels !== null && typeof labels !== "object")) {
211
+ return { kind: "unreadable", error: new RemoteExecutionError("connect_failed", `docker inspect of '${this.containerName}' answered a form this adapter does not read (${JSON.stringify(line.slice(0, 160))}) — nothing was started or removed`) };
212
+ }
213
+ const ours = labels !== null && Object.entries(labels).some(([k, v]) => `${k}=${String(v)}` === this.cfg.workerLabel);
214
+ return ours ? { kind: "ours", id, state: known } : { kind: "foreign" };
215
+ }
216
+ async attachContainer(id, signal) {
217
+ this.custody = { kind: "held", id };
218
+ if (this.homeSeat.asks && !(await this.untilOrAborted(this.probeContainerShell(), signal))) {
219
+ return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted while detecting the container's shell") };
220
+ }
221
+ const home = await this.homeVerdict(signal);
222
+ if (home !== undefined)
223
+ return { ok: false, error: home };
224
+ await this.floor.attach(this.floorTransport(), { origin: "resumed", workspaceRoot: this.cfg.mountPath });
225
+ if (this.destroyed)
226
+ return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
227
+ const filled = await this.fillWorkspace("restored");
228
+ if (!filled.ok) {
229
+ if (filled.standing)
230
+ this.connectRefusal = filled.error;
231
+ return { ok: false, error: filled.error };
232
+ }
233
+ if (this.destroyed)
234
+ return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
235
+ this.containerId = id;
236
+ this.handle = { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
237
+ return ok(this.handle);
238
+ }
239
+ async startContainer(config) {
92
240
  const pidsLimit = this.cfg.pidsLimit ?? 512;
93
241
  const runArgs = [
94
242
  "run",
@@ -96,6 +244,7 @@ export class RemoteLocalDockerExecutionEnv {
96
244
  "--init",
97
245
  "--name",
98
246
  this.containerName,
247
+ "--label", this.cfg.workerLabel,
99
248
  "-w",
100
249
  this.cfg.mountPath,
101
250
  "--security-opt",
@@ -110,48 +259,71 @@ export class RemoteLocalDockerExecutionEnv {
110
259
  "-c",
111
260
  `mkdir -p ${shellQuote(this.cfg.mountPath)} && exec tail -f /dev/null`,
112
261
  ];
262
+ const admitted = await this.admitStart(config);
263
+ if (!admitted.ok)
264
+ return admitted;
113
265
  const run = await this.docker(runArgs, { signal: config?.abortSignal, timeoutMs: this.cfg.controlTimeoutMs });
114
- if (!run.ok) {
115
- await this.forceRemove().catch(() => { });
116
- return { ok: false, error: this.dockerToRemoteError(run.error, "connect_failed") };
117
- }
118
- if (run.value.exitCode !== 0) {
119
- await this.forceRemove().catch(() => { });
120
- return { ok: false, error: new RemoteExecutionError("connect_failed", `docker run failed: ${run.value.stderr.trim() || `exit ${run.value.exitCode}`}`) };
266
+ const outcome = this.startOutcomeOf(run);
267
+ admitted.ticket?.settle(outcome);
268
+ const id = run.ok && run.value.exitCode === 0 ? run.value.stdout.trim() : "";
269
+ if (id === "") {
270
+ if (outcome !== "absent")
271
+ await this.claimLeftoverOfThisStart();
272
+ const error = !run.ok ? this.dockerToRemoteError(run.error, "connect_failed") : new RemoteExecutionError("connect_failed", `docker run failed: ${run.value.stderr.trim() || (run.value.exitCode === 0 ? "it printed no container id" : `exit ${run.value.exitCode}`)}`);
273
+ return { ok: false, error };
121
274
  }
275
+ this.custody = { kind: "held", id };
122
276
  if (this.destroyed) {
123
- await this.forceRemove().catch(() => { });
277
+ await this.removeInstance(id);
124
278
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
125
279
  }
126
280
  if (this.homeSeat.asks && !(await this.untilOrAborted(this.probeContainerShell(), config?.abortSignal))) {
127
- await this.forceRemove().catch(() => { });
281
+ await this.removeInstance(id);
128
282
  return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted while detecting the container's shell") };
129
283
  }
130
284
  const home = await this.homeVerdict(config?.abortSignal);
131
285
  if (home !== undefined) {
132
- await this.forceRemove().catch(() => { });
286
+ await this.removeInstance(id);
133
287
  return { ok: false, error: home };
134
288
  }
135
289
  await this.floor.attach(this.floorTransport(), { origin: "created", workspaceRoot: this.cfg.mountPath });
136
290
  if (this.destroyed) {
137
- await this.forceRemove().catch(() => { });
291
+ await this.removeInstance(id);
138
292
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
139
293
  }
140
- const filled = await this.fillWorkspace();
294
+ const filled = await this.fillWorkspace("minted");
141
295
  if (!filled.ok || this.destroyed) {
142
- await this.forceRemove().catch(() => { });
296
+ await this.removeInstance(id);
143
297
  if (!filled.ok && filled.standing)
144
298
  this.connectRefusal = filled.error;
145
299
  return { ok: false, error: filled.ok ? new RemoteExecutionError("connect_failed", "execution env destroyed during connect") : filled.error };
146
300
  }
147
- this.containerId = run.value.stdout.trim() || this.containerName;
301
+ this.containerId = id;
148
302
  this.handle = { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
149
303
  if (config?.secrets?.length) {
150
304
  }
151
305
  return ok(this.handle);
152
306
  }
153
- fillWorkspace() {
154
- return this.cfg.workspaceFill === undefined ? Promise.resolve({ ok: true }) : this.cfg.workspaceFill.fill(this.fillChannel(), this.cfg.mountPath, "minted");
307
+ async admitStart(config) {
308
+ const capacity = this.cfg.capacity;
309
+ if (capacity === undefined)
310
+ return { ok: true };
311
+ this.capacityDeadline ??= Date.now() + this.cfg.controlTimeoutMs;
312
+ const signal = config?.abortSignal ? AbortSignal.any([config.abortSignal, this.lifecycle.signal]) : this.lifecycle.signal;
313
+ const admitted = await capacity.admit({ name: this.containerName, read: (budgetMs, readSignal) => this.readLaneRegistry(budgetMs, readSignal), deadline: this.capacityDeadline, readBoundMs: this.cfg.controlTimeoutMs, signal });
314
+ if (!admitted.ok)
315
+ return { ok: false, error: new RemoteExecutionError(admitted.refusal.verdict === "aborted" ? "aborted" : "connect_failed", admitted.refusal.message) };
316
+ return { ok: true, ticket: admitted.ticket };
317
+ }
318
+ startOutcomeOf(r) {
319
+ if (!r.ok)
320
+ return r.error.code === "spawn_error" ? "absent" : "unknown";
321
+ if (r.value.exitCode === 0)
322
+ return "present";
323
+ return isNameConflict(r.value.stderr, this.containerName) ? "absent" : "unknown";
324
+ }
325
+ fillWorkspace(origin) {
326
+ return this.cfg.workspaceFill === undefined ? Promise.resolve({ ok: true }) : this.cfg.workspaceFill.fill(this.fillChannel(), this.cfg.mountPath, origin);
155
327
  }
156
328
  fillChannel() {
157
329
  const run = async (command, input) => {
@@ -201,47 +373,100 @@ export class RemoteLocalDockerExecutionEnv {
201
373
  if (sessionToken && sessionToken !== this.containerName) {
202
374
  return { ok: false, error: new RemoteExecutionError("connect_failed", `reconnect token '${String(sessionToken)}' does not match this env's container '${this.containerName}'`) };
203
375
  }
204
- if (this.connecting !== undefined && this.containerId === undefined) {
205
- const joined = await this.connecting;
206
- if (!joined.ok || this.containerId !== undefined)
207
- return joined;
208
- }
209
- const insp = await this.docker(["inspect", "-f", "{{.State.Running}}", this.containerName], { timeoutMs: this.cfg.controlTimeoutMs });
210
- if (insp.ok && insp.value.exitCode === 0 && insp.value.stdout.trim() === "true") {
211
- if (this.containerId === undefined && this.homeSeat.asks)
212
- await this.probeContainerShell();
213
- const home = this.containerId === undefined ? await this.homeVerdict() : undefined;
214
- if (home !== undefined)
215
- return { ok: false, error: home };
216
- if (this.destroyed || this.connectRefusal)
217
- return { ok: false, error: this.connectRefusal ?? new RemoteExecutionError("connect_failed", "execution env destroyed during reconnect") };
218
- this.containerId = this.containerName;
219
- this.handle ??= { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
220
- return ok(this.handle);
221
- }
222
- return { ok: false, error: new RemoteExecutionError("connect_failed", `container '${this.containerName}' is not running (gone or stopped) — no managed snapshot to resume`) };
223
- }
224
- async destroy() {
225
- if (this.destroyed)
226
- return;
227
- this.destroyed = true;
228
- const inflight = this.connecting;
229
- this.connecting = undefined;
230
- this.containerId = undefined;
231
- await inflight?.catch(() => { });
232
- this.floor.dispose();
233
- await this.forceRemove().catch(() => { });
376
+ if (this.containerId === undefined)
377
+ return this.connectOnce(undefined, "attach-only");
378
+ const seen = await this.sightContainer(undefined);
379
+ if (seen.kind === "ours" && seen.state === "running" && seen.id === this.containerId)
380
+ return ok(this.workspaceHandle());
381
+ if (seen.kind === "unreadable")
382
+ return { ok: false, error: seen.error };
383
+ return this.notResumable(seen.kind === "absent" ? "gone" : seen.kind === "foreign" ? "the name now holds a container this worker did not start" : seen.id !== this.containerId ? "the name now holds another container" : seen.state);
384
+ }
385
+ destroy() {
386
+ this.teardown ??= (async () => {
387
+ this.destroyed = true;
388
+ this.lifecycle.abort();
389
+ const inflight = this.connecting;
390
+ this.connecting = undefined;
391
+ this.containerId = undefined;
392
+ await inflight?.catch(() => { });
393
+ this.floor.dispose();
394
+ const owed = this.custody;
395
+ switch (owed.kind) {
396
+ case "held":
397
+ case "doomed":
398
+ await this.removeInstance(owed.id);
399
+ break;
400
+ case "doomed-unknown":
401
+ await this.claimLeftoverOfThisStart();
402
+ break;
403
+ case "none":
404
+ break;
405
+ }
406
+ })().finally(() => {
407
+ this.teardownSettled = true;
408
+ });
409
+ return this.teardown;
234
410
  }
235
411
  async cleanup() {
236
412
  return this.destroy();
237
413
  }
238
- async forceRemove() {
239
- await this.docker(["rm", "-f", this.containerName], { timeoutMs: this.cfg.controlTimeoutMs }).catch(() => { });
414
+ async reapWorkspace() {
415
+ if (this.custody.kind === "none")
416
+ this.custody = { kind: "doomed-unknown" };
417
+ await this.destroy();
418
+ return this.leftover !== undefined ? "owed" : this.removedAny ? "removed" : "nothing";
419
+ }
420
+ async removeInstance(id) {
421
+ this.custody = { kind: "doomed", id };
422
+ const r = await this.docker(["rm", "-f", id], { timeoutMs: this.cfg.controlTimeoutMs }).catch(() => undefined);
423
+ const removed = r?.ok === true && (r.value.exitCode === 0 || /no such container/i.test(r.value.stderr));
424
+ if (r?.ok === true && r.value.exitCode === 0)
425
+ this.removedAny = true;
426
+ if (removed) {
427
+ this.cfg.capacity?.removed(this.containerName);
428
+ if (this.custody.kind === "doomed" && this.custody.id === id)
429
+ this.custody = NO_CUSTODY;
430
+ }
431
+ return removed;
432
+ }
433
+ async claimLeftoverOfThisStart() {
434
+ const seen = await this.sightContainer(undefined);
435
+ switch (seen.kind) {
436
+ case "ours":
437
+ await this.removeInstance(seen.id);
438
+ return;
439
+ case "absent":
440
+ this.custody = NO_CUSTODY;
441
+ this.cfg.capacity?.removed(this.containerName);
442
+ return;
443
+ case "foreign":
444
+ this.custody = NO_CUSTODY;
445
+ return;
446
+ case "unreadable":
447
+ this.custody = { kind: "doomed-unknown" };
448
+ return;
449
+ }
450
+ }
451
+ async readLaneRegistry(budgetMs, signal) {
452
+ const r = await this.docker(["ps", "-a", "--filter", `label=${this.cfg.workerLabel}`, "--format", "{{.Names}}\t{{.State}}"], { timeoutMs: Math.max(1, budgetMs), ...(signal !== undefined ? { signal } : {}) });
453
+ if (!r.ok)
454
+ return { ok: false, reason: r.error.message };
455
+ if (r.value.exitCode !== 0)
456
+ return { ok: false, reason: r.value.stderr.trim() || `exit ${r.value.exitCode}` };
457
+ const rows = r.value.stdout
458
+ .split("\n")
459
+ .filter((line) => line.trim().length > 0)
460
+ .map((line) => {
461
+ const [name = "", state = ""] = line.split("\t");
462
+ return { name: name.trim(), state: state.trim().toLowerCase() };
463
+ });
464
+ return { ok: true, rows };
240
465
  }
241
466
  async exec(command, options) {
242
467
  if (options?.abortSignal?.aborted)
243
468
  return { ok: false, error: new ExecutionError("aborted", "exec aborted before start") };
244
- const ready = await this.ensureConnectedExec();
469
+ const ready = await this.ensureConnectedExec(options?.abortSignal);
245
470
  if (!ready.ok)
246
471
  return ready;
247
472
  const execArgs = this.startArgv(engineStart(options?.readFloor), command, { cwd: options?.cwd, env: options?.env });
@@ -258,9 +483,9 @@ export class RemoteLocalDockerExecutionEnv {
258
483
  return ok({ stdout: r.value.stdout, stderr: r.value.stderr, exitCode: r.value.exitCode });
259
484
  }
260
485
  async *execStream(command, options) {
261
- const ready = await this.ensureConnectedExec();
486
+ const ready = await this.ensureConnectedExec(options?.signal);
262
487
  if (!ready.ok)
263
- throw new RemoteExecutionError("connect_failed", ready.error.message, ready.error);
488
+ throw new RemoteExecutionError(ready.error.code === "aborted" ? "aborted" : "connect_failed", ready.error.message, ready.error);
264
489
  const execArgs = this.startArgv(engineStart(undefined), command, { cwd: options?.cwd, env: options?.env });
265
490
  if (!execArgs.ok)
266
491
  throw new RemoteExecutionError("unsupported", execArgs.error.message, execArgs.error);
@@ -392,7 +617,7 @@ export class RemoteLocalDockerExecutionEnv {
392
617
  return ok(path.posix.join(...parts));
393
618
  }
394
619
  async readBinaryFile(p, abortSignal) {
395
- const ready = await this.fsReady(p);
620
+ const ready = await this.fsReady(p, abortSignal);
396
621
  if (!ready.ok)
397
622
  return ready;
398
623
  const abs = this.resolve(p);
@@ -423,13 +648,13 @@ export class RemoteLocalDockerExecutionEnv {
423
648
  return { prepare: readyUpload((dest, signal) => this.pipeIn(content, dest, signal)), run: (cmd, signal) => this.fsExec(cmd, signal), provider: PROVIDER, logger: this.cfg.logger };
424
649
  }
425
650
  async writeFile(p, content, abortSignal) {
426
- const ready = await this.fsReady(p);
651
+ const ready = await this.fsReady(p, abortSignal);
427
652
  if (!ready.ok)
428
653
  return ready;
429
654
  return writeFileStaged(this.stagedLane(content), this.resolve(p), abortSignal);
430
655
  }
431
656
  async appendFile(p, content, abortSignal) {
432
- const ready = await this.fsReady(p);
657
+ const ready = await this.fsReady(p, abortSignal);
433
658
  if (!ready.ok)
434
659
  return ready;
435
660
  const abs = this.resolve(p);
@@ -455,7 +680,9 @@ export class RemoteLocalDockerExecutionEnv {
455
680
  if (this.destroyed)
456
681
  return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
457
682
  if (!this.containerId) {
458
- const c = await this.connect();
683
+ const c = await this.joinConnect(abortSignal);
684
+ if (!c.ok && c.error.code === "aborted" && abortSignal?.aborted)
685
+ return { ok: false, error: new ExecutionError("aborted", "exec aborted before start (file operation; the container was still connecting)", c.error) };
459
686
  if (!c.ok)
460
687
  return { ok: false, error: new ExecutionError("shell_unavailable", `local-docker connect failed: ${c.error.message}`, c.error) };
461
688
  }
@@ -512,7 +739,9 @@ export class RemoteLocalDockerExecutionEnv {
512
739
  for (const [k, v] of Object.entries(mergedEnv))
513
740
  args.push("-e", `${k}=${v}`);
514
741
  }
515
- args.push(this.containerName, ...floorArgv(launch.value, [o.shell ?? this.containerShell, "-c", command]));
742
+ if (this.instance === undefined)
743
+ return { ok: false, error: new ExecutionError("shell_unavailable", "no container instance is held by this execution env") };
744
+ args.push(this.instance, ...floorArgv(launch.value, [o.shell ?? this.containerShell, "-c", command]));
516
745
  return ok(args);
517
746
  }
518
747
  floorTransport() {
@@ -538,7 +767,12 @@ export class RemoteLocalDockerExecutionEnv {
538
767
  return r.value.exitCode === 0 ? { ok: true } : { ok: false, error: r.value.stderr.trim() || `exit ${r.value.exitCode}` };
539
768
  },
540
769
  openRelay: (identity, command, events) => {
541
- const args = ["exec", "-i", ...dockerIdentityFlags({ mode: "control", identity }), this.containerName, "/bin/sh", "-c", command];
770
+ const instance = this.instance;
771
+ if (instance === undefined) {
772
+ queueMicrotask(() => events.closed("no container instance is held by this execution env"));
773
+ return { send: () => { }, close: () => { } };
774
+ }
775
+ const args = ["exec", "-i", ...dockerIdentityFlags({ mode: "control", identity }), instance, "/bin/sh", "-c", command];
542
776
  const child = this.spawnFn(this.cfg.dockerPath, this.withHostFlag(args), { stdio: ["pipe", "pipe", "pipe"], env: this.cliEnv() });
543
777
  let closed = false;
544
778
  const close = (why) => {
@@ -567,7 +801,14 @@ export class RemoteLocalDockerExecutionEnv {
567
801
  return new Promise((resolve) => {
568
802
  let settled = false;
569
803
  let forceSettleTimer;
570
- const child = this.spawnFn(this.cfg.dockerPath, this.withHostFlag(args), { stdio: [opts?.input !== undefined ? "pipe" : "ignore", "pipe", "pipe"], env: this.cliEnv() });
804
+ let child;
805
+ try {
806
+ child = this.spawnFn(this.cfg.dockerPath, this.withHostFlag(args), { stdio: [opts?.input !== undefined ? "pipe" : "ignore", "pipe", "pipe"], env: this.cliEnv() });
807
+ }
808
+ catch (e) {
809
+ resolve({ ok: false, error: new ExecutionError("spawn_error", `docker spawn failed (is '${this.cfg.dockerPath}' installed?): ${errMsg(e)}`, e instanceof Error ? e : undefined) });
810
+ return;
811
+ }
571
812
  const killGrace = () => {
572
813
  try {
573
814
  child.kill("SIGKILL");
@@ -635,6 +876,21 @@ export class RemoteLocalDockerExecutionEnv {
635
876
  });
636
877
  });
637
878
  }
879
+ joinConnect(signal) {
880
+ const shared = this.connect();
881
+ if (signal === undefined)
882
+ return shared;
883
+ if (signal.aborted)
884
+ return Promise.resolve({ ok: false, error: new RemoteExecutionError("aborted", "aborted while connecting") });
885
+ return new Promise((resolve) => {
886
+ const onAbort = () => resolve({ ok: false, error: new RemoteExecutionError("aborted", "aborted while connecting") });
887
+ signal.addEventListener("abort", onAbort, { once: true });
888
+ void shared.then((r) => {
889
+ signal.removeEventListener("abort", onAbort);
890
+ resolve(r);
891
+ });
892
+ });
893
+ }
638
894
  containerShell = "/bin/sh";
639
895
  containerShellReady;
640
896
  async untilOrAborted(shared, signal) {
@@ -668,25 +924,27 @@ export class RemoteLocalDockerExecutionEnv {
668
924
  catch {
669
925
  }
670
926
  }
671
- async ensureConnectedExec() {
927
+ async ensureConnectedExec(signal) {
672
928
  if (this.destroyed)
673
929
  return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
674
930
  if (this.containerId) {
675
931
  await this.probeContainerShell();
676
932
  return ok(undefined);
677
933
  }
678
- const c = await this.connect();
934
+ const c = await this.joinConnect(signal);
935
+ if (!c.ok && c.error.code === "aborted" && signal?.aborted)
936
+ return { ok: false, error: new ExecutionError("aborted", "exec aborted before start (the container was still connecting)", c.error) };
679
937
  if (!c.ok)
680
938
  return { ok: false, error: new ExecutionError("shell_unavailable", `local-docker connect failed: ${c.error.message}`, c.error) };
681
939
  await this.probeContainerShell();
682
940
  return ok(undefined);
683
941
  }
684
- async fsReady(p) {
942
+ async fsReady(p, signal) {
685
943
  if (this.destroyed)
686
944
  return { ok: false, error: new FileError("unknown", "execution env already destroyed", this.resolve(p)) };
687
945
  if (this.containerId)
688
946
  return ok(undefined);
689
- const c = await this.connect();
947
+ const c = await this.joinConnect(signal);
690
948
  if (!c.ok)
691
949
  return { ok: false, error: fileErrorFromExec(c.error, this.resolve(p)) };
692
950
  return ok(undefined);
@@ -702,14 +960,51 @@ export class RemoteLocalDockerExecutionEnv {
702
960
  function sanitizeId(id) {
703
961
  return id.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 48) || "task";
704
962
  }
963
+ export function localDockerContainerNameOf(workerLabel, identity) {
964
+ const digest = createHash("sha256").update(workerLabel).update("\0").update(identity).digest("hex").slice(0, 12);
965
+ return `sema-docker-${sanitizeId(identity)}-${digest}`;
966
+ }
967
+ export function localDockerIdentityOfName(workerLabel, name) {
968
+ const m = /^sema-docker-(.+)-[0-9a-f]{12}$/.exec(name);
969
+ const candidate = m?.[1];
970
+ return candidate !== undefined && localDockerContainerNameOf(workerLabel, candidate) === name ? candidate : undefined;
971
+ }
705
972
  function errMsg(e) {
706
973
  return e instanceof Error ? e.message : String(e);
707
974
  }
708
975
  export function localDockerExecutionEnvFactory(config, deps = {}) {
709
976
  const { workspaceFillOf, ...base } = config;
710
- return (ctx) => {
711
- const workspaceFill = workspaceFillOf?.(ctx);
712
- return new RemoteLocalDockerExecutionEnv({ ...base, id: base.id ?? ctx.taskId ?? ctx.sessionId, ...(workspaceFill !== undefined ? { workspaceFill } : {}) }, deps);
977
+ const live = new Map();
978
+ const envOf = async (identity, ctx) => {
979
+ sweep();
980
+ let leftover;
981
+ for (let held = live.get(identity); held !== undefined; held = live.get(identity)) {
982
+ const releasing = held.releasing;
983
+ if (releasing === undefined)
984
+ return held;
985
+ await releasing;
986
+ if (live.get(identity) === held) {
987
+ leftover = held.leftover;
988
+ live.delete(identity);
989
+ }
990
+ }
991
+ const workspaceFill = ctx !== undefined ? workspaceFillOf?.(ctx) : undefined;
992
+ const env = new RemoteLocalDockerExecutionEnv({ ...base, id: identity, ...(workspaceFill !== undefined ? { workspaceFill } : {}), ...(leftover !== undefined ? { leftover } : {}) }, deps);
993
+ live.set(identity, env);
994
+ return env;
995
+ };
996
+ const sweep = () => {
997
+ for (const [identity, env] of live)
998
+ if (env.released && env.leftover === undefined)
999
+ live.delete(identity);
1000
+ };
1001
+ let lister;
1002
+ const reapPort = {
1003
+ label: base.workerLabel,
1004
+ list: () => (lister ??= new RemoteLocalDockerExecutionEnv({ ...base, id: "reaper-lister" }, deps)).readLaneRegistry(base.controlTimeoutMs ?? DEFAULT_CONTROL_TIMEOUT_MS, undefined),
1005
+ identityOf: (name) => localDockerIdentityOfName(base.workerLabel, name),
1006
+ settle: async (identity) => (await envOf(identity, undefined)).reapWorkspace(),
713
1007
  };
1008
+ return Object.assign((ctx) => envOf(ctx.taskId ?? ctx.sessionId, ctx), { reapPort });
714
1009
  }
715
1010
  //# sourceMappingURL=remote-env-local-docker.js.map
@@ -6,8 +6,11 @@ export function remoteScratchpadDirFor(sessionId) {
6
6
  return `/tmp/scratchpad/${sessionId}`;
7
7
  }
8
8
  export function withRemoteScratchpad(factory, logger) {
9
+ const decorated = new WeakSet();
9
10
  return async (ctx) => {
10
11
  const env = await factory(ctx);
12
+ if (decorated.has(env))
13
+ return env;
11
14
  const dir = remoteScratchpadDirFor(ctx.sessionId);
12
15
  if (dir === undefined) {
13
16
  if (ctx.sessionId)
@@ -17,6 +20,7 @@ export function withRemoteScratchpad(factory, logger) {
17
20
  const e = env;
18
21
  if (typeof e.exec !== "function")
19
22
  return env;
23
+ decorated.add(env);
20
24
  const origExec = e.exec.bind(e);
21
25
  let ensured;
22
26
  const ensure = () => (ensured ??= (async () => {