@sema-agent/server 1.323.0 → 2.0.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 (539) hide show
  1. package/dist/approval-hmac.d.ts +40 -0
  2. package/dist/approval-hmac.js +67 -0
  3. package/dist/approval.d.ts +31 -0
  4. package/dist/approval.js +72 -4
  5. package/dist/audit.d.ts +26 -0
  6. package/dist/audit.js +40 -1
  7. package/dist/auth-bridge.d.ts +10 -0
  8. package/dist/auth-bridge.js +47 -4
  9. package/dist/auth-keys.d.ts +19 -0
  10. package/dist/auth-keys.js +13 -0
  11. package/dist/bake-runner/main.d.ts +5 -0
  12. package/dist/bake-runner/main.js +43 -4
  13. package/dist/bake-runner/protocol.d.ts +57 -0
  14. package/dist/bake-runner/protocol.js +58 -3
  15. package/dist/bake-runner/runner.d.ts +54 -0
  16. package/dist/bake-runner/runner.js +87 -5
  17. package/dist/bench/l8/artifact.d.ts +63 -0
  18. package/dist/bench/l8/artifact.js +61 -2
  19. package/dist/bench/l8/escape.d.ts +36 -0
  20. package/dist/bench/l8/escape.js +25 -0
  21. package/dist/bench/l8/index.d.ts +14 -0
  22. package/dist/bench/l8/index.js +14 -0
  23. package/dist/bench/l8/probes.d.ts +74 -0
  24. package/dist/bench/l8/probes.js +60 -0
  25. package/dist/bench/l8/run-probes.d.ts +43 -0
  26. package/dist/bench/l8/run-probes.js +45 -2
  27. package/dist/bench/s1/arms.d.ts +126 -0
  28. package/dist/bench/s1/arms.js +188 -13
  29. package/dist/bench/s1/live-deps.d.ts +66 -0
  30. package/dist/bench/s1/live-deps.js +249 -11
  31. package/dist/bench/s1/oracle.d.ts +49 -0
  32. package/dist/bench/s1/oracle.js +36 -1
  33. package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
  34. package/dist/bench/s1/repair-oracle-adapter.js +30 -0
  35. package/dist/bench/s1/reviewer.d.ts +38 -0
  36. package/dist/bench/s1/reviewer.js +34 -0
  37. package/dist/bench/s1/row.d.ts +77 -0
  38. package/dist/bench/s1/row.js +62 -0
  39. package/dist/bench/s1/run-firm.d.ts +47 -0
  40. package/dist/bench/s1/run-firm.js +70 -4
  41. package/dist/bench/s1/runner-ctx.d.ts +71 -0
  42. package/dist/bench/s1/runner-ctx.js +36 -0
  43. package/dist/bench/s1/tasks.d.ts +108 -0
  44. package/dist/bench/s1/tasks.js +115 -0
  45. package/dist/boot-reclaim.d.ts +19 -0
  46. package/dist/boot-reclaim.js +20 -0
  47. package/dist/brain.d.ts +25 -0
  48. package/dist/brain.js +61 -1
  49. package/dist/budget.d.ts +66 -0
  50. package/dist/budget.js +128 -4
  51. package/dist/capabilities/builtin-tools.d.ts +3 -0
  52. package/dist/capabilities/builtin-tools.js +3 -0
  53. package/dist/capabilities/center-plugins.d.ts +17 -1
  54. package/dist/capabilities/center-plugins.js +45 -4
  55. package/dist/capabilities/center-prompts.d.ts +46 -0
  56. package/dist/capabilities/center-prompts.js +57 -3
  57. package/dist/capabilities/code-review-council.d.ts +28 -0
  58. package/dist/capabilities/code-review-council.js +40 -7
  59. package/dist/capabilities/collab-wire.d.ts +2 -0
  60. package/dist/capabilities/collab-workflows.d.ts +7 -0
  61. package/dist/capabilities/collab-workflows.js +44 -1
  62. package/dist/capabilities/oa-tools.d.ts +6 -0
  63. package/dist/capabilities/oa-tools.js +10 -0
  64. package/dist/capabilities/prompt.d.ts +14 -0
  65. package/dist/capabilities/prompt.js +14 -0
  66. package/dist/capabilities/prompts/code-review.d.ts +14 -0
  67. package/dist/capabilities/prompts/code-review.js +14 -0
  68. package/dist/capabilities/prompts/identity.d.ts +6 -0
  69. package/dist/capabilities/prompts/identity.js +6 -0
  70. package/dist/capabilities/prompts/team.d.ts +4 -0
  71. package/dist/capabilities/prompts/team.js +4 -0
  72. package/dist/capabilities/repo-tools.d.ts +11 -0
  73. package/dist/capabilities/repo-tools.js +8 -0
  74. package/dist/capabilities/sandbox-file-send.d.ts +95 -0
  75. package/dist/capabilities/sandbox-file-send.js +91 -3
  76. package/dist/capabilities/scenarios.d.ts +130 -0
  77. package/dist/capabilities/scenarios.js +180 -7
  78. package/dist/capabilities/select-environment-tool.d.ts +7 -0
  79. package/dist/capabilities/select-environment-tool.js +30 -0
  80. package/dist/capabilities/send-user-file-tool.d.ts +33 -0
  81. package/dist/capabilities/send-user-file-tool.js +65 -1
  82. package/dist/capabilities/skills.d.ts +29 -0
  83. package/dist/capabilities/skills.js +13 -2
  84. package/dist/capabilities/team.d.ts +22 -0
  85. package/dist/capabilities/team.js +22 -2
  86. package/dist/capabilities/tool-defer.d.ts +26 -0
  87. package/dist/capabilities/tool-defer.js +3 -0
  88. package/dist/config-center/apply-effective.d.ts +65 -0
  89. package/dist/config-center/apply-effective.js +204 -12
  90. package/dist/config-center/http-client.d.ts +34 -0
  91. package/dist/config-center/http-client.js +68 -0
  92. package/dist/config-center/restart-signal.d.ts +22 -0
  93. package/dist/config-center/restart-signal.js +39 -0
  94. package/dist/config-center/skills-mcp.d.ts +20 -1
  95. package/dist/config-center/skills-mcp.js +42 -7
  96. package/dist/config-center/types.d.ts +92 -1
  97. package/dist/config-lkg.d.ts +12 -0
  98. package/dist/config-lkg.js +41 -0
  99. package/dist/config-provider.d.ts +78 -0
  100. package/dist/config-provider.js +165 -2
  101. package/dist/config-types.d.ts +546 -0
  102. package/dist/config.d.ts +64 -0
  103. package/dist/config.js +455 -60
  104. package/dist/degenerate-instrument.d.ts +38 -0
  105. package/dist/degenerate-instrument.js +55 -3
  106. package/dist/elicitation.d.ts +39 -0
  107. package/dist/elicitation.js +87 -3
  108. package/dist/env-facts.d.ts +61 -0
  109. package/dist/env-facts.js +73 -7
  110. package/dist/fleet/fleet-bus.d.ts +185 -0
  111. package/dist/fleet/fleet-bus.js +333 -27
  112. package/dist/fleet/subagent-tail-bus.d.ts +11 -0
  113. package/dist/fleet/subagent-tail-bus.js +33 -2
  114. package/dist/fleet-client.d.ts +38 -0
  115. package/dist/fleet-client.js +44 -9
  116. package/dist/fleet-lease.d.ts +46 -0
  117. package/dist/fleet-lease.js +73 -5
  118. package/dist/hooks/branch-transcript.d.ts +8 -0
  119. package/dist/hooks/branch-transcript.js +33 -0
  120. package/dist/hooks/cc-stop-prompt.d.ts +42 -0
  121. package/dist/hooks/cc-stop-prompt.js +43 -1
  122. package/dist/hooks/hook-llm.d.ts +15 -0
  123. package/dist/hooks/hook-llm.js +47 -0
  124. package/dist/hooks/hook-runner.d.ts +89 -0
  125. package/dist/hooks/hook-runner.js +327 -18
  126. package/dist/http/idempotency.d.ts +31 -0
  127. package/dist/http/idempotency.js +34 -0
  128. package/dist/http/principal-gate.d.ts +17 -0
  129. package/dist/http/principal-gate.js +35 -3
  130. package/dist/http/route-ctx.d.ts +36 -0
  131. package/dist/http/routes/approvals-assistant.d.ts +25 -0
  132. package/dist/http/routes/approvals-assistant.js +253 -23
  133. package/dist/http/routes/attachments.js +22 -4
  134. package/dist/http/routes/capabilities.d.ts +7 -0
  135. package/dist/http/routes/capabilities.js +202 -1
  136. package/dist/http/routes/fleet.d.ts +6 -0
  137. package/dist/http/routes/fleet.js +94 -5
  138. package/dist/http/routes/images.d.ts +17 -0
  139. package/dist/http/routes/images.js +222 -14
  140. package/dist/http/routes/leader.d.ts +7 -0
  141. package/dist/http/routes/leader.js +10 -0
  142. package/dist/http/routes/memory-policy.d.ts +6 -0
  143. package/dist/http/routes/memory-policy.js +38 -6
  144. package/dist/http/routes/notify-wake.d.ts +10 -0
  145. package/dist/http/routes/notify-wake.js +19 -3
  146. package/dist/http/routes/observability.d.ts +6 -0
  147. package/dist/http/routes/observability.js +19 -2
  148. package/dist/http/routes/runs.d.ts +15 -0
  149. package/dist/http/routes/runs.js +464 -41
  150. package/dist/http/routes/session-sync.d.ts +15 -0
  151. package/dist/http/routes/session-sync.js +229 -19
  152. package/dist/http/routes/sessions-list.d.ts +6 -0
  153. package/dist/http/routes/sessions-list.js +35 -4
  154. package/dist/http/routes/sessions.d.ts +14 -0
  155. package/dist/http/routes/sessions.js +176 -31
  156. package/dist/http/routes/side-query.d.ts +8 -0
  157. package/dist/http/routes/side-query.js +26 -1
  158. package/dist/http/routes/tasks.d.ts +18 -0
  159. package/dist/http/routes/tasks.js +501 -38
  160. package/dist/http/routes/trace-usage.d.ts +10 -0
  161. package/dist/http/routes/trace-usage.js +66 -14
  162. package/dist/http/routes/workflows.d.ts +12 -0
  163. package/dist/http/routes/workflows.js +180 -19
  164. package/dist/http/run-meta.d.ts +11 -0
  165. package/dist/http/run-meta.js +6 -0
  166. package/dist/http/send.d.ts +19 -0
  167. package/dist/http/send.js +11 -0
  168. package/dist/http/server.d.ts +255 -0
  169. package/dist/http/server.js +921 -75
  170. package/dist/http/sse-log.d.ts +15 -0
  171. package/dist/http/sse-log.js +16 -0
  172. package/dist/http/tar.d.ts +9 -0
  173. package/dist/http/tar.js +21 -5
  174. package/dist/http/wire-gate.d.ts +9 -0
  175. package/dist/http/wire-gate.js +9 -0
  176. package/dist/http/wire-types.d.ts +187 -0
  177. package/dist/http/workspace-content.d.ts +8 -0
  178. package/dist/http/workspace-content.js +10 -0
  179. package/dist/images/bake-validate.d.ts +63 -0
  180. package/dist/images/bake-validate.js +71 -1
  181. package/dist/images/manifest.d.ts +19 -0
  182. package/dist/images/manifest.js +6 -1
  183. package/dist/index.d.ts +1 -5
  184. package/dist/index.js +10 -5
  185. package/dist/key-resolver.d.ts +23 -0
  186. package/dist/key-resolver.js +27 -2
  187. package/dist/leader/diffout.d.ts +23 -0
  188. package/dist/leader/diffout.js +8 -1
  189. package/dist/leader/diffup.d.ts +32 -0
  190. package/dist/leader/diffup.js +43 -0
  191. package/dist/leader/endpoint.d.ts +20 -0
  192. package/dist/leader/endpoint.js +25 -2
  193. package/dist/leader/fanout.d.ts +60 -0
  194. package/dist/leader/fanout.js +29 -3
  195. package/dist/leader/grader-env-factory.d.ts +74 -0
  196. package/dist/leader/grader-env-factory.js +62 -3
  197. package/dist/leader/leader.d.ts +97 -0
  198. package/dist/leader/leader.js +151 -4
  199. package/dist/leader/merge.d.ts +94 -0
  200. package/dist/leader/merge.js +112 -9
  201. package/dist/leader/planner.d.ts +52 -0
  202. package/dist/leader/planner.js +47 -3
  203. package/dist/leader/repair-oracle.d.ts +61 -0
  204. package/dist/leader/repair-oracle.js +59 -1
  205. package/dist/leader/repair-wire.d.ts +78 -0
  206. package/dist/leader/repair-wire.js +79 -2
  207. package/dist/leader/wire.d.ts +79 -0
  208. package/dist/leader/wire.js +255 -8
  209. package/dist/lsp/e2b-bridge.d.ts +16 -0
  210. package/dist/lsp/e2b-bridge.js +32 -4
  211. package/dist/lsp/e2b-manager.d.ts +10 -0
  212. package/dist/lsp/e2b-manager.js +18 -2
  213. package/dist/lsp/lsp-frames.d.ts +12 -0
  214. package/dist/lsp/lsp-frames.js +12 -0
  215. package/dist/lsp/manager.d.ts +12 -0
  216. package/dist/lsp/manager.js +35 -2
  217. package/dist/lsp/types.d.ts +10 -0
  218. package/dist/lsp/ws-transport.d.ts +12 -0
  219. package/dist/lsp/ws-transport.js +53 -5
  220. package/dist/lsp-evict.d.ts +14 -0
  221. package/dist/lsp-evict.js +15 -1
  222. package/dist/main.js +2132 -96
  223. package/dist/memory-export.d.ts +1 -0
  224. package/dist/memory-export.js +4 -0
  225. package/dist/memory-scope.d.ts +49 -0
  226. package/dist/memory-scope.js +87 -4
  227. package/dist/memory-sync-client.d.ts +24 -0
  228. package/dist/memory-sync-client.js +41 -2
  229. package/dist/memory-sync.d.ts +23 -0
  230. package/dist/memory-sync.js +70 -1
  231. package/dist/model-select.d.ts +39 -0
  232. package/dist/model-select.js +42 -1
  233. package/dist/observability/cost-quota.d.ts +20 -0
  234. package/dist/observability/cost-quota.js +4 -0
  235. package/dist/observability/cost-taxonomy.d.ts +45 -0
  236. package/dist/observability/cost-taxonomy.js +34 -0
  237. package/dist/observability/logger.d.ts +1 -0
  238. package/dist/observability/logger.js +6 -0
  239. package/dist/observability/metrics.d.ts +13 -0
  240. package/dist/observability/metrics.js +63 -0
  241. package/dist/observability/otel-exporter.d.ts +9 -0
  242. package/dist/observability/otel-exporter.js +11 -3
  243. package/dist/observability/principal-context.d.ts +9 -0
  244. package/dist/observability/principal-context.js +9 -0
  245. package/dist/observability/prompt-manifest.d.ts +37 -0
  246. package/dist/observability/prompt-manifest.js +35 -1
  247. package/dist/observability/rate-limit.d.ts +10 -0
  248. package/dist/observability/rate-limit.js +1 -0
  249. package/dist/observability/tool-trace.d.ts +24 -0
  250. package/dist/observability/tool-trace.js +41 -1
  251. package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
  252. package/dist/orchestration/hardened-vm-runner.js +119 -4
  253. package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
  254. package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
  255. package/dist/orchestration/hardened-vm-worker.js +27 -0
  256. package/dist/orchestration/subagent-steer.d.ts +44 -0
  257. package/dist/orchestration/subagent-steer.js +45 -1
  258. package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
  259. package/dist/orchestration/workflow-agent-steer.js +80 -1
  260. package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
  261. package/dist/orchestration/workflow-completion-inbox.js +283 -32
  262. package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
  263. package/dist/orchestration/workflow-notify-journal.js +187 -13
  264. package/dist/parked-decide.d.ts +61 -0
  265. package/dist/parked-decide.js +58 -1
  266. package/dist/per-task-image.d.ts +54 -0
  267. package/dist/per-task-image.js +41 -0
  268. package/dist/plan-cache-probe.d.ts +35 -0
  269. package/dist/plan-cache-probe.js +18 -3
  270. package/dist/plugins/approval-exemption-store.d.ts +7 -0
  271. package/dist/plugins/approval-exemption-store.js +28 -2
  272. package/dist/plugins/approval-store-sql.d.ts +64 -0
  273. package/dist/plugins/approval-store-sql.js +32 -0
  274. package/dist/plugins/background-agent-store-sql.d.ts +55 -0
  275. package/dist/plugins/background-agent-store-sql.js +91 -4
  276. package/dist/plugins/background-shell-support.d.ts +98 -0
  277. package/dist/plugins/background-shell-support.js +117 -13
  278. package/dist/plugins/blob-backend.d.ts +104 -0
  279. package/dist/plugins/blob-backend.js +163 -6
  280. package/dist/plugins/breaker-state-sql.d.ts +65 -1
  281. package/dist/plugins/breaker-state-sql.js +46 -8
  282. package/dist/plugins/caching-session-store.d.ts +42 -0
  283. package/dist/plugins/caching-session-store.js +63 -3
  284. package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
  285. package/dist/plugins/checkpoint-store-sql.js +239 -11
  286. package/dist/plugins/file-outcome-sink.d.ts +4 -0
  287. package/dist/plugins/file-outcome-sink.js +9 -0
  288. package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
  289. package/dist/plugins/file-resume-anchor-store.js +43 -4
  290. package/dist/plugins/file-run-store.d.ts +90 -0
  291. package/dist/plugins/file-run-store.js +145 -13
  292. package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
  293. package/dist/plugins/file-snapshot-store-sql.js +178 -10
  294. package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
  295. package/dist/plugins/file-workflow-journal-store.js +10 -0
  296. package/dist/plugins/fork-routing-session-store.d.ts +47 -0
  297. package/dist/plugins/fork-routing-session-store.js +51 -3
  298. package/dist/plugins/host-platform.d.ts +69 -0
  299. package/dist/plugins/host-platform.js +81 -2
  300. package/dist/plugins/image-bake-store-sql.d.ts +136 -1
  301. package/dist/plugins/image-bake-store-sql.js +241 -7
  302. package/dist/plugins/image-index-sql.d.ts +46 -0
  303. package/dist/plugins/image-index-sql.js +113 -4
  304. package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
  305. package/dist/plugins/k8s-bg-scripts.js +88 -4
  306. package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
  307. package/dist/plugins/k8s-exec-protocol.js +41 -0
  308. package/dist/plugins/local-checkpoint-store.d.ts +40 -0
  309. package/dist/plugins/local-checkpoint-store.js +86 -7
  310. package/dist/plugins/local-session-store.d.ts +157 -0
  311. package/dist/plugins/local-session-store.js +289 -34
  312. package/dist/plugins/local-task-attachment-store.js +14 -2
  313. package/dist/plugins/mailbox-store-sql.d.ts +38 -0
  314. package/dist/plugins/mailbox-store-sql.js +39 -6
  315. package/dist/plugins/memory-engine-pg.d.ts +30 -0
  316. package/dist/plugins/memory-engine-pg.js +122 -4
  317. package/dist/plugins/memory-engine-tidb.d.ts +26 -0
  318. package/dist/plugins/memory-engine-tidb.js +115 -5
  319. package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
  320. package/dist/plugins/memory-engine-vector-util.js +5 -0
  321. package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
  322. package/dist/plugins/memory-resume-anchor-store.js +17 -0
  323. package/dist/plugins/memory-run-store.d.ts +63 -0
  324. package/dist/plugins/memory-run-store.js +73 -8
  325. package/dist/plugins/memory-session-policy-store.d.ts +17 -0
  326. package/dist/plugins/memory-session-policy-store.js +17 -0
  327. package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
  328. package/dist/plugins/memory-sync-store-pg.js +45 -2
  329. package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
  330. package/dist/plugins/memory-sync-store-tidb.js +31 -1
  331. package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
  332. package/dist/plugins/outcome-ledger-sql.js +97 -3
  333. package/dist/plugins/pg-approval-store.d.ts +7 -0
  334. package/dist/plugins/pg-approval-store.js +7 -0
  335. package/dist/plugins/pg-breaker-state.d.ts +6 -0
  336. package/dist/plugins/pg-breaker-state.js +6 -0
  337. package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
  338. package/dist/plugins/pg-checkpoint-store.js +8 -0
  339. package/dist/plugins/pg-cost-quota.d.ts +17 -0
  340. package/dist/plugins/pg-cost-quota.js +6 -0
  341. package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
  342. package/dist/plugins/pg-file-snapshot-store.js +6 -0
  343. package/dist/plugins/pg-image-bake.d.ts +9 -0
  344. package/dist/plugins/pg-image-bake.js +9 -0
  345. package/dist/plugins/pg-image-index.d.ts +9 -0
  346. package/dist/plugins/pg-image-index.js +9 -0
  347. package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
  348. package/dist/plugins/pg-outcome-ledger.js +9 -0
  349. package/dist/plugins/pg-pool.d.ts +50 -0
  350. package/dist/plugins/pg-pool.js +78 -0
  351. package/dist/plugins/pg-query.d.ts +11 -0
  352. package/dist/plugins/pg-rate-limiter.d.ts +23 -0
  353. package/dist/plugins/pg-rate-limiter.js +12 -2
  354. package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
  355. package/dist/plugins/pg-resume-anchor-store.js +5 -0
  356. package/dist/plugins/pg-run-store.d.ts +7 -0
  357. package/dist/plugins/pg-run-store.js +7 -0
  358. package/dist/plugins/pg-safe-json.d.ts +13 -0
  359. package/dist/plugins/pg-safe-json.js +40 -4
  360. package/dist/plugins/pg-session-policy-store.d.ts +5 -0
  361. package/dist/plugins/pg-session-policy-store.js +5 -0
  362. package/dist/plugins/pg-session-storage.d.ts +72 -0
  363. package/dist/plugins/pg-session-storage.js +152 -20
  364. package/dist/plugins/pg-session-store.d.ts +10 -0
  365. package/dist/plugins/pg-session-store.js +10 -0
  366. package/dist/plugins/pg-tool-result-store.d.ts +7 -0
  367. package/dist/plugins/pg-tool-result-store.js +7 -0
  368. package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
  369. package/dist/plugins/pg-workflow-journal-store.js +7 -0
  370. package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
  371. package/dist/plugins/pg-workflow-run-store.js +7 -0
  372. package/dist/plugins/posix-shell-fs.d.ts +3 -0
  373. package/dist/plugins/posix-shell-fs.js +31 -1
  374. package/dist/plugins/remote-env-adb.d.ts +25 -0
  375. package/dist/plugins/remote-env-adb.js +95 -12
  376. package/dist/plugins/remote-env-e2b.d.ts +146 -0
  377. package/dist/plugins/remote-env-e2b.js +371 -40
  378. package/dist/plugins/remote-env-file-error.d.ts +34 -0
  379. package/dist/plugins/remote-env-file-error.js +37 -0
  380. package/dist/plugins/remote-env-host.d.ts +151 -0
  381. package/dist/plugins/remote-env-host.js +468 -70
  382. package/dist/plugins/remote-env-k8s.d.ts +135 -0
  383. package/dist/plugins/remote-env-k8s.js +318 -32
  384. package/dist/plugins/remote-env-local-docker.d.ts +65 -0
  385. package/dist/plugins/remote-env-local-docker.js +149 -24
  386. package/dist/plugins/remote-env-ssh.d.ts +35 -0
  387. package/dist/plugins/remote-env-ssh.js +117 -20
  388. package/dist/plugins/remote-scratchpad.d.ts +38 -0
  389. package/dist/plugins/remote-scratchpad.js +32 -2
  390. package/dist/plugins/remote-shell.d.ts +22 -0
  391. package/dist/plugins/remote-shell.js +27 -1
  392. package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
  393. package/dist/plugins/resume-anchor-store-sql.js +11 -0
  394. package/dist/plugins/roster-store-sql.d.ts +27 -0
  395. package/dist/plugins/roster-store-sql.js +54 -0
  396. package/dist/plugins/run-store-sql.d.ts +194 -0
  397. package/dist/plugins/run-store-sql.js +210 -9
  398. package/dist/plugins/s3-presign.d.ts +20 -0
  399. package/dist/plugins/s3-presign.js +49 -2
  400. package/dist/plugins/scheduler-support.d.ts +12 -0
  401. package/dist/plugins/scheduler-support.js +75 -3
  402. package/dist/plugins/send-file-ledger.d.ts +15 -0
  403. package/dist/plugins/send-file-ledger.js +53 -4
  404. package/dist/plugins/send-user-file.d.ts +77 -0
  405. package/dist/plugins/send-user-file.js +92 -4
  406. package/dist/plugins/session-policy-store-sql.d.ts +15 -0
  407. package/dist/plugins/session-policy-store-sql.js +75 -3
  408. package/dist/plugins/session-store.d.ts +45 -0
  409. package/dist/plugins/session-store.js +51 -0
  410. package/dist/plugins/sql-driver.d.ts +77 -0
  411. package/dist/plugins/sql-driver.js +4 -0
  412. package/dist/plugins/sql-escape.d.ts +8 -0
  413. package/dist/plugins/sql-escape.js +8 -0
  414. package/dist/plugins/sql-row-helpers.d.ts +25 -0
  415. package/dist/plugins/sql-row-helpers.js +25 -0
  416. package/dist/plugins/store-backend.d.ts +103 -0
  417. package/dist/plugins/store-backend.js +120 -27
  418. package/dist/plugins/store-contracts.d.ts +62 -0
  419. package/dist/plugins/store-contracts.js +3 -0
  420. package/dist/plugins/task-attachment-store.d.ts +60 -0
  421. package/dist/plugins/task-attachment-store.js +49 -3
  422. package/dist/plugins/task-list-store-sql.d.ts +25 -0
  423. package/dist/plugins/task-list-store-sql.js +21 -0
  424. package/dist/plugins/tidb-approval-store.d.ts +6 -0
  425. package/dist/plugins/tidb-approval-store.js +6 -0
  426. package/dist/plugins/tidb-breaker-state.d.ts +5 -0
  427. package/dist/plugins/tidb-breaker-state.js +5 -0
  428. package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
  429. package/dist/plugins/tidb-checkpoint-store.js +7 -0
  430. package/dist/plugins/tidb-cost-quota.d.ts +6 -0
  431. package/dist/plugins/tidb-cost-quota.js +1 -0
  432. package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
  433. package/dist/plugins/tidb-file-snapshot-store.js +6 -0
  434. package/dist/plugins/tidb-image-bake.d.ts +8 -0
  435. package/dist/plugins/tidb-image-bake.js +8 -0
  436. package/dist/plugins/tidb-image-index.d.ts +8 -0
  437. package/dist/plugins/tidb-image-index.js +8 -0
  438. package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
  439. package/dist/plugins/tidb-outcome-ledger.js +10 -0
  440. package/dist/plugins/tidb-pool.d.ts +56 -0
  441. package/dist/plugins/tidb-pool.js +181 -1
  442. package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
  443. package/dist/plugins/tidb-rate-limiter.js +5 -2
  444. package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
  445. package/dist/plugins/tidb-resume-anchor-store.js +5 -0
  446. package/dist/plugins/tidb-run-store.d.ts +7 -0
  447. package/dist/plugins/tidb-run-store.js +7 -0
  448. package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
  449. package/dist/plugins/tidb-session-policy-store.js +5 -0
  450. package/dist/plugins/tidb-session-storage.d.ts +43 -0
  451. package/dist/plugins/tidb-session-storage.js +62 -3
  452. package/dist/plugins/tidb-session-store.d.ts +142 -0
  453. package/dist/plugins/tidb-session-store.js +264 -16
  454. package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
  455. package/dist/plugins/tidb-tool-result-store.js +7 -0
  456. package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
  457. package/dist/plugins/tidb-workflow-journal-store.js +7 -0
  458. package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
  459. package/dist/plugins/tidb-workflow-run-store.js +8 -0
  460. package/dist/plugins/tool-result-store-sql.d.ts +58 -0
  461. package/dist/plugins/tool-result-store-sql.js +38 -2
  462. package/dist/plugins/web-search.d.ts +35 -0
  463. package/dist/plugins/web-search.js +45 -9
  464. package/dist/plugins/workflow-journal-limits.d.ts +10 -0
  465. package/dist/plugins/workflow-journal-limits.js +10 -0
  466. package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
  467. package/dist/plugins/workflow-journal-store-sql.js +58 -2
  468. package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
  469. package/dist/plugins/workflow-run-store-sql.js +84 -6
  470. package/dist/plugins/worktree-isolation.d.ts +45 -0
  471. package/dist/plugins/worktree-isolation.js +105 -6
  472. package/dist/plugins/write-behind-counter.d.ts +56 -1
  473. package/dist/plugins/write-behind-counter.js +53 -14
  474. package/dist/principal-jwt.d.ts +24 -0
  475. package/dist/principal-jwt.js +60 -5
  476. package/dist/project-memory.d.ts +22 -0
  477. package/dist/project-memory.js +144 -15
  478. package/dist/prompts-domain-validate.d.ts +28 -0
  479. package/dist/prompts-domain-validate.js +42 -1
  480. package/dist/question.d.ts +33 -0
  481. package/dist/question.js +69 -2
  482. package/dist/resource-suspend.d.ts +21 -0
  483. package/dist/resource-suspend.js +16 -0
  484. package/dist/router/route-orchestration.d.ts +78 -0
  485. package/dist/router/route-orchestration.js +77 -0
  486. package/dist/run-local.d.ts +32 -0
  487. package/dist/run-local.js +172 -8
  488. package/dist/runs.d.ts +189 -4
  489. package/dist/runs.js +465 -24
  490. package/dist/runtime-caps-resolver.d.ts +106 -0
  491. package/dist/runtime-caps-resolver.js +89 -4
  492. package/dist/runtime-governance.d.ts +84 -0
  493. package/dist/runtime-governance.js +127 -1
  494. package/dist/sandbox-pkg-source.d.ts +23 -0
  495. package/dist/sandbox-pkg-source.js +37 -0
  496. package/dist/sealed-key.d.ts +54 -0
  497. package/dist/sealed-key.js +68 -3
  498. package/dist/security.d.ts +108 -0
  499. package/dist/security.js +145 -6
  500. package/dist/sema-registry.d.ts +21 -0
  501. package/dist/sema-registry.js +21 -0
  502. package/dist/session-leaf-bus.d.ts +31 -0
  503. package/dist/session-leaf-bus.js +32 -0
  504. package/dist/session-sync-content.d.ts +46 -0
  505. package/dist/session-sync-content.js +47 -1
  506. package/dist/session-sync-kernel.d.ts +89 -0
  507. package/dist/session-sync-kernel.js +48 -3
  508. package/dist/session-sync.d.ts +125 -0
  509. package/dist/session-sync.js +181 -8
  510. package/dist/session-titler.d.ts +26 -0
  511. package/dist/session-titler.js +41 -7
  512. package/dist/session-watch.d.ts +53 -1
  513. package/dist/session-watch.js +73 -9
  514. package/dist/sighup-idle.d.ts +30 -0
  515. package/dist/sighup-idle.js +12 -1
  516. package/dist/spec-fields.d.ts +91 -3
  517. package/dist/spec-fields.js +116 -4
  518. package/dist/task-cwd.d.ts +50 -0
  519. package/dist/task-cwd.js +68 -1
  520. package/dist/task-mcp.d.ts +47 -0
  521. package/dist/task-mcp.js +29 -0
  522. package/dist/task-settings.d.ts +175 -0
  523. package/dist/task-settings.js +226 -11
  524. package/dist/task-workflow.d.ts +67 -0
  525. package/dist/task-workflow.js +75 -8
  526. package/dist/tool-approval.d.ts +91 -0
  527. package/dist/tool-approval.js +215 -0
  528. package/dist/trace/artifacts.d.ts +9 -0
  529. package/dist/trace/artifacts.js +30 -5
  530. package/dist/trace/core-keyset-guard.d.ts +18 -0
  531. package/dist/trace/ledger-sink.d.ts +43 -0
  532. package/dist/trace/ledger-sink.js +64 -5
  533. package/dist/trace/project.d.ts +133 -0
  534. package/dist/trace/project.js +189 -2
  535. package/dist/trace/redact.d.ts +19 -0
  536. package/dist/trace/redact.js +66 -9
  537. package/dist/usage-analytics.d.ts +26 -0
  538. package/dist/usage-analytics.js +26 -3
  539. package/package.json +1 -1
