@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.
- package/dist/approval-hmac.d.ts +40 -0
- package/dist/approval-hmac.js +67 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +72 -4
- package/dist/audit.d.ts +26 -0
- package/dist/audit.js +40 -1
- package/dist/auth-bridge.d.ts +10 -0
- package/dist/auth-bridge.js +47 -4
- package/dist/auth-keys.d.ts +19 -0
- package/dist/auth-keys.js +13 -0
- package/dist/bake-runner/main.d.ts +5 -0
- package/dist/bake-runner/main.js +43 -4
- package/dist/bake-runner/protocol.d.ts +57 -0
- package/dist/bake-runner/protocol.js +58 -3
- package/dist/bake-runner/runner.d.ts +54 -0
- package/dist/bake-runner/runner.js +87 -5
- package/dist/bench/l8/artifact.d.ts +63 -0
- package/dist/bench/l8/artifact.js +61 -2
- package/dist/bench/l8/escape.d.ts +36 -0
- package/dist/bench/l8/escape.js +25 -0
- package/dist/bench/l8/index.d.ts +14 -0
- package/dist/bench/l8/index.js +14 -0
- package/dist/bench/l8/probes.d.ts +74 -0
- package/dist/bench/l8/probes.js +60 -0
- package/dist/bench/l8/run-probes.d.ts +43 -0
- package/dist/bench/l8/run-probes.js +45 -2
- package/dist/bench/s1/arms.d.ts +126 -0
- package/dist/bench/s1/arms.js +188 -13
- package/dist/bench/s1/live-deps.d.ts +66 -0
- package/dist/bench/s1/live-deps.js +249 -11
- package/dist/bench/s1/oracle.d.ts +49 -0
- package/dist/bench/s1/oracle.js +36 -1
- package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
- package/dist/bench/s1/repair-oracle-adapter.js +30 -0
- package/dist/bench/s1/reviewer.d.ts +38 -0
- package/dist/bench/s1/reviewer.js +34 -0
- package/dist/bench/s1/row.d.ts +77 -0
- package/dist/bench/s1/row.js +62 -0
- package/dist/bench/s1/run-firm.d.ts +47 -0
- package/dist/bench/s1/run-firm.js +70 -4
- package/dist/bench/s1/runner-ctx.d.ts +71 -0
- package/dist/bench/s1/runner-ctx.js +36 -0
- package/dist/bench/s1/tasks.d.ts +108 -0
- package/dist/bench/s1/tasks.js +115 -0
- package/dist/boot-reclaim.d.ts +19 -0
- package/dist/boot-reclaim.js +20 -0
- package/dist/brain.d.ts +25 -0
- package/dist/brain.js +61 -1
- package/dist/budget.d.ts +66 -0
- package/dist/budget.js +128 -4
- package/dist/capabilities/builtin-tools.d.ts +3 -0
- package/dist/capabilities/builtin-tools.js +3 -0
- package/dist/capabilities/center-plugins.d.ts +17 -1
- package/dist/capabilities/center-plugins.js +45 -4
- package/dist/capabilities/center-prompts.d.ts +46 -0
- package/dist/capabilities/center-prompts.js +57 -3
- package/dist/capabilities/code-review-council.d.ts +28 -0
- package/dist/capabilities/code-review-council.js +40 -7
- package/dist/capabilities/collab-wire.d.ts +2 -0
- package/dist/capabilities/collab-workflows.d.ts +7 -0
- package/dist/capabilities/collab-workflows.js +44 -1
- package/dist/capabilities/oa-tools.d.ts +6 -0
- package/dist/capabilities/oa-tools.js +10 -0
- package/dist/capabilities/prompt.d.ts +14 -0
- package/dist/capabilities/prompt.js +14 -0
- package/dist/capabilities/prompts/code-review.d.ts +14 -0
- package/dist/capabilities/prompts/code-review.js +14 -0
- package/dist/capabilities/prompts/identity.d.ts +6 -0
- package/dist/capabilities/prompts/identity.js +6 -0
- package/dist/capabilities/prompts/team.d.ts +4 -0
- package/dist/capabilities/prompts/team.js +4 -0
- package/dist/capabilities/repo-tools.d.ts +11 -0
- package/dist/capabilities/repo-tools.js +8 -0
- package/dist/capabilities/sandbox-file-send.d.ts +95 -0
- package/dist/capabilities/sandbox-file-send.js +91 -3
- package/dist/capabilities/scenarios.d.ts +130 -0
- package/dist/capabilities/scenarios.js +180 -7
- package/dist/capabilities/select-environment-tool.d.ts +7 -0
- package/dist/capabilities/select-environment-tool.js +30 -0
- package/dist/capabilities/send-user-file-tool.d.ts +33 -0
- package/dist/capabilities/send-user-file-tool.js +65 -1
- package/dist/capabilities/skills.d.ts +29 -0
- package/dist/capabilities/skills.js +13 -2
- package/dist/capabilities/team.d.ts +22 -0
- package/dist/capabilities/team.js +22 -2
- package/dist/capabilities/tool-defer.d.ts +26 -0
- package/dist/capabilities/tool-defer.js +3 -0
- package/dist/config-center/apply-effective.d.ts +65 -0
- package/dist/config-center/apply-effective.js +204 -12
- package/dist/config-center/http-client.d.ts +34 -0
- package/dist/config-center/http-client.js +68 -0
- package/dist/config-center/restart-signal.d.ts +22 -0
- package/dist/config-center/restart-signal.js +39 -0
- package/dist/config-center/skills-mcp.d.ts +20 -1
- package/dist/config-center/skills-mcp.js +42 -7
- package/dist/config-center/types.d.ts +92 -1
- package/dist/config-lkg.d.ts +12 -0
- package/dist/config-lkg.js +41 -0
- package/dist/config-provider.d.ts +78 -0
- package/dist/config-provider.js +165 -2
- package/dist/config-types.d.ts +546 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.js +455 -60
- package/dist/degenerate-instrument.d.ts +38 -0
- package/dist/degenerate-instrument.js +55 -3
- package/dist/elicitation.d.ts +39 -0
- package/dist/elicitation.js +87 -3
- package/dist/env-facts.d.ts +61 -0
- package/dist/env-facts.js +73 -7
- package/dist/fleet/fleet-bus.d.ts +185 -0
- package/dist/fleet/fleet-bus.js +333 -27
- package/dist/fleet/subagent-tail-bus.d.ts +11 -0
- package/dist/fleet/subagent-tail-bus.js +33 -2
- package/dist/fleet-client.d.ts +38 -0
- package/dist/fleet-client.js +44 -9
- package/dist/fleet-lease.d.ts +46 -0
- package/dist/fleet-lease.js +73 -5
- package/dist/hooks/branch-transcript.d.ts +8 -0
- package/dist/hooks/branch-transcript.js +33 -0
- package/dist/hooks/cc-stop-prompt.d.ts +42 -0
- package/dist/hooks/cc-stop-prompt.js +43 -1
- package/dist/hooks/hook-llm.d.ts +15 -0
- package/dist/hooks/hook-llm.js +47 -0
- package/dist/hooks/hook-runner.d.ts +89 -0
- package/dist/hooks/hook-runner.js +327 -18
- package/dist/http/idempotency.d.ts +31 -0
- package/dist/http/idempotency.js +34 -0
- package/dist/http/principal-gate.d.ts +17 -0
- package/dist/http/principal-gate.js +35 -3
- package/dist/http/route-ctx.d.ts +36 -0
- package/dist/http/routes/approvals-assistant.d.ts +25 -0
- package/dist/http/routes/approvals-assistant.js +253 -23
- package/dist/http/routes/attachments.js +22 -4
- package/dist/http/routes/capabilities.d.ts +7 -0
- package/dist/http/routes/capabilities.js +202 -1
- package/dist/http/routes/fleet.d.ts +6 -0
- package/dist/http/routes/fleet.js +94 -5
- package/dist/http/routes/images.d.ts +17 -0
- package/dist/http/routes/images.js +222 -14
- package/dist/http/routes/leader.d.ts +7 -0
- package/dist/http/routes/leader.js +10 -0
- package/dist/http/routes/memory-policy.d.ts +6 -0
- package/dist/http/routes/memory-policy.js +38 -6
- package/dist/http/routes/notify-wake.d.ts +10 -0
- package/dist/http/routes/notify-wake.js +19 -3
- package/dist/http/routes/observability.d.ts +6 -0
- package/dist/http/routes/observability.js +19 -2
- package/dist/http/routes/runs.d.ts +15 -0
- package/dist/http/routes/runs.js +464 -41
- package/dist/http/routes/session-sync.d.ts +15 -0
- package/dist/http/routes/session-sync.js +229 -19
- package/dist/http/routes/sessions-list.d.ts +6 -0
- package/dist/http/routes/sessions-list.js +35 -4
- package/dist/http/routes/sessions.d.ts +14 -0
- package/dist/http/routes/sessions.js +176 -31
- package/dist/http/routes/side-query.d.ts +8 -0
- package/dist/http/routes/side-query.js +26 -1
- package/dist/http/routes/tasks.d.ts +18 -0
- package/dist/http/routes/tasks.js +501 -38
- package/dist/http/routes/trace-usage.d.ts +10 -0
- package/dist/http/routes/trace-usage.js +66 -14
- package/dist/http/routes/workflows.d.ts +12 -0
- package/dist/http/routes/workflows.js +180 -19
- package/dist/http/run-meta.d.ts +11 -0
- package/dist/http/run-meta.js +6 -0
- package/dist/http/send.d.ts +19 -0
- package/dist/http/send.js +11 -0
- package/dist/http/server.d.ts +255 -0
- package/dist/http/server.js +921 -75
- package/dist/http/sse-log.d.ts +15 -0
- package/dist/http/sse-log.js +16 -0
- package/dist/http/tar.d.ts +9 -0
- package/dist/http/tar.js +21 -5
- package/dist/http/wire-gate.d.ts +9 -0
- package/dist/http/wire-gate.js +9 -0
- package/dist/http/wire-types.d.ts +187 -0
- package/dist/http/workspace-content.d.ts +8 -0
- package/dist/http/workspace-content.js +10 -0
- package/dist/images/bake-validate.d.ts +63 -0
- package/dist/images/bake-validate.js +71 -1
- package/dist/images/manifest.d.ts +19 -0
- package/dist/images/manifest.js +6 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +10 -5
- package/dist/key-resolver.d.ts +23 -0
- package/dist/key-resolver.js +27 -2
- package/dist/leader/diffout.d.ts +23 -0
- package/dist/leader/diffout.js +8 -1
- package/dist/leader/diffup.d.ts +32 -0
- package/dist/leader/diffup.js +43 -0
- package/dist/leader/endpoint.d.ts +20 -0
- package/dist/leader/endpoint.js +25 -2
- package/dist/leader/fanout.d.ts +60 -0
- package/dist/leader/fanout.js +29 -3
- package/dist/leader/grader-env-factory.d.ts +74 -0
- package/dist/leader/grader-env-factory.js +62 -3
- package/dist/leader/leader.d.ts +97 -0
- package/dist/leader/leader.js +151 -4
- package/dist/leader/merge.d.ts +94 -0
- package/dist/leader/merge.js +112 -9
- package/dist/leader/planner.d.ts +52 -0
- package/dist/leader/planner.js +47 -3
- package/dist/leader/repair-oracle.d.ts +61 -0
- package/dist/leader/repair-oracle.js +59 -1
- package/dist/leader/repair-wire.d.ts +78 -0
- package/dist/leader/repair-wire.js +79 -2
- package/dist/leader/wire.d.ts +79 -0
- package/dist/leader/wire.js +255 -8
- package/dist/lsp/e2b-bridge.d.ts +16 -0
- package/dist/lsp/e2b-bridge.js +32 -4
- package/dist/lsp/e2b-manager.d.ts +10 -0
- package/dist/lsp/e2b-manager.js +18 -2
- package/dist/lsp/lsp-frames.d.ts +12 -0
- package/dist/lsp/lsp-frames.js +12 -0
- package/dist/lsp/manager.d.ts +12 -0
- package/dist/lsp/manager.js +35 -2
- package/dist/lsp/types.d.ts +10 -0
- package/dist/lsp/ws-transport.d.ts +12 -0
- package/dist/lsp/ws-transport.js +53 -5
- package/dist/lsp-evict.d.ts +14 -0
- package/dist/lsp-evict.js +15 -1
- package/dist/main.js +2132 -96
- package/dist/memory-export.d.ts +1 -0
- package/dist/memory-export.js +4 -0
- package/dist/memory-scope.d.ts +49 -0
- package/dist/memory-scope.js +87 -4
- package/dist/memory-sync-client.d.ts +24 -0
- package/dist/memory-sync-client.js +41 -2
- package/dist/memory-sync.d.ts +23 -0
- package/dist/memory-sync.js +70 -1
- package/dist/model-select.d.ts +39 -0
- package/dist/model-select.js +42 -1
- package/dist/observability/cost-quota.d.ts +20 -0
- package/dist/observability/cost-quota.js +4 -0
- package/dist/observability/cost-taxonomy.d.ts +45 -0
- package/dist/observability/cost-taxonomy.js +34 -0
- package/dist/observability/logger.d.ts +1 -0
- package/dist/observability/logger.js +6 -0
- package/dist/observability/metrics.d.ts +13 -0
- package/dist/observability/metrics.js +63 -0
- package/dist/observability/otel-exporter.d.ts +9 -0
- package/dist/observability/otel-exporter.js +11 -3
- package/dist/observability/principal-context.d.ts +9 -0
- package/dist/observability/principal-context.js +9 -0
- package/dist/observability/prompt-manifest.d.ts +37 -0
- package/dist/observability/prompt-manifest.js +35 -1
- package/dist/observability/rate-limit.d.ts +10 -0
- package/dist/observability/rate-limit.js +1 -0
- package/dist/observability/tool-trace.d.ts +24 -0
- package/dist/observability/tool-trace.js +41 -1
- package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
- package/dist/orchestration/hardened-vm-runner.js +119 -4
- package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
- package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
- package/dist/orchestration/hardened-vm-worker.js +27 -0
- package/dist/orchestration/subagent-steer.d.ts +44 -0
- package/dist/orchestration/subagent-steer.js +45 -1
- package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
- package/dist/orchestration/workflow-agent-steer.js +80 -1
- package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
- package/dist/orchestration/workflow-completion-inbox.js +283 -32
- package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
- package/dist/orchestration/workflow-notify-journal.js +187 -13
- package/dist/parked-decide.d.ts +61 -0
- package/dist/parked-decide.js +58 -1
- package/dist/per-task-image.d.ts +54 -0
- package/dist/per-task-image.js +41 -0
- package/dist/plan-cache-probe.d.ts +35 -0
- package/dist/plan-cache-probe.js +18 -3
- package/dist/plugins/approval-exemption-store.d.ts +7 -0
- package/dist/plugins/approval-exemption-store.js +28 -2
- package/dist/plugins/approval-store-sql.d.ts +64 -0
- package/dist/plugins/approval-store-sql.js +32 -0
- package/dist/plugins/background-agent-store-sql.d.ts +55 -0
- package/dist/plugins/background-agent-store-sql.js +91 -4
- package/dist/plugins/background-shell-support.d.ts +98 -0
- package/dist/plugins/background-shell-support.js +117 -13
- package/dist/plugins/blob-backend.d.ts +104 -0
- package/dist/plugins/blob-backend.js +163 -6
- package/dist/plugins/breaker-state-sql.d.ts +65 -1
- package/dist/plugins/breaker-state-sql.js +46 -8
- package/dist/plugins/caching-session-store.d.ts +42 -0
- package/dist/plugins/caching-session-store.js +63 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
- package/dist/plugins/checkpoint-store-sql.js +239 -11
- package/dist/plugins/file-outcome-sink.d.ts +4 -0
- package/dist/plugins/file-outcome-sink.js +9 -0
- package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
- package/dist/plugins/file-resume-anchor-store.js +43 -4
- package/dist/plugins/file-run-store.d.ts +90 -0
- package/dist/plugins/file-run-store.js +145 -13
- package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
- package/dist/plugins/file-snapshot-store-sql.js +178 -10
- package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
- package/dist/plugins/file-workflow-journal-store.js +10 -0
- package/dist/plugins/fork-routing-session-store.d.ts +47 -0
- package/dist/plugins/fork-routing-session-store.js +51 -3
- package/dist/plugins/host-platform.d.ts +69 -0
- package/dist/plugins/host-platform.js +81 -2
- package/dist/plugins/image-bake-store-sql.d.ts +136 -1
- package/dist/plugins/image-bake-store-sql.js +241 -7
- package/dist/plugins/image-index-sql.d.ts +46 -0
- package/dist/plugins/image-index-sql.js +113 -4
- package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
- package/dist/plugins/k8s-bg-scripts.js +88 -4
- package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
- package/dist/plugins/k8s-exec-protocol.js +41 -0
- package/dist/plugins/local-checkpoint-store.d.ts +40 -0
- package/dist/plugins/local-checkpoint-store.js +86 -7
- package/dist/plugins/local-session-store.d.ts +157 -0
- package/dist/plugins/local-session-store.js +289 -34
- package/dist/plugins/local-task-attachment-store.js +14 -2
- package/dist/plugins/mailbox-store-sql.d.ts +38 -0
- package/dist/plugins/mailbox-store-sql.js +39 -6
- package/dist/plugins/memory-engine-pg.d.ts +30 -0
- package/dist/plugins/memory-engine-pg.js +122 -4
- package/dist/plugins/memory-engine-tidb.d.ts +26 -0
- package/dist/plugins/memory-engine-tidb.js +115 -5
- package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
- package/dist/plugins/memory-engine-vector-util.js +5 -0
- package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
- package/dist/plugins/memory-resume-anchor-store.js +17 -0
- package/dist/plugins/memory-run-store.d.ts +63 -0
- package/dist/plugins/memory-run-store.js +73 -8
- package/dist/plugins/memory-session-policy-store.d.ts +17 -0
- package/dist/plugins/memory-session-policy-store.js +17 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
- package/dist/plugins/memory-sync-store-pg.js +45 -2
- package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
- package/dist/plugins/memory-sync-store-tidb.js +31 -1
- package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
- package/dist/plugins/outcome-ledger-sql.js +97 -3
- package/dist/plugins/pg-approval-store.d.ts +7 -0
- package/dist/plugins/pg-approval-store.js +7 -0
- package/dist/plugins/pg-breaker-state.d.ts +6 -0
- package/dist/plugins/pg-breaker-state.js +6 -0
- package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
- package/dist/plugins/pg-checkpoint-store.js +8 -0
- package/dist/plugins/pg-cost-quota.d.ts +17 -0
- package/dist/plugins/pg-cost-quota.js +6 -0
- package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/pg-file-snapshot-store.js +6 -0
- package/dist/plugins/pg-image-bake.d.ts +9 -0
- package/dist/plugins/pg-image-bake.js +9 -0
- package/dist/plugins/pg-image-index.d.ts +9 -0
- package/dist/plugins/pg-image-index.js +9 -0
- package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
- package/dist/plugins/pg-outcome-ledger.js +9 -0
- package/dist/plugins/pg-pool.d.ts +50 -0
- package/dist/plugins/pg-pool.js +78 -0
- package/dist/plugins/pg-query.d.ts +11 -0
- package/dist/plugins/pg-rate-limiter.d.ts +23 -0
- package/dist/plugins/pg-rate-limiter.js +12 -2
- package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/pg-resume-anchor-store.js +5 -0
- package/dist/plugins/pg-run-store.d.ts +7 -0
- package/dist/plugins/pg-run-store.js +7 -0
- package/dist/plugins/pg-safe-json.d.ts +13 -0
- package/dist/plugins/pg-safe-json.js +40 -4
- package/dist/plugins/pg-session-policy-store.d.ts +5 -0
- package/dist/plugins/pg-session-policy-store.js +5 -0
- package/dist/plugins/pg-session-storage.d.ts +72 -0
- package/dist/plugins/pg-session-storage.js +152 -20
- package/dist/plugins/pg-session-store.d.ts +10 -0
- package/dist/plugins/pg-session-store.js +10 -0
- package/dist/plugins/pg-tool-result-store.d.ts +7 -0
- package/dist/plugins/pg-tool-result-store.js +7 -0
- package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-journal-store.js +7 -0
- package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-run-store.js +7 -0
- package/dist/plugins/posix-shell-fs.d.ts +3 -0
- package/dist/plugins/posix-shell-fs.js +31 -1
- package/dist/plugins/remote-env-adb.d.ts +25 -0
- package/dist/plugins/remote-env-adb.js +95 -12
- package/dist/plugins/remote-env-e2b.d.ts +146 -0
- package/dist/plugins/remote-env-e2b.js +371 -40
- package/dist/plugins/remote-env-file-error.d.ts +34 -0
- package/dist/plugins/remote-env-file-error.js +37 -0
- package/dist/plugins/remote-env-host.d.ts +151 -0
- package/dist/plugins/remote-env-host.js +468 -70
- package/dist/plugins/remote-env-k8s.d.ts +135 -0
- package/dist/plugins/remote-env-k8s.js +318 -32
- package/dist/plugins/remote-env-local-docker.d.ts +65 -0
- package/dist/plugins/remote-env-local-docker.js +149 -24
- package/dist/plugins/remote-env-ssh.d.ts +35 -0
- package/dist/plugins/remote-env-ssh.js +117 -20
- package/dist/plugins/remote-scratchpad.d.ts +38 -0
- package/dist/plugins/remote-scratchpad.js +32 -2
- package/dist/plugins/remote-shell.d.ts +22 -0
- package/dist/plugins/remote-shell.js +27 -1
- package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
- package/dist/plugins/resume-anchor-store-sql.js +11 -0
- package/dist/plugins/roster-store-sql.d.ts +27 -0
- package/dist/plugins/roster-store-sql.js +54 -0
- package/dist/plugins/run-store-sql.d.ts +194 -0
- package/dist/plugins/run-store-sql.js +210 -9
- package/dist/plugins/s3-presign.d.ts +20 -0
- package/dist/plugins/s3-presign.js +49 -2
- package/dist/plugins/scheduler-support.d.ts +12 -0
- package/dist/plugins/scheduler-support.js +75 -3
- package/dist/plugins/send-file-ledger.d.ts +15 -0
- package/dist/plugins/send-file-ledger.js +53 -4
- package/dist/plugins/send-user-file.d.ts +77 -0
- package/dist/plugins/send-user-file.js +92 -4
- package/dist/plugins/session-policy-store-sql.d.ts +15 -0
- package/dist/plugins/session-policy-store-sql.js +75 -3
- package/dist/plugins/session-store.d.ts +45 -0
- package/dist/plugins/session-store.js +51 -0
- package/dist/plugins/sql-driver.d.ts +77 -0
- package/dist/plugins/sql-driver.js +4 -0
- package/dist/plugins/sql-escape.d.ts +8 -0
- package/dist/plugins/sql-escape.js +8 -0
- package/dist/plugins/sql-row-helpers.d.ts +25 -0
- package/dist/plugins/sql-row-helpers.js +25 -0
- package/dist/plugins/store-backend.d.ts +103 -0
- package/dist/plugins/store-backend.js +120 -27
- package/dist/plugins/store-contracts.d.ts +62 -0
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +60 -0
- package/dist/plugins/task-attachment-store.js +49 -3
- package/dist/plugins/task-list-store-sql.d.ts +25 -0
- package/dist/plugins/task-list-store-sql.js +21 -0
- package/dist/plugins/tidb-approval-store.d.ts +6 -0
- package/dist/plugins/tidb-approval-store.js +6 -0
- package/dist/plugins/tidb-breaker-state.d.ts +5 -0
- package/dist/plugins/tidb-breaker-state.js +5 -0
- package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
- package/dist/plugins/tidb-checkpoint-store.js +7 -0
- package/dist/plugins/tidb-cost-quota.d.ts +6 -0
- package/dist/plugins/tidb-cost-quota.js +1 -0
- package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/tidb-file-snapshot-store.js +6 -0
- package/dist/plugins/tidb-image-bake.d.ts +8 -0
- package/dist/plugins/tidb-image-bake.js +8 -0
- package/dist/plugins/tidb-image-index.d.ts +8 -0
- package/dist/plugins/tidb-image-index.js +8 -0
- package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
- package/dist/plugins/tidb-outcome-ledger.js +10 -0
- package/dist/plugins/tidb-pool.d.ts +56 -0
- package/dist/plugins/tidb-pool.js +181 -1
- package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
- package/dist/plugins/tidb-rate-limiter.js +5 -2
- package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/tidb-resume-anchor-store.js +5 -0
- package/dist/plugins/tidb-run-store.d.ts +7 -0
- package/dist/plugins/tidb-run-store.js +7 -0
- package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
- package/dist/plugins/tidb-session-policy-store.js +5 -0
- package/dist/plugins/tidb-session-storage.d.ts +43 -0
- package/dist/plugins/tidb-session-storage.js +62 -3
- package/dist/plugins/tidb-session-store.d.ts +142 -0
- package/dist/plugins/tidb-session-store.js +264 -16
- package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
- package/dist/plugins/tidb-tool-result-store.js +7 -0
- package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/tidb-workflow-journal-store.js +7 -0
- package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
- package/dist/plugins/tidb-workflow-run-store.js +8 -0
- package/dist/plugins/tool-result-store-sql.d.ts +58 -0
- package/dist/plugins/tool-result-store-sql.js +38 -2
- package/dist/plugins/web-search.d.ts +35 -0
- package/dist/plugins/web-search.js +45 -9
- package/dist/plugins/workflow-journal-limits.d.ts +10 -0
- package/dist/plugins/workflow-journal-limits.js +10 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
- package/dist/plugins/workflow-journal-store-sql.js +58 -2
- package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
- package/dist/plugins/workflow-run-store-sql.js +84 -6
- package/dist/plugins/worktree-isolation.d.ts +45 -0
- package/dist/plugins/worktree-isolation.js +105 -6
- package/dist/plugins/write-behind-counter.d.ts +56 -1
- package/dist/plugins/write-behind-counter.js +53 -14
- package/dist/principal-jwt.d.ts +24 -0
- package/dist/principal-jwt.js +60 -5
- package/dist/project-memory.d.ts +22 -0
- package/dist/project-memory.js +144 -15
- package/dist/prompts-domain-validate.d.ts +28 -0
- package/dist/prompts-domain-validate.js +42 -1
- package/dist/question.d.ts +33 -0
- package/dist/question.js +69 -2
- package/dist/resource-suspend.d.ts +21 -0
- package/dist/resource-suspend.js +16 -0
- package/dist/router/route-orchestration.d.ts +78 -0
- package/dist/router/route-orchestration.js +77 -0
- package/dist/run-local.d.ts +32 -0
- package/dist/run-local.js +172 -8
- package/dist/runs.d.ts +189 -4
- package/dist/runs.js +465 -24
- package/dist/runtime-caps-resolver.d.ts +106 -0
- package/dist/runtime-caps-resolver.js +89 -4
- package/dist/runtime-governance.d.ts +84 -0
- package/dist/runtime-governance.js +127 -1
- package/dist/sandbox-pkg-source.d.ts +23 -0
- package/dist/sandbox-pkg-source.js +37 -0
- package/dist/sealed-key.d.ts +54 -0
- package/dist/sealed-key.js +68 -3
- package/dist/security.d.ts +108 -0
- package/dist/security.js +145 -6
- package/dist/sema-registry.d.ts +21 -0
- package/dist/sema-registry.js +21 -0
- package/dist/session-leaf-bus.d.ts +31 -0
- package/dist/session-leaf-bus.js +32 -0
- package/dist/session-sync-content.d.ts +46 -0
- package/dist/session-sync-content.js +47 -1
- package/dist/session-sync-kernel.d.ts +89 -0
- package/dist/session-sync-kernel.js +48 -3
- package/dist/session-sync.d.ts +125 -0
- package/dist/session-sync.js +181 -8
- package/dist/session-titler.d.ts +26 -0
- package/dist/session-titler.js +41 -7
- package/dist/session-watch.d.ts +53 -1
- package/dist/session-watch.js +73 -9
- package/dist/sighup-idle.d.ts +30 -0
- package/dist/sighup-idle.js +12 -1
- package/dist/spec-fields.d.ts +91 -3
- package/dist/spec-fields.js +116 -4
- package/dist/task-cwd.d.ts +50 -0
- package/dist/task-cwd.js +68 -1
- package/dist/task-mcp.d.ts +47 -0
- package/dist/task-mcp.js +29 -0
- package/dist/task-settings.d.ts +175 -0
- package/dist/task-settings.js +226 -11
- package/dist/task-workflow.d.ts +67 -0
- package/dist/task-workflow.js +75 -8
- package/dist/tool-approval.d.ts +91 -0
- package/dist/tool-approval.js +215 -0
- package/dist/trace/artifacts.d.ts +9 -0
- package/dist/trace/artifacts.js +30 -5
- package/dist/trace/core-keyset-guard.d.ts +18 -0
- package/dist/trace/ledger-sink.d.ts +43 -0
- package/dist/trace/ledger-sink.js +64 -5
- package/dist/trace/project.d.ts +133 -0
- package/dist/trace/project.js +189 -2
- package/dist/trace/redact.d.ts +19 -0
- package/dist/trace/redact.js +66 -9
- package/dist/usage-analytics.d.ts +26 -0
- package/dist/usage-analytics.js +26 -3
- 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
|
}
|