@@ -1,36 +1,76 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory } from "@sema-agent/core";
3
+ /** Options accepted by `ExecutionEnv.exec` (core does not re-export the type; derive it from the seam). */
3
4
  type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
5
+ /** Construction config for {@link RemoteLocalDockerExecutionEnv}. */
4
6
  export interface LocalDockerEnvConfig {
7
+ /**
8
+ * Base container image to run the task in. **REQUIRED — no default** (domestic-images iron rule: the registry
9
+ * is the operator's choice, never a hardcoded `docker.io` origin). e.g. the docker.io/claybobby pool or an
10
+ * in-network `node:20` mirror (SWR deprecated).
11
+ */
5
12
  image: string;
13
+ /** Workspace root INSIDE the container; relative paths resolve against it. Default {@link DEFAULT_MOUNT_PATH}. */
6
14
  mountPath?: string;
15
+ /** Path to the docker binary (or a docker-compatible CLI). Default "docker" (must be on PATH in the worker). */
7
16
  dockerPath?: string;
17
+ /**
18
+ * DOCKER_HOST override for the docker CLI (e.g. a non-default socket). Passed as `-H <value>` on EVERY docker
19
+ * invocation (not the process env) so it can't leak / drift. Absent = the daemon DOCKER_HOST points at.
20
+ */
8
21
  dockerHost?: string;
22
+ /** Container memory limit (docker `--memory`, e.g. "2g"). Absent = no limit. */
9
23
  memory?: string;
24
+ /** Container CPU limit (docker `--cpus`, e.g. 1.5). Absent = no limit. */
10
25
  cpus?: number;
26
+ /** Max processes in the container (docker `--pids-limit`) — a fork-bomb backstop on the shared host kernel.
27
+ * Default 512 (generous for builds; raise for heavy parallelism). Set 0/negative to disable (not advised). */
11
28
  pidsLimit?: number;
29
+ /** Drop ALL Linux capabilities (docker `--cap-drop ALL`) — hardens this code sandbox, but BREAKS workloads
30
+ * that need caps (some installs/builds). Default OFF (the container boundary + no-new-privileges + pids-limit
31
+ * are always on). Turn ON for a genuinely-untrusted lane; operators re-add caps as needed. */
12
32
  dropAllCaps?: boolean;
33
+ /** Container network mode (docker `--network`). Absent = docker default (bridge, isolated net-ns). 🔴 `"host"`
34
+ * SHARES the host network namespace — it removes the net-ns isolation dimension and exposes host-local
35
+ * services (DBs/admin ports/metadata) to agent-run code. It is NOT a true isolation boundary; use only when
36
+ * the lane is operator-trusted and needs host networking. */
13
37
  network?: "none" | "bridge" | "host";
38
+ /**
39
+ * Out-of-band env injected into EVERY command via `docker exec -e` (secrets etc.). The control plane resolves
40
+ * secret env-NAMEs from the worker's own `process.env` and passes resolved values here — they never enter the
41
+ * model prompt or the tool command string. Merged UNDER per-command `options.env` (per-call wins).
42
+ */
14
43
  env?: Record<string, string>;
44
+ /** Per-command wall-clock bound (ms) when the caller gives none. Default {@link DEFAULT_COMMAND_TIMEOUT_MS}. */
15
45
  commandTimeoutMs?: number;
46
+ /** Wall-clock bound (ms) for a docker control op (run/rm/inspect/cp). Default {@link DEFAULT_CONTROL_TIMEOUT_MS}. */
16
47
  controlTimeoutMs?: number;
48
+ /** Stable id mixed into the container NAME (e.g. the sessionId). A random suffix is always appended so two
49
+ * envs sharing an id never collide. Default = a random id. */
17
50
  id?: string;
18
51
  }
52
+ /** Inject `spawn` for tests (a fake docker CLI). Defaults to node's child_process spawn. */
19
53
  export interface LocalDockerEnvDeps {
20
54
  spawn?: typeof spawn;
21
55
  }
22
56
  export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv {
57
+ /** DUAL-MODE-DESIGN §5: a container is a real isolation boundary; v1 has no managed memory snapshot. */
23
58
  readonly capabilities: {
24
59
  isolation: boolean;
25
60
  suspendable: boolean;
26
61
  };
62
+ /** Working directory inside the container; relative paths resolve against it (ExecutionEnv contract). */
27
63
  cwd: string;
28
64
  private readonly cfg;
29
65
  private readonly spawnFn;
66
+ /** Stable container NAME (also our handle id). Created on connect; targeted by every docker exec/cp/rm. */
30
67
  private readonly containerName;
68
+ /** Set once the container is up + the workspace dir created. */
31
69
  private containerId?;
32
70
  private handle?;
71
+ /** Terminal once {@link destroy} ran — guards exec/fs from running against a removed container. */
33
72
  private destroyed;
73
+ /** In-flight connect, memoized so concurrent lazy first-use starts exactly one container (no double-run). */
34
74
  private connecting?;
35
75
  constructor(config: LocalDockerEnvConfig, deps?: LocalDockerEnvDeps);
36
76
  private resolve;
@@ -64,6 +104,11 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
64
104
  ok: false;
65
105
  error: RemoteExecutionError;
66
106
  }>;
107
+ /**
108
+ * Liveness re-check (NOT a snapshot resume): if the container is still running → idempotent success; if it's
109
+ * gone (crashed / externally removed) → `connect_failed` (no managed snapshot to re-attach to). `sessionToken`
110
+ * is the container name (== our handle id); a mismatch with this env's container fails closed.
111
+ */
67
112
  reconnect(sessionToken: SessionToken): Promise<{
68
113
  ok: true;
69
114
  value: WorkspaceHandle;
@@ -71,6 +116,7 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
71
116
  ok: false;
72
117
  error: RemoteExecutionError;
73
118
  }>;
119
+ /** `docker rm -f` the container (full teardown). Idempotent, best-effort, never throws (cleanup contract). */
74
120
  destroy(): Promise<void>;
75
121
  cleanup(): Promise<void>;
76
122
  private forceRemove;
@@ -112,17 +158,36 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
112
158
  suffix?: string;
113
159
  abortSignal?: AbortSignal;
114
160
  }): Promise<Result<string, FileError>>;
161
+ /** Prepend `-H <dockerHost>` to a docker argv when configured (per-invocation, not the process env). */
115
162
  private withHostFlag;
163
+ /** Build the `docker exec` argv for a command, applying cwd (`-w`) and per-command env (`-e K=V`).
164
+ * argv elements are SEPARATE (no host-side shell join) — the command runs in the CONTAINER's `/bin/sh -c`. */
116
165
  private execArgv;
166
+ /** Like {@link execArgv} but for an internal FileSystem helper command (uses the workspace cwd + adapter env). */
117
167
  private execRaw;
168
+ /** Spawn the docker binary with argv; collect stdout/stderr (+ raw bytes when binary), bound by timeout/abort. */
118
169
  private docker;
170
+ /** Lazy connect for an exec path → ExecutionError on failure (fail closed, never throws). */
171
+ /** S1([1870]):容器内**用户命令**的 shell。缺省 /bin/sh;首次连接后探测一次 `command -v bash`,
172
+ * 有则切 bash(TB 实测 dash 三病:$'\t' 静默错字节/[[ ]] 127/进程替换)。镜像无 bash(alpine 裸底)
173
+ * ⇒ 保 sh(修前字节形,诚实降级);探测自身失败同。容器**创建**引导命令(mkdir && tail)不走
174
+ * 本 shell——无 bash 语法且探测尚不可能。 */
119
175
  private containerShell;
120
176
  private containerShellProbed;
121
177
  private probeContainerShell;
122
178
  private ensureConnectedExec;
179
+ /** Lazy connect for a FileSystem path → FileError on failure (fail closed, never throws). */
123
180
  private fsReady;
181
+ /** Map an ExecutionError from a docker CONTROL op (run/inspect) onto a RemoteExecutionError code. */
124
182
  private dockerToRemoteError;
125
183
  }
184
+ /**
185
+ * `ExecutionEnvFactory` for the TOC `local-docker` backend (DUAL-MODE-DESIGN §5). Wiring this onto a deployment
186
+ * makes its agent run each task in a per-task container on the worker's OWN docker daemon (isolation:true,
187
+ * suspendable:false). Returns the env UNCONNECTED (lazy — the container starts on first fs/exec use); the Runner
188
+ * owns the lifetime and calls `destroy()` (= `docker rm -f`) on task end. Folds the per-task `ctx.taskId`/
189
+ * `ctx.sessionId` into the container name so two concurrent tasks never share a container.
190
+ */
126
191
  export declare function localDockerExecutionEnvFactory(config: LocalDockerEnvConfig, deps?: LocalDockerEnvDeps): ExecutionEnvFactory;
127
192
  export {};
128
193
  //# sourceMappingURL=remote-env-local-docker.d.ts.map
@@ -1,3 +1,41 @@
1
+ /**
2
+ * `RemoteLocalDockerExecutionEnv` — the TOC **local-docker** exec adapter (DUAL-MODE-DESIGN §5, the
3
+ * fast-follow promoted from the host file's typed stub). Runs the agent's bash/file tools inside a long-lived
4
+ * container ON THIS MACHINE's docker daemon, so a single-machine ("take-over-this-computer") deployment gets a
5
+ * REAL OS-level isolation boundary — the in-between lane between the un-isolated `host` adapter (no container)
6
+ * and the fleet `e2b`/`k8s` sandboxes (remote microVMs/pods).
7
+ *
8
+ * It is a PEER adapter to host / e2b / k8s / ssh / adb (NOT chained): all implement the SAME core seam
9
+ * `RemoteExecutionEnv` (core src/core/remote-env.ts, design/48 §5), selected by `remoteExec.provider` —
10
+ * this one is `provider:"local-docker"`.
11
+ *
12
+ * 🔴 **Isolation, NOT a snapshot sandbox** (DUAL-MODE-DESIGN §5 / design/61 §9):
13
+ * - `capabilities = { isolation: true, suspendable: false }`. The container is a true isolation boundary
14
+ * (separate filesystem/PID/mount namespaces), so the agent's writes/exec are confined to it and parallel
15
+ * fan-out tasks don't collide. But v1 is **NOT suspendable** — `suspendVM`/`resumeVM`/`postResumeInit`
16
+ * return typed `"unsupported"`. (`docker pause`/`docker commit` could give a coarse suspend later; we do
17
+ * NOT fake it here — durable suspend is honestly N/A until that lands.)
18
+ * - `destroy()` / `cleanup()` **`docker rm -f` the container** — a full teardown (the container IS the
19
+ * per-task sandbox, unlike host which only `rm -rf`s a dir). Idempotent, best-effort, never throws.
20
+ * - `reconnect()` is supported as a liveness re-check: if the container is still running it is an idempotent
21
+ * success; if it's gone (crashed / externally `rm`'d) it FAILS (`connect_failed`) — there is no managed
22
+ * snapshot to resume from, so a dead container cannot be re-attached.
23
+ *
24
+ * 🔴 **Domestic-images iron rule (clay):** the base `image` comes ENTIRELY from config (no hardcoded
25
+ * `docker.io/...` default). A deployment points it at the sandbox-image-pool (docker.io/claybobby public pool;
26
+ * CN fallback = in-network Gitea registry — SWR deprecated 2026-07-13). This adapter never invents a registry.
27
+ *
28
+ * 🔴 **argv anti-injection (council, host/ssh §4):** every `docker` invocation is a `spawn(binary, argv[])`
29
+ * with NO host-side shell (`shell:false` default) — the container id, image, paths, and env values are
30
+ * SEPARATE argv elements, never string-concatenated into a `sh -c` on the host. The agent's COMMAND itself is
31
+ * still a shell command (it runs the container's `/bin/sh -c <command>`, exactly like the host/ssh adapters
32
+ * run the operator's command in a shell) — that is the agent's intended surface, bounded by the container.
33
+ *
34
+ * **never-throw invariant:** every base `ExecutionEnv` FileSystem/Shell method encodes failure in its returned
35
+ * `Result` (never throws) — docker missing from PATH, a command timeout, a container that won't start, all
36
+ * become `Result.error`. `execStream`'s iterator MAY throw `RemoteExecutionError` mid-stream before an `exit`
37
+ * chunk (the seam contract). A non-zero command exit is a NORMAL value (`{ ok:true, exitCode }`), never an error.
38
+ */
1
39
  import path from "node:path";
2
40
  import os from "node:os";
3
41
  import fs from "node:fs/promises";
@@ -8,8 +46,11 @@ import { FileError, ExecutionError, RemoteExecutionError, RollingTailBuffer, mar
8
46
  import { fileErrorFromExec, classifyFsStderr } from "./remote-env-file-error.js";
9
47
  import { createPosixShellFs } from "./posix-shell-fs.js";
10
48
  const PROVIDER = "local-docker";
49
+ /** Default workspace root INSIDE the container (the agent's tools resolve relative paths against it). */
11
50
  const DEFAULT_MOUNT_PATH = "/workspace";
51
+ /** Per-command wall-clock when the caller passes none (a single exec bound, NOT the container lifetime). */
12
52
  const DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60_000;
53
+ /** Wall-clock for a control-plane docker op (run/rm/inspect/cp) — should be fast; a hang surfaces typed. */
13
54
  const DEFAULT_CONTROL_TIMEOUT_MS = 60_000;
14
55
  const ok = (value) => ({ ok: true, value });
15
56
  const unsupported = (op) => ({
@@ -17,17 +58,25 @@ const unsupported = (op) => ({
17
58
  error: new RemoteExecutionError("unsupported", `${op} is not supported on a local-docker target (v1: a container is an isolation boundary but not a managed memory snapshot; capabilities.suspendable=false)`),
18
59
  });
19
60
  export class RemoteLocalDockerExecutionEnv {
61
+ /** DUAL-MODE-DESIGN §5: a container is a real isolation boundary; v1 has no managed memory snapshot. */
20
62
  capabilities = { isolation: true, suspendable: false };
63
+ /** Working directory inside the container; relative paths resolve against it (ExecutionEnv contract). */
21
64
  cwd;
22
65
  cfg;
23
66
  spawnFn;
67
+ /** Stable container NAME (also our handle id). Created on connect; targeted by every docker exec/cp/rm. */
24
68
  containerName;
69
+ /** Set once the container is up + the workspace dir created. */
25
70
  containerId;
26
71
  handle;
72
+ /** Terminal once {@link destroy} ran — guards exec/fs from running against a removed container. */
27
73
  destroyed = false;
74
+ /** In-flight connect, memoized so concurrent lazy first-use starts exactly one container (no double-run). */
28
75
  connecting;
29
76
  constructor(config, deps = {}) {
30
77
  if (!config.image || !config.image.trim()) {
78
+ // image is required (no docker.io default) — but the constructor must not throw (never-throw posture):
79
+ // record the empty image and let connect() fail closed with a typed error.
31
80
  config = { ...config, image: config.image ?? "" };
32
81
  }
33
82
  this.cfg = {
@@ -41,7 +90,7 @@ export class RemoteLocalDockerExecutionEnv {
41
90
  this.cwd = this.cfg.mountPath;
42
91
  this.spawnFn = deps.spawn ?? spawn;
43
92
  const id = config.id ?? randomBytes(6).toString("hex");
44
- const suffix = randomBytes(4).toString("hex");
93
+ const suffix = randomBytes(4).toString("hex"); // always-unique tail so a reused id never collides
45
94
  this.containerName = `sema-docker-${sanitizeId(id)}-${suffix}`;
46
95
  }
47
96
  resolve(p) {
@@ -52,27 +101,31 @@ export class RemoteLocalDockerExecutionEnv {
52
101
  throw new RemoteExecutionError("connect_failed", "workspaceHandle() called before connect()");
53
102
  return this.handle;
54
103
  }
104
+ // ─────────────────────────────── lifecycle ───────────────────────────────
55
105
  async connect(config) {
56
106
  if (config?.snapshotId)
57
- return unsupported("connect(snapshotId)");
107
+ return unsupported("connect(snapshotId)"); // no managed snapshots
58
108
  if (this.destroyed)
59
109
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
60
110
  if (config?.abortSignal?.aborted)
61
111
  return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted") };
62
112
  if (this.containerId)
63
- return ok(this.workspaceHandle());
113
+ return ok(this.workspaceHandle()); // idempotent: already running
64
114
  if (this.connecting)
65
- return this.connecting;
115
+ return this.connecting; // concurrent first-use shares one container start
66
116
  this.connecting = this.doConnect(config);
67
117
  const r = await this.connecting;
68
118
  if (!r.ok)
69
- this.connecting = undefined;
119
+ this.connecting = undefined; // failed start → allow a later retry
70
120
  return r;
71
121
  }
72
122
  async doConnect(config) {
73
123
  if (!this.cfg.image.trim()) {
74
124
  return { ok: false, error: new RemoteExecutionError("connect_failed", "local-docker: no base image configured (the image is required — no docker.io default)") };
75
125
  }
126
+ // Start a long-lived detached container that idles forever (the agent's commands are `docker exec`'d into
127
+ // it). `tail -f /dev/null` is the most portable idle keep-alive (no `sleep infinity` dependency on some
128
+ // busybox/alpine images). --init reaps zombie exec children. The workspace dir is created via the same run.
76
129
  const pidsLimit = this.cfg.pidsLimit ?? 512;
77
130
  const runArgs = [
78
131
  "run",
@@ -82,6 +135,8 @@ export class RemoteLocalDockerExecutionEnv {
82
135
  this.containerName,
83
136
  "-w",
84
137
  this.cfg.mountPath,
138
+ // Always-safe sandbox hardening (no build breakage): block setuid privilege escalation + cap the process
139
+ // count so generated code can't fork-bomb the shared host kernel. cap-drop is opt-in (it DOES break some builds).
85
140
  "--security-opt",
86
141
  "no-new-privileges",
87
142
  ...(pidsLimit > 0 ? ["--pids-limit", String(pidsLimit)] : []),
@@ -92,17 +147,19 @@ export class RemoteLocalDockerExecutionEnv {
92
147
  this.cfg.image,
93
148
  "/bin/sh",
94
149
  "-c",
150
+ // Create the workspace then idle. mkdir -p is harmless if the image already has it.
95
151
  `mkdir -p ${shellQuote(this.cfg.mountPath)} && exec tail -f /dev/null`,
96
152
  ];
97
153
  const run = await this.docker(runArgs, { signal: config?.abortSignal, timeoutMs: this.cfg.controlTimeoutMs });
98
154
  if (!run.ok) {
99
- await this.forceRemove().catch(() => { });
155
+ await this.forceRemove().catch(() => { }); // a half-created container must not leak on a failed start
100
156
  return { ok: false, error: this.dockerToRemoteError(run.error, "connect_failed") };
101
157
  }
102
158
  if (run.value.exitCode !== 0) {
103
159
  await this.forceRemove().catch(() => { });
104
160
  return { ok: false, error: new RemoteExecutionError("connect_failed", `docker run failed: ${run.value.stderr.trim() || `exit ${run.value.exitCode}`}`) };
105
161
  }
162
+ // Race with destroy() (the awaits above yield): a destroy that landed must win — remove + bail (no orphan).
106
163
  if (this.destroyed) {
107
164
  await this.forceRemove().catch(() => { });
108
165
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
@@ -110,6 +167,7 @@ export class RemoteLocalDockerExecutionEnv {
110
167
  this.containerId = run.value.stdout.trim() || this.containerName;
111
168
  this.handle = { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
112
169
  if (config?.secrets?.length) {
170
+ // Secrets are injected per-command via cfg.env (resolved by the control plane), not at connect — record only.
113
171
  }
114
172
  return ok(this.handle);
115
173
  }
@@ -122,6 +180,11 @@ export class RemoteLocalDockerExecutionEnv {
122
180
  async postResumeInit() {
123
181
  return unsupported("postResumeInit");
124
182
  }
183
+ /**
184
+ * Liveness re-check (NOT a snapshot resume): if the container is still running → idempotent success; if it's
185
+ * gone (crashed / externally removed) → `connect_failed` (no managed snapshot to re-attach to). `sessionToken`
186
+ * is the container name (== our handle id); a mismatch with this env's container fails closed.
187
+ */
125
188
  async reconnect(sessionToken) {
126
189
  if (this.destroyed)
127
190
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
@@ -130,28 +193,39 @@ export class RemoteLocalDockerExecutionEnv {
130
193
  }
131
194
  const insp = await this.docker(["inspect", "-f", "{{.State.Running}}", this.containerName], { timeoutMs: this.cfg.controlTimeoutMs });
132
195
  if (insp.ok && insp.value.exitCode === 0 && insp.value.stdout.trim() === "true") {
196
+ // 🔴 mark CONNECTED to the live container: else ensureConnectedExec/fsReady (gate on containerId) would
197
+ // re-run `docker run --name <same>` → name conflict → the failure path rm -f's the very container we just
198
+ // re-attached to (destroying the running workspace). The name IS our exec/handle target.
133
199
  this.containerId = this.containerName;
134
200
  this.handle ??= { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
135
201
  return ok(this.handle);
136
202
  }
137
203
  return { ok: false, error: new RemoteExecutionError("connect_failed", `container '${this.containerName}' is not running (gone or stopped) — no managed snapshot to resume`) };
138
204
  }
205
+ /** `docker rm -f` the container (full teardown). Idempotent, best-effort, never throws (cleanup contract). */
139
206
  async destroy() {
140
207
  if (this.destroyed)
141
- return;
208
+ return; // idempotent — safe double-call (Runner end + reaper)
142
209
  this.destroyed = true;
210
+ // 🔴 AWAIT any racing `docker run` before reaping: a destroy that lands mid-connect must not resolve before
211
+ // the container exists, else forceRemove (by name) no-ops now and the just-created container LEAKS forever
212
+ // (docker has no self-timeout). Mirrors the host adapter's await-prepare-then-rm.
143
213
  const inflight = this.connecting;
144
214
  this.connecting = undefined;
145
215
  this.containerId = undefined;
146
216
  await inflight?.catch(() => { });
217
+ // Remove by NAME (covers a container that started but whose id we never recorded) — `rm -f` no-ops a
218
+ // never-created name; best-effort, never throws.
147
219
  await this.forceRemove().catch(() => { });
148
220
  }
149
221
  async cleanup() {
150
222
  return this.destroy();
151
223
  }
152
224
  async forceRemove() {
225
+ // best-effort, short bound; swallow everything (teardown must never throw).
153
226
  await this.docker(["rm", "-f", this.containerName], { timeoutMs: this.cfg.controlTimeoutMs }).catch(() => { });
154
227
  }
228
+ // ─────────────────────────────── Shell: buffered exec ───────────────────────────────
155
229
  async exec(command, options) {
156
230
  if (options?.abortSignal?.aborted)
157
231
  return { ok: false, error: new ExecutionError("aborted", "exec aborted before start") };
@@ -167,8 +241,10 @@ export class RemoteLocalDockerExecutionEnv {
167
241
  });
168
242
  if (!r.ok)
169
243
  return { ok: false, error: r.error };
244
+ // A non-zero exit is a NORMAL value (ExecutionEnv contract) — `docker exec` propagates the inner exit code.
170
245
  return ok({ stdout: r.value.stdout, stderr: r.value.stderr, exitCode: r.value.exitCode });
171
246
  }
247
+ // ─────────────────────────────── streaming exec ───────────────────────────────
172
248
  async *execStream(command, options) {
173
249
  const ready = await this.ensureConnectedExec();
174
250
  if (!ready.ok)
@@ -196,7 +272,7 @@ export class RemoteLocalDockerExecutionEnv {
196
272
  if (queue.length >= 512 && !paused) {
197
273
  paused = true;
198
274
  child.stdout?.pause();
199
- }
275
+ } // backpressure
200
276
  signalReady();
201
277
  };
202
278
  child.stdout?.on("data", (d) => enqueue("stdout", d.toString()));
@@ -224,7 +300,7 @@ export class RemoteLocalDockerExecutionEnv {
224
300
  try {
225
301
  child.kill("SIGKILL");
226
302
  }
227
- catch { }
303
+ catch { /* noop */ }
228
304
  }
229
305
  };
230
306
  if (options?.signal) {
@@ -236,7 +312,7 @@ export class RemoteLocalDockerExecutionEnv {
236
312
  try {
237
313
  child.kill("SIGKILL");
238
314
  }
239
- catch { }
315
+ catch { /* noop */ }
240
316
  signalReady();
241
317
  };
242
318
  if (options.signal.aborted)
@@ -254,7 +330,7 @@ export class RemoteLocalDockerExecutionEnv {
254
330
  try {
255
331
  child.kill("SIGKILL");
256
332
  }
257
- catch { }
333
+ catch { /* noop */ }
258
334
  throw new RemoteExecutionError("unknown", `execStream output exceeded maxOutputBytes=${cap}`);
259
335
  }
260
336
  }
@@ -273,7 +349,7 @@ export class RemoteLocalDockerExecutionEnv {
273
349
  try {
274
350
  child.kill("SIGKILL");
275
351
  }
276
- catch { }
352
+ catch { /* noop */ }
277
353
  throw new RemoteExecutionError("timeout", `execStream idle > ${idleMs}ms (command hang)`);
278
354
  }
279
355
  }
@@ -287,6 +363,7 @@ export class RemoteLocalDockerExecutionEnv {
287
363
  teardown();
288
364
  }
289
365
  }
366
+ // ─────────────────────────────── FileSystem ───────────────────────────────
290
367
  async absolutePath(p) {
291
368
  return ok(this.resolve(p));
292
369
  }
@@ -298,12 +375,13 @@ export class RemoteLocalDockerExecutionEnv {
298
375
  if (!ready.ok)
299
376
  return ready;
300
377
  const abs = this.resolve(p);
378
+ // `docker exec ... cat <file>` streams raw bytes on stdout (binary-safe); stderr carries cat's error.
301
379
  const r = await this.docker(this.execRaw(`cat ${shellQuote(abs)}`), { signal: abortSignal, timeoutMs: this.cfg.commandTimeoutMs, binary: true });
302
380
  if (!r.ok)
303
381
  return { ok: false, error: fileErrorFromExec(r.error, abs) };
304
382
  if (r.value.exitCode !== 0)
305
383
  return { ok: false, error: classifyFsStderr(r.value.stderr, abs) };
306
- return ok(r.value.stdoutBytes ?? new Uint8Array());
384
+ return ok(r.value.stdoutBytes ?? new Uint8Array()); // binary:true always sets stdoutBytes (empty file → empty); ?? satisfies the now-optional type
307
385
  }
308
386
  async readTextFile(p, abortSignal) {
309
387
  const r = await this.readBinaryFile(p, abortSignal);
@@ -325,6 +403,7 @@ export class RemoteLocalDockerExecutionEnv {
325
403
  if (!ready.ok)
326
404
  return ready;
327
405
  const abs = this.resolve(p);
406
+ // Create parent dirs inside the container (parity with the host/e2b adapters' auto-create).
328
407
  const dir = path.posix.dirname(abs);
329
408
  if (dir && dir !== "." && dir !== "/") {
330
409
  const mk = await this.docker(this.execRaw(`mkdir -p ${shellQuote(dir)}`), { signal: abortSignal, timeoutMs: this.cfg.commandTimeoutMs });
@@ -333,6 +412,8 @@ export class RemoteLocalDockerExecutionEnv {
333
412
  if (mk.value.exitCode !== 0)
334
413
  return { ok: false, error: classifyFsStderr(mk.value.stderr, dir) };
335
414
  }
415
+ // Stage to a host temp file then `docker cp` (binary-safe; avoids shell-escaping the payload — same
416
+ // discipline as the ADB adapter's `adb push`). `docker cp <src> <name>:<abs>` writes into the container.
336
417
  let local;
337
418
  try {
338
419
  local = path.join(os.tmpdir(), `docker-cp-${Date.now()}-${randomBytes(6).toString("hex")}`);
@@ -368,9 +449,14 @@ export class RemoteLocalDockerExecutionEnv {
368
449
  merged.set(add, base.length);
369
450
  return this.writeFile(abs, merged, abortSignal);
370
451
  }
452
+ // ── design/158 B6:POSIX shell-out 文件面全部委托共享基座(posix-shell-fs.ts)─────────────────────
453
+ // 六裁决与 readLink・lstat 契约见基座头注。本腿此前的 fsErrorFromStderr 无 not_found 缺省(缺文件报
454
+ // unknown),且 `|not found` 宽包含会把「命令缺失」误判成「文件不存在」—— 两处都被基座正形纠正。
371
455
  posixFsInst;
372
456
  get posixFs() {
373
457
  return (this.posixFsInst ??= createPosixShellFs({
458
+ // 承 fsReady 契约(其余三腿走公有 exec、守卫天然在;本腿直用私有 docker,必须自带):
459
+ // destroyed 守卫 + 懒连接,fail closed 不抛 —— 消息保留 "destroyed" 字样(测试钉的就是它)。
374
460
  run: async (cmd, abortSignal) => {
375
461
  if (this.destroyed)
376
462
  return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
@@ -412,43 +498,63 @@ export class RemoteLocalDockerExecutionEnv {
412
498
  async createTempFile(options) {
413
499
  return this.posixFs.createTempFile(options);
414
500
  }
501
+ // ─────────────────────────────── internals ───────────────────────────────
502
+ /** Prepend `-H <dockerHost>` to a docker argv when configured (per-invocation, not the process env). */
415
503
  withHostFlag(args) {
416
504
  return this.cfg.dockerHost ? ["-H", this.cfg.dockerHost, ...args] : args;
417
505
  }
506
+ /** Build the `docker exec` argv for a command, applying cwd (`-w`) and per-command env (`-e K=V`).
507
+ * argv elements are SEPARATE (no host-side shell join) — the command runs in the CONTAINER's `/bin/sh -c`. */
418
508
  execArgv(command, cwd, env) {
419
509
  const args = ["exec"];
420
510
  const dir = cwd != null ? this.resolve(cwd) : this.cwd;
421
511
  args.push("-w", dir);
422
- const mergedEnv = { ...(this.cfg.env ?? {}), ...(env ?? {}) };
512
+ const mergedEnv = { ...(this.cfg.env ?? {}), ...(env ?? {}) }; // adapter secrets base; per-command wins
423
513
  for (const [k, v] of Object.entries(mergedEnv))
424
- args.push("-e", `${k}=${v}`);
425
- args.push(this.containerName, this.containerShell, "-c", command);
514
+ args.push("-e", `${k}=${v}`); // separate argv → no injection
515
+ args.push(this.containerName, this.containerShell, "-c", command); // S1:探测后 bash 优先,无则 sh
426
516
  return args;
427
517
  }
518
+ /** Like {@link execArgv} but for an internal FileSystem helper command (uses the workspace cwd + adapter env). */
428
519
  execRaw(command) {
429
520
  return this.execArgv(command);
430
521
  }
522
+ /** Spawn the docker binary with argv; collect stdout/stderr (+ raw bytes when binary), bound by timeout/abort. */
431
523
  docker(args, opts) {
432
524
  return new Promise((resolve) => {
433
525
  let settled = false;
526
+ // D5 pipe-destroy grace: armed by killGrace() on abort/timeout; destroys stdout/stderr pipes if `close`
527
+ // never fires (a re-parented grandchild holding the FD). Cleared in finish() — the real `close` routes
528
+ // through finish() — so the timer never leaks. Does NOT change the synchronous settle below.
434
529
  let forceSettleTimer;
435
530
  const child = this.spawnFn(this.cfg.dockerPath, this.withHostFlag(args), { stdio: ["ignore", "pipe", "pipe"] });
531
+ // SIGKILL the child then arm the D5 pipe-destroy grace (only on the abort/timeout paths — a normal `close`
532
+ // never calls this). Idempotent: only one timer is ever armed.
436
533
  const killGrace = () => {
437
534
  try {
438
535
  child.kill("SIGKILL");
439
536
  }
440
- catch { }
537
+ catch { /* noop */ }
441
538
  forceSettleTimer ??= armPipeDestroyGrace(child);
442
539
  };
540
+ // 🔴 binary caveat (D2): this helper is SHARED by the model-facing TEXT exec (must be bounded — a
541
+ // `yes`/`cat big` could OOM the durable worker) and by readBinaryFile (`docker exec ... cat`, opts.binary=true,
542
+ // which reads stdoutBytes and MUST keep EVERY byte or binary/large reads silently corrupt). So we branch:
543
+ // - binary=true → full Buffer[] + concat (byte-exact stdoutBytes; readBinaryFile path).
544
+ // - binary=false → bounded rolling-TAIL for stdout (keep last 8MB + dropped-byte marker, O(maxBytes));
545
+ // no text caller reads stdoutBytes, so it is derived from the bounded text (DockerRun.stdoutBytes is non-optional).
546
+ // stderr is ALWAYS bounded — it never carries the binary payload (just cat's error text).
443
547
  const binary = !!opts?.binary;
444
- const outBufs = [];
445
- const outTail = binary ? undefined : new RollingTailBuffer();
548
+ const outBufs = []; // binary path only: full bytes for stdoutBytes
549
+ const outTail = binary ? undefined : new RollingTailBuffer(); // text path only: bounded stdout
446
550
  const errBuf = new RollingTailBuffer();
447
551
  const finish = (r) => {
448
552
  if (settled)
449
553
  return;
450
554
  settled = true;
451
555
  clearTimeout(timer);
556
+ // D5 grace cleared in the `close` handler (pipes closed naturally), NOT here — abort/timeout reaches
557
+ // finish() BEFORE `close`, so clearing here would cancel the grace before it can free a leaked FD.
452
558
  opts?.signal?.removeEventListener("abort", onAbort);
453
559
  resolve(r);
454
560
  };
@@ -469,26 +575,33 @@ export class RemoteLocalDockerExecutionEnv {
469
575
  child.stdout?.on("data", (d) => { if (binary)
470
576
  outBufs.push(d);
471
577
  else
472
- outTail.push(d); opts?.onStdout?.(d.toString()); });
578
+ outTail.push(d); opts?.onStdout?.(d.toString()); }); // streaming callback gets the full chunk; only the buffered return is bounded
473
579
  child.stderr?.on("data", (d) => { errBuf.push(d); opts?.onStderr?.(d.toString()); });
474
580
  child.on("error", (e) => finish({ ok: false, error: new ExecutionError("spawn_error", `docker spawn failed (is '${this.cfg.dockerPath}' installed?): ${e.message}`, e) }));
475
581
  child.on("close", (code, signal) => {
476
582
  if (forceSettleTimer)
477
- clearTimeout(forceSettleTimer);
583
+ clearTimeout(forceSettleTimer); // `close` fired → pipes closed naturally; clear D5 grace
584
+ // A signal-killed process (external SIGKILL/OOM) has code===null → derive the conventional 128+signo
585
+ // (parity with execStream + the host adapter); NEVER report a killed command as a success exitCode 0.
478
586
  const exitCode = code ?? (signal ? 128 + (signalNumber(signal) ?? 0) : 1);
479
587
  const err = errBuf.result();
480
588
  const stderr = markTruncated(err.text, err.droppedBytes);
481
589
  if (binary) {
482
- const outBytes = Buffer.concat(outBufs);
590
+ const outBytes = Buffer.concat(outBufs); // byte-exact: readBinaryFile decodes stdoutBytes; never truncate.
483
591
  finish(ok({ stdout: outBytes.toString("utf8"), stderr, exitCode, stdoutBytes: new Uint8Array(outBytes) }));
484
592
  }
485
593
  else {
486
- const out = outTail.result();
594
+ const out = outTail.result(); // text path: no stdoutBytes (readBinaryFile is the only reader, and it uses binary:true)
487
595
  finish(ok({ stdout: markTruncated(out.text, out.droppedBytes), stderr, exitCode }));
488
596
  }
489
597
  });
490
598
  });
491
599
  }
600
+ /** Lazy connect for an exec path → ExecutionError on failure (fail closed, never throws). */
601
+ /** S1([1870]):容器内**用户命令**的 shell。缺省 /bin/sh;首次连接后探测一次 `command -v bash`,
602
+ * 有则切 bash(TB 实测 dash 三病:$'\t' 静默错字节/[[ ]] 127/进程替换)。镜像无 bash(alpine 裸底)
603
+ * ⇒ 保 sh(修前字节形,诚实降级);探测自身失败同。容器**创建**引导命令(mkdir && tail)不走
604
+ * 本 shell——无 bash 语法且探测尚不可能。 */
492
605
  containerShell = "/bin/sh";
493
606
  containerShellProbed = false;
494
607
  async probeContainerShell() {
@@ -501,13 +614,14 @@ export class RemoteLocalDockerExecutionEnv {
501
614
  this.containerShell = "bash";
502
615
  }
503
616
  catch {
617
+ /* 保 sh —— 探测绝不 fault 连接路径 */
504
618
  }
505
619
  }
506
620
  async ensureConnectedExec() {
507
621
  if (this.destroyed)
508
622
  return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
509
623
  if (this.containerId) {
510
- await this.probeContainerShell();
624
+ await this.probeContainerShell(); // S1:已连的腿也补探测(幂等一次)
511
625
  return ok(undefined);
512
626
  }
513
627
  const c = await this.connect();
@@ -516,6 +630,7 @@ export class RemoteLocalDockerExecutionEnv {
516
630
  await this.probeContainerShell();
517
631
  return ok(undefined);
518
632
  }
633
+ /** Lazy connect for a FileSystem path → FileError on failure (fail closed, never throws). */
519
634
  async fsReady(p) {
520
635
  if (this.destroyed)
521
636
  return { ok: false, error: new FileError("unknown", "execution env already destroyed", this.resolve(p)) };
@@ -526,6 +641,7 @@ export class RemoteLocalDockerExecutionEnv {
526
641
  return { ok: false, error: new FileError("unknown", `local-docker connect failed: ${c.error.message}`, this.resolve(p), c.error) };
527
642
  return ok(undefined);
528
643
  }
644
+ /** Map an ExecutionError from a docker CONTROL op (run/inspect) onto a RemoteExecutionError code. */
529
645
  dockerToRemoteError(e, fallback) {
530
646
  if (e.code === "timeout")
531
647
  return new RemoteExecutionError("timeout", e.message, e);
@@ -534,16 +650,25 @@ export class RemoteLocalDockerExecutionEnv {
534
650
  return new RemoteExecutionError(fallback, e.message, e);
535
651
  }
536
652
  }
653
+ /** Strip anything but [A-Za-z0-9._-] from an id so it is safe in a docker container name. */
537
654
  function sanitizeId(id) {
538
655
  return id.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 48) || "task";
539
656
  }
540
657
  function errMsg(e) {
541
658
  return e instanceof Error ? e.message : String(e);
542
659
  }
660
+ /** Conventional exit code for a process killed by a signal (128 + signal number); falls back to undefined. */
543
661
  function signalNumber(signal) {
544
662
  const table = { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGKILL: 9, SIGTERM: 15, SIGSEGV: 11 };
545
663
  return table[signal];
546
664
  }
665
+ /**
666
+ * `ExecutionEnvFactory` for the TOC `local-docker` backend (DUAL-MODE-DESIGN §5). Wiring this onto a deployment
667
+ * makes its agent run each task in a per-task container on the worker's OWN docker daemon (isolation:true,
668
+ * suspendable:false). Returns the env UNCONNECTED (lazy — the container starts on first fs/exec use); the Runner
669
+ * owns the lifetime and calls `destroy()` (= `docker rm -f`) on task end. Folds the per-task `ctx.taskId`/
670
+ * `ctx.sessionId` into the container name so two concurrent tasks never share a container.
671
+ */
547
672
  export function localDockerExecutionEnvFactory(config, deps = {}) {
548
673
  return (ctx) => new RemoteLocalDockerExecutionEnv({ ...config, id: config.id ?? ctx.taskId ?? ctx.sessionId }, deps);
549
674
  }