@sema-agent/server 1.323.0 → 2.0.1
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/budget-tracing.d.ts +48 -0
- package/dist/boot/budget-tracing.js +86 -0
- package/dist/boot/config-center.d.ts +62 -0
- package/dist/boot/config-center.js +995 -0
- package/dist/boot/coordinators.d.ts +33 -0
- package/dist/boot/coordinators.js +97 -0
- package/dist/boot/execution-env.d.ts +26 -0
- package/dist/boot/execution-env.js +370 -0
- package/dist/boot/leader.d.ts +27 -0
- package/dist/boot/leader.js +81 -0
- package/dist/boot/reapers.d.ts +53 -0
- package/dist/boot/reapers.js +252 -0
- package/dist/boot/resolve-spec.d.ts +70 -0
- package/dist/boot/resolve-spec.js +1072 -0
- package/dist/boot/runner-deps.d.ts +101 -0
- package/dist/boot/runner-deps.js +343 -0
- package/dist/boot/runtime-caps.d.ts +21 -0
- package/dist/boot/runtime-caps.js +62 -0
- package/dist/boot/session-faces.d.ts +57 -0
- package/dist/boot/session-faces.js +157 -0
- package/dist/boot/shutdown.d.ts +50 -0
- package/dist/boot/shutdown.js +129 -0
- package/dist/boot/stores.d.ts +32 -0
- package/dist/boot/stores.js +361 -0
- package/dist/boot/workflow-orchestration.d.ts +46 -0
- package/dist/boot/workflow-orchestration.js +150 -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 +585 -1
- package/dist/config.d.ts +78 -2
- package/dist/config.js +874 -301
- 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 +530 -2129
- 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
|
@@ -4,12 +4,18 @@ export interface SshEnvConfig {
|
|
|
4
4
|
host: string;
|
|
5
5
|
port?: number;
|
|
6
6
|
username: string;
|
|
7
|
+
/** PEM private key — from the adapter-held secret env (NEVER the workspace). design/61 §5 #2. */
|
|
7
8
|
privateKey: string;
|
|
8
9
|
passphrase?: string;
|
|
10
|
+
/** Workspace root; relative paths resolve against it. Default "." (the login dir). */
|
|
9
11
|
mountPath?: string;
|
|
12
|
+
/** Connect handshake bound (ms). */
|
|
10
13
|
readyTimeoutMs?: number;
|
|
14
|
+
/** Per-command wall-clock bound (ms) when the caller gives none — NOT the connection lifetime. */
|
|
11
15
|
commandTimeoutMs?: number;
|
|
16
|
+
/** File-transfer (sftp) bound (ms). */
|
|
12
17
|
dataTimeoutMs?: number;
|
|
18
|
+
/** connect retry (idempotent establishment only — withRetry, design/61 §2A). */
|
|
13
19
|
retry?: {
|
|
14
20
|
maxAttempts?: number;
|
|
15
21
|
backoffMs?: number;
|
|
@@ -22,6 +28,11 @@ type ConnectResult = {
|
|
|
22
28
|
ok: false;
|
|
23
29
|
error: RemoteExecutionError;
|
|
24
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Test seam (design/61 §9.3 fault-injection): inject a `connectOnce` that replays an error sequence, so the
|
|
33
|
+
* retry policy (retryable codes / maxAttempts / "first fail → second success") is deterministically testable
|
|
34
|
+
* without a live host.
|
|
35
|
+
*/
|
|
25
36
|
export interface SshEnvDeps {
|
|
26
37
|
connectOnce?: (signal?: AbortSignal) => Promise<ConnectResult>;
|
|
27
38
|
}
|
|
@@ -30,11 +41,21 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
30
41
|
isolation: boolean;
|
|
31
42
|
suspendable: boolean;
|
|
32
43
|
};
|
|
44
|
+
/** Working directory; relative paths resolve against it (ExecutionEnv contract). Starts at mountPath. */
|
|
33
45
|
cwd: string;
|
|
34
46
|
private conn?;
|
|
35
47
|
private sftpPromise?;
|
|
36
48
|
private destroyed;
|
|
49
|
+
/** [1165] 代际闸:destroy/reconnect 各自 +1。逻辑连接操作(startConnect)起飞时抓拍一次并贯穿全部
|
|
50
|
+
* 重试轮(四轮复审:抓拍若在 connectOnce 内,被顶替代的下一轮重试会抓到**新**代际混进新代)——
|
|
51
|
+
* 每轮起跳先查代际/destroyed(陈旧=不再开 socket),ready 到达再查一次(在飞握手 late-ready=end
|
|
52
|
+
* 不赋 conn)。关死「A 在飞→reconnect 起 B→A 竞后抢占/成孤儿活连接」的全部竞窗。 */
|
|
37
53
|
private connEpoch;
|
|
54
|
+
/** [1165] 四轮复审 single-flight:connect()/reconnect()/懒连接三路共用一条代际键在飞记录——任何一路
|
|
55
|
+
* 在飞时其余路 join 同一 promise(同代际),绝不并起两条连接;settle 时按记录身份清(旧败诺清不掉
|
|
56
|
+
* 新记录)。五轮复审:①记录带 kind——并发 reconnect join 已在飞的 replace 记录(不再各自 bump 代际
|
|
57
|
+
* =重连风暴);②共享尝试**不携带任何 caller 信号**(首创者的 pre-abort 不得毒化 joiner;单 caller
|
|
58
|
+
* 取消只退它自己的等待,底层握手余命 ≤ readyTimeout 有界,彻底取消归 destroy 代际闸)。 */
|
|
38
59
|
private inFlight?;
|
|
39
60
|
private readonly cfg;
|
|
40
61
|
private readonly injectedConnectOnce?;
|
|
@@ -48,8 +69,15 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
48
69
|
ok: false;
|
|
49
70
|
error: RemoteExecutionError;
|
|
50
71
|
}>;
|
|
72
|
+
/** 五轮复审:per-caller 可中止等待(共享诺不动,只退本 caller)。 */
|
|
51
73
|
private raceConnAbort;
|
|
74
|
+
/** [1165] 四轮复审:三路(connect/reconnect/懒连接)唯一的连接发起点——同代际在飞记录 join,settle
|
|
75
|
+
* 身份清;代际抓拍一次贯穿全部重试轮,每轮起跳先查(陈旧/destroyed=不再开 socket,用不可重试的
|
|
76
|
+
* aborted 码即刻收敛 withRetry)。五轮复审:共享尝试零 caller 信号(取消面全在 per-caller race)。 */
|
|
52
77
|
private startConnect;
|
|
78
|
+
/** [1165] exec/execStream/sftp 共用的懒连接入口。已连=零开销直过;未连=走完整 connect()(含重试策略)。
|
|
79
|
+
* caller 的 signal 只中止**它自己的等待**(共享在飞尝试继续伺服其他等待者;彻底取消归 destroy 的代际
|
|
80
|
+
* 闸管,handshake 余命 ≤ readyTimeout 有界)。 */
|
|
53
81
|
private ensureConnected;
|
|
54
82
|
private connectOnce;
|
|
55
83
|
suspendVM(_options?: VmLifecycleOptions): Promise<{
|
|
@@ -73,6 +101,7 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
73
101
|
ok: false;
|
|
74
102
|
error: RemoteExecutionError;
|
|
75
103
|
}>;
|
|
104
|
+
/** Re-establish a dropped connection (supported — unlike suspend, reconnect is idempotent establishment). */
|
|
76
105
|
reconnect(_sessionToken: SessionToken): Promise<{
|
|
77
106
|
ok: true;
|
|
78
107
|
value: WorkspaceHandle;
|
|
@@ -80,6 +109,7 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
80
109
|
ok: false;
|
|
81
110
|
error: RemoteExecutionError;
|
|
82
111
|
}>;
|
|
112
|
+
/** Close the SSH connection. Does NOT destroy the machine (real system) — idempotent, never throws. */
|
|
83
113
|
destroy(): Promise<void>;
|
|
84
114
|
cleanup(): Promise<void>;
|
|
85
115
|
exec(command: string, options?: ExecOpts): Promise<Result<{
|
|
@@ -125,6 +155,11 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
125
155
|
private connErr;
|
|
126
156
|
private fileError;
|
|
127
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* `ExecutionEnvFactory` for the SSH backend — one fresh (unconnected) env per task against the same host
|
|
160
|
+
* (lazy: connects on first use). Config (host/user/key) is control-plane-supplied; the Runner owns the
|
|
161
|
+
* lifetime and calls `destroy()` (= disconnect) on task end.
|
|
162
|
+
*/
|
|
128
163
|
export declare function sshExecutionEnvFactory(config: SshEnvConfig): ExecutionEnvFactory;
|
|
129
164
|
export {};
|
|
130
165
|
//# sourceMappingURL=remote-env-ssh.d.ts.map
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RemoteSshExecutionEnv` — the SSH implementation of core's `RemoteExecutionEnv` seam (design/61). A PEER
|
|
3
|
+
* adapter to the E2B one (NOT chained): `docker(agent) → [SSH adapter] → real machine`. Use case: an AI
|
|
4
|
+
* orchestrating batch deployment over SSH-reachable hosts.
|
|
5
|
+
*
|
|
6
|
+
* 🔴 **SSH targets a REAL system, not a disposable sandbox** (design/61 §5):
|
|
7
|
+
* - `capabilities = { isolation: false, suspendable: false }` — actions are PERMANENT, no snapshot. The
|
|
8
|
+
* orchestrator/leader tightens autonomy + the design/37 policy gate + HITL on a non-isolated env (design/53).
|
|
9
|
+
* - `destroy()` / `cleanup()` **close the SSH connection** — they do NOT destroy the machine (vs E2B's
|
|
10
|
+
* teardown). Anything the agent created on the host PERSISTS (cleanup is the gate/operator's job).
|
|
11
|
+
* - `suspendVM`/`resumeVM`/`postResumeInit` → typed `"unsupported"` (a real machine isn't snapshotable;
|
|
12
|
+
* design/45 durable suspend N/A). `reconnect` re-establishes the connection.
|
|
13
|
+
*
|
|
14
|
+
* 🔴 **Credential isolation (design/61 §5 #2 / design/53):** the private key is held by the control plane and
|
|
15
|
+
* injected to the ADAPTER as a secret env (`SecretRef.destination:"adapter"`) — it NEVER enters the workspace
|
|
16
|
+
* / agent-reachable surface. The agent operates via exec/sftp; it cannot read the key.
|
|
17
|
+
*
|
|
18
|
+
* Liveness: every SSH op is a network round-trip that can hang — `connect` is wrapped in `withRetry` (idempotent
|
|
19
|
+
* establishment only), exec/sftp carry a bounded timeout, and a hang surfaces a typed retryable error (the
|
|
20
|
+
* shared timeout/liveness contract, reused).
|
|
21
|
+
*/
|
|
1
22
|
import path from "node:path";
|
|
2
23
|
import { Client } from "ssh2";
|
|
3
24
|
import { shellQuote, kindFromMode } from "./remote-shell.js";
|
|
@@ -12,11 +33,21 @@ const unsupported = (op) => ({
|
|
|
12
33
|
});
|
|
13
34
|
export class RemoteSshExecutionEnv {
|
|
14
35
|
capabilities = { isolation: false, suspendable: false };
|
|
36
|
+
/** Working directory; relative paths resolve against it (ExecutionEnv contract). Starts at mountPath. */
|
|
15
37
|
cwd;
|
|
16
38
|
conn;
|
|
17
39
|
sftpPromise;
|
|
18
40
|
destroyed = false;
|
|
41
|
+
/** [1165] 代际闸:destroy/reconnect 各自 +1。逻辑连接操作(startConnect)起飞时抓拍一次并贯穿全部
|
|
42
|
+
* 重试轮(四轮复审:抓拍若在 connectOnce 内,被顶替代的下一轮重试会抓到**新**代际混进新代)——
|
|
43
|
+
* 每轮起跳先查代际/destroyed(陈旧=不再开 socket),ready 到达再查一次(在飞握手 late-ready=end
|
|
44
|
+
* 不赋 conn)。关死「A 在飞→reconnect 起 B→A 竞后抢占/成孤儿活连接」的全部竞窗。 */
|
|
19
45
|
connEpoch = 0;
|
|
46
|
+
/** [1165] 四轮复审 single-flight:connect()/reconnect()/懒连接三路共用一条代际键在飞记录——任何一路
|
|
47
|
+
* 在飞时其余路 join 同一 promise(同代际),绝不并起两条连接;settle 时按记录身份清(旧败诺清不掉
|
|
48
|
+
* 新记录)。五轮复审:①记录带 kind——并发 reconnect join 已在飞的 replace 记录(不再各自 bump 代际
|
|
49
|
+
* =重连风暴);②共享尝试**不携带任何 caller 信号**(首创者的 pre-abort 不得毒化 joiner;单 caller
|
|
50
|
+
* 取消只退它自己的等待,底层握手余命 ≤ readyTimeout 有界,彻底取消归 destroy 代际闸)。 */
|
|
20
51
|
inFlight;
|
|
21
52
|
cfg;
|
|
22
53
|
injectedConnectOnce;
|
|
@@ -42,13 +73,20 @@ export class RemoteSshExecutionEnv {
|
|
|
42
73
|
workspaceHandle() {
|
|
43
74
|
return { sandboxId: `${this.cfg.host}:${this.cfg.port}`, provider: PROVIDER, mountPath: this.cwd };
|
|
44
75
|
}
|
|
76
|
+
// ─────────────────────────────── lifecycle ───────────────────────────────
|
|
45
77
|
async connect(config) {
|
|
78
|
+
// [1165] 三轮复审:destroy 后一律不复活(显式 connect/reconnect 同禁)——destroyed env 是 teardown
|
|
79
|
+
// 终态,复活=任务清理后还能执行命令/留活连接(资源与安全双漏)。恢复流程走工厂新铸实例。
|
|
46
80
|
if (this.destroyed)
|
|
47
81
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "SSH env destroyed — create a fresh env instead of reviving") };
|
|
82
|
+
// 已连=幂等成功(四轮复审:旧形二次 connect 会并起第二条连接顶掉第一条=孤儿活连接)。
|
|
48
83
|
if (this.conn)
|
|
49
84
|
return ok(this.workspaceHandle());
|
|
85
|
+
// 五轮复审:caller 信号只管 caller 自己的等待(race),共享尝试不沾——pre-abort 的首创者不得
|
|
86
|
+
// 毒化同拍 joiner,反向 pre-abort 的 joiner 也必须即时退出。
|
|
50
87
|
return this.raceConnAbort(this.startConnect("establish"), config?.abortSignal);
|
|
51
88
|
}
|
|
89
|
+
/** 五轮复审:per-caller 可中止等待(共享诺不动,只退本 caller)。 */
|
|
52
90
|
async raceConnAbort(shared, signal) {
|
|
53
91
|
if (!signal)
|
|
54
92
|
return shared;
|
|
@@ -66,12 +104,15 @@ export class RemoteSshExecutionEnv {
|
|
|
66
104
|
return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted while waiting") };
|
|
67
105
|
return raced;
|
|
68
106
|
}
|
|
107
|
+
/** [1165] 四轮复审:三路(connect/reconnect/懒连接)唯一的连接发起点——同代际在飞记录 join,settle
|
|
108
|
+
* 身份清;代际抓拍一次贯穿全部重试轮,每轮起跳先查(陈旧/destroyed=不再开 socket,用不可重试的
|
|
109
|
+
* aborted 码即刻收敛 withRetry)。五轮复审:共享尝试零 caller 信号(取消面全在 per-caller race)。 */
|
|
69
110
|
startConnect(kind) {
|
|
70
111
|
if (this.inFlight && this.inFlight.epoch === this.connEpoch)
|
|
71
112
|
return this.inFlight.promise;
|
|
72
113
|
const epoch = this.connEpoch;
|
|
73
114
|
const policy = {
|
|
74
|
-
retryableCodes: ["connect_failed", "timeout"],
|
|
115
|
+
retryableCodes: ["connect_failed", "timeout"], // NOT auth_failed (permanent — retrying a rejected key burns budget)
|
|
75
116
|
maxAttempts: this.cfg.retry.maxAttempts,
|
|
76
117
|
backoffMs: (failed) => this.cfg.retry.backoffMs * failed,
|
|
77
118
|
};
|
|
@@ -86,22 +127,28 @@ export class RemoteSshExecutionEnv {
|
|
|
86
127
|
const record = { epoch, kind, promise: raw };
|
|
87
128
|
record.promise = raw.then((r) => {
|
|
88
129
|
if (this.inFlight === record)
|
|
89
|
-
this.inFlight = undefined;
|
|
130
|
+
this.inFlight = undefined; // 身份清:旧败诺清不掉新记录
|
|
90
131
|
return r;
|
|
91
132
|
});
|
|
92
133
|
this.inFlight = record;
|
|
93
134
|
return record.promise;
|
|
94
135
|
}
|
|
136
|
+
/** [1165] exec/execStream/sftp 共用的懒连接入口。已连=零开销直过;未连=走完整 connect()(含重试策略)。
|
|
137
|
+
* caller 的 signal 只中止**它自己的等待**(共享在飞尝试继续伺服其他等待者;彻底取消归 destroy 的代际
|
|
138
|
+
* 闸管,handshake 余命 ≤ readyTimeout 有界)。 */
|
|
95
139
|
async ensureConnected(signal) {
|
|
140
|
+
// destroyed 先于 conn 查(三轮复审:teardown 竞窗里 conn 可能在 destroy 后才被 ready 赋上——那条
|
|
141
|
+
// 连接由 ready 守卫立即 end,这里也不得放行)。
|
|
96
142
|
if (this.destroyed)
|
|
97
143
|
return { ok: false, error: new ExecutionError("shell_unavailable", "SSH env destroyed — no lazy reconnect after teardown") };
|
|
98
144
|
if (signal?.aborted)
|
|
99
145
|
return { ok: false, error: new ExecutionError("aborted", "aborted before SSH lazy-connect") };
|
|
100
146
|
if (this.conn)
|
|
101
147
|
return ok(undefined);
|
|
102
|
-
const shared = this.startConnect("establish");
|
|
148
|
+
const shared = this.startConnect("establish"); // 四轮复审:single-flight join(与显式 connect/reconnect 同记录)
|
|
103
149
|
let r;
|
|
104
150
|
if (signal) {
|
|
151
|
+
// 可中止等待:只退出本 caller 的 await,不动共享在飞记录(其他等待者照常拿结果)。
|
|
105
152
|
let onAbort;
|
|
106
153
|
const aborted = new Promise((res) => {
|
|
107
154
|
onAbort = () => res("aborted");
|
|
@@ -120,6 +167,8 @@ export class RemoteSshExecutionEnv {
|
|
|
120
167
|
if (r.ok) {
|
|
121
168
|
if (this.conn)
|
|
122
169
|
return ok(undefined);
|
|
170
|
+
// 旗到手但连接已不在(建后即断竞窗)——在飞记录 settle 时已自清;诚实报不可用,下次调用自然
|
|
171
|
+
// 重建(不递归重连:防断连风暴无界重试)。
|
|
123
172
|
return { ok: false, error: new ExecutionError("shell_unavailable", "SSH connection dropped right after connect") };
|
|
124
173
|
}
|
|
125
174
|
if (r.error.code === "aborted")
|
|
@@ -127,7 +176,7 @@ export class RemoteSshExecutionEnv {
|
|
|
127
176
|
return { ok: false, error: new ExecutionError("shell_unavailable", `SSH lazy-connect failed: ${r.error.message}`, r.error) };
|
|
128
177
|
}
|
|
129
178
|
connectOnce(signal, opEpoch) {
|
|
130
|
-
const epoch = opEpoch ?? this.connEpoch;
|
|
179
|
+
const epoch = opEpoch ?? this.connEpoch; // 代际=逻辑连接操作抓拍(四轮复审:重试轮不得混进新代)
|
|
131
180
|
return new Promise((resolve) => {
|
|
132
181
|
if (signal?.aborted)
|
|
133
182
|
return resolve({ ok: false, error: new RemoteExecutionError("aborted", "connect aborted") });
|
|
@@ -142,15 +191,19 @@ export class RemoteSshExecutionEnv {
|
|
|
142
191
|
};
|
|
143
192
|
c.on("ready", () => {
|
|
144
193
|
if (this.destroyed || epoch !== this.connEpoch) {
|
|
194
|
+
// [1165] 三轮复审 teardown/代际竞窗:destroy 或 reconnect 之后才 ready 的旧代连接——立即断掉
|
|
195
|
+
// 且不赋 conn(否则 sole destroy 之后留一条活连接+可执行面,或旧代顶掉新代)。
|
|
145
196
|
try {
|
|
146
197
|
c.end();
|
|
147
198
|
}
|
|
148
|
-
catch { }
|
|
199
|
+
catch { /* noop */ }
|
|
149
200
|
resolve({ ok: false, error: new RemoteExecutionError("connect_failed", this.destroyed ? "SSH env destroyed while connecting" : "SSH connection superseded while connecting (reconnect)") });
|
|
150
201
|
return;
|
|
151
202
|
}
|
|
152
203
|
this.conn = c;
|
|
153
204
|
this.sftpPromise = undefined;
|
|
205
|
+
// If this live connection later drops, clear our handles so the next exec()/fs op fails closed with
|
|
206
|
+
// "shell_unavailable" (council #3) instead of hitting a dead-but-truthy client with a raw ssh2 error.
|
|
154
207
|
c.on("close", () => {
|
|
155
208
|
if (this.conn === c) {
|
|
156
209
|
this.conn = undefined;
|
|
@@ -163,6 +216,7 @@ export class RemoteSshExecutionEnv {
|
|
|
163
216
|
c.end();
|
|
164
217
|
}
|
|
165
218
|
catch {
|
|
219
|
+
/* noop */
|
|
166
220
|
}
|
|
167
221
|
resolve({ ok: false, error: this.connErr(e) });
|
|
168
222
|
});
|
|
@@ -183,24 +237,29 @@ export class RemoteSshExecutionEnv {
|
|
|
183
237
|
async postResumeInit() {
|
|
184
238
|
return unsupported("postResumeInit");
|
|
185
239
|
}
|
|
240
|
+
/** Re-establish a dropped connection (supported — unlike suspend, reconnect is idempotent establishment). */
|
|
186
241
|
async reconnect(_sessionToken) {
|
|
187
242
|
if (this.destroyed)
|
|
188
243
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "SSH env destroyed — create a fresh env instead of reviving") };
|
|
244
|
+
// 五轮复审:并发 reconnect join 已在飞的 replace 记录(不再各自 bump 代际=每路一条握手的重连风暴);
|
|
245
|
+
// 只有「无在飞 replace」才推代际顶掉旧 establish/旧连接。
|
|
189
246
|
if (this.inFlight && this.inFlight.epoch === this.connEpoch && this.inFlight.kind === "replace")
|
|
190
247
|
return this.inFlight.promise;
|
|
191
|
-
this.connEpoch++;
|
|
248
|
+
this.connEpoch++; // [1165] 在飞旧代自灭(每轮起跳查 + ready 守卫双闸)
|
|
192
249
|
this.conn?.end();
|
|
193
250
|
this.conn = undefined;
|
|
194
251
|
this.sftpPromise = undefined;
|
|
195
|
-
return this.startConnect("replace");
|
|
252
|
+
return this.startConnect("replace"); // 四轮复审:发起点唯一——重连期间的 exec/sftp join 同一记录
|
|
196
253
|
}
|
|
254
|
+
/** Close the SSH connection. Does NOT destroy the machine (real system) — idempotent, never throws. */
|
|
197
255
|
async destroy() {
|
|
198
|
-
this.destroyed = true;
|
|
199
|
-
this.connEpoch++;
|
|
256
|
+
this.destroyed = true; // [1165] 懒连接不复活已拆毁的 env(fail-closed 契约保持)
|
|
257
|
+
this.connEpoch++; // 在飞连接每轮起跳查+ready 守卫自灭(handshake 余命 ≤ readyTimeout,有界)
|
|
200
258
|
try {
|
|
201
259
|
this.conn?.end();
|
|
202
260
|
}
|
|
203
261
|
catch {
|
|
262
|
+
/* best-effort */
|
|
204
263
|
}
|
|
205
264
|
this.conn = undefined;
|
|
206
265
|
this.sftpPromise = undefined;
|
|
@@ -208,7 +267,10 @@ export class RemoteSshExecutionEnv {
|
|
|
208
267
|
async cleanup() {
|
|
209
268
|
return this.destroy();
|
|
210
269
|
}
|
|
270
|
+
// ─────────────────────────────── exec ───────────────────────────────
|
|
211
271
|
async exec(command, options) {
|
|
272
|
+
// [1165] lazy-connect:core prepare-task 铸 env 不显式 connect(k8s 道同前提)——首用自动建连;
|
|
273
|
+
// caller 的 abortSignal 覆盖建连等待段(pre-abort 与等待中 abort 都即时退出)。
|
|
212
274
|
const ready = await this.ensureConnected(options?.abortSignal);
|
|
213
275
|
if (!ready.ok)
|
|
214
276
|
return ready;
|
|
@@ -217,6 +279,10 @@ export class RemoteSshExecutionEnv {
|
|
|
217
279
|
return { ok: false, error: new ExecutionError("shell_unavailable", "SSH not connected — call connect() first") };
|
|
218
280
|
const timeoutMs = options?.timeout != null ? options.timeout * 1000 : this.cfg.commandTimeoutMs;
|
|
219
281
|
return new Promise((resolve) => {
|
|
282
|
+
// D2: bounded rolling-TAIL (core's RollingTailBuffer) so a model-driven `yes`/`cat big` can't OOM the
|
|
283
|
+
// durable worker — keep only the last 8MB (tail = errors+exit) + a dropped-byte marker, O(maxBytes) memory.
|
|
284
|
+
// SAFE: this is the model-facing TEXT exec path only; readBinaryFile uses SFTP (sftp.readFile), a separate
|
|
285
|
+
// byte-exact path that NEVER flows through these buffers.
|
|
220
286
|
const outBuf = new RollingTailBuffer();
|
|
221
287
|
const errBuf = new RollingTailBuffer();
|
|
222
288
|
let settled = false;
|
|
@@ -229,14 +295,18 @@ export class RemoteSshExecutionEnv {
|
|
|
229
295
|
options?.abortSignal?.removeEventListener("abort", onAbort);
|
|
230
296
|
conn.removeListener("close", onConnClose);
|
|
231
297
|
try {
|
|
232
|
-
streamRef?.close();
|
|
298
|
+
streamRef?.close(); // timeout/abort: close the channel so the remote command stops (council #1). No-op if already closed.
|
|
233
299
|
}
|
|
234
300
|
catch {
|
|
301
|
+
/* noop */
|
|
235
302
|
}
|
|
236
303
|
resolve(r);
|
|
237
304
|
};
|
|
238
305
|
const timer = setTimeout(() => finish({ ok: false, error: new ExecutionError("timeout", `ssh exec exceeded ${timeoutMs}ms (provider/command hang)`) }), timeoutMs);
|
|
239
306
|
const onAbort = () => finish({ ok: false, error: new ExecutionError("aborted", "ssh exec aborted") });
|
|
307
|
+
// Transport mapping: the TCP/SSH transport dying mid-command must NOT look like success or a vague
|
|
308
|
+
// failure. Typed "transport_lost" since core 1.89 (outcome UNKNOWN — reconnect, then retry
|
|
309
|
+
// only per the op's idempotency); the close-without-exit-status path below catches the same condition.
|
|
240
310
|
const onConnClose = () => finish({ ok: false, error: new ExecutionError("transport_lost", "ssh transport lost mid-command (connection closed before exit status) — reconnect + retry per idempotency") });
|
|
241
311
|
conn.once("close", onConnClose);
|
|
242
312
|
if (options?.abortSignal) {
|
|
@@ -251,18 +321,21 @@ export class RemoteSshExecutionEnv {
|
|
|
251
321
|
streamRef = stream;
|
|
252
322
|
if (settled) {
|
|
253
323
|
try {
|
|
254
|
-
stream.close();
|
|
324
|
+
stream.close(); // already timed out/aborted before the exec callback fired → don't leak the channel
|
|
255
325
|
}
|
|
256
326
|
catch {
|
|
327
|
+
/* noop */
|
|
257
328
|
}
|
|
258
329
|
return;
|
|
259
330
|
}
|
|
260
331
|
stream
|
|
261
332
|
.on("data", (d) => {
|
|
262
|
-
outBuf.push(d);
|
|
333
|
+
outBuf.push(d); // streaming callback still gets the full chunk; only the buffered return value is bounded
|
|
263
334
|
options?.onStdout?.(d.toString());
|
|
264
335
|
})
|
|
265
336
|
.on("close", (code, signal) => {
|
|
337
|
+
// code==null without a kill signal = the channel died without delivering an exit status (transport drop),
|
|
338
|
+
// NOT a successful exit — was silently mapped to exitCode 0 before ([R78]#1).
|
|
266
339
|
if (code == null && !signal) {
|
|
267
340
|
return finish({ ok: false, error: new ExecutionError("transport_lost", "ssh channel closed without exit status (transport lost / connection dropped)") });
|
|
268
341
|
}
|
|
@@ -277,8 +350,9 @@ export class RemoteSshExecutionEnv {
|
|
|
277
350
|
});
|
|
278
351
|
});
|
|
279
352
|
}
|
|
353
|
+
// ─────────────────────────────── streaming exec ───────────────────────────────
|
|
280
354
|
async *execStream(command, options) {
|
|
281
|
-
const ready = await this.ensureConnected(options?.signal);
|
|
355
|
+
const ready = await this.ensureConnected(options?.signal); // [1165] 同 exec:首用自动建连(等待可中止)
|
|
282
356
|
if (!ready.ok)
|
|
283
357
|
throw new RemoteExecutionError(ready.error.code === "aborted" ? "aborted" : "connect_failed", ready.error.message, ready.error);
|
|
284
358
|
const conn = this.conn;
|
|
@@ -299,7 +373,9 @@ export class RemoteSshExecutionEnv {
|
|
|
299
373
|
let streamRef;
|
|
300
374
|
let onAbort;
|
|
301
375
|
let paused = false;
|
|
302
|
-
let done = false;
|
|
376
|
+
let done = false; // set in teardown; a conn.exec callback that fires AFTER the consumer cancelled must close its late stream
|
|
377
|
+
// [R78]#1: the SSH transport dying mid-stream is a typed, retryable condition — map to "transport_lost"
|
|
378
|
+
// (in the upper layer's retry allowlist) instead of a silent finish or "unknown".
|
|
303
379
|
const onConnClose = () => {
|
|
304
380
|
if (!finished) {
|
|
305
381
|
failure = failure ?? new RemoteExecutionError("transport_lost", "ssh transport lost mid-stream (connection closed) — reconnect + retry per idempotency");
|
|
@@ -309,6 +385,8 @@ export class RemoteSshExecutionEnv {
|
|
|
309
385
|
};
|
|
310
386
|
conn.once("close", onConnClose);
|
|
311
387
|
const teardown = () => {
|
|
388
|
+
// runs on EVERY exit path (normal end, idle-timeout/failure throw, AND consumer early-break via the generator's
|
|
389
|
+
// .return()): drop the abort listener (council #5) + close the channel (council #2 idle-timeout leak).
|
|
312
390
|
done = true;
|
|
313
391
|
conn.removeListener("close", onConnClose);
|
|
314
392
|
if (onAbort) {
|
|
@@ -319,6 +397,7 @@ export class RemoteSshExecutionEnv {
|
|
|
319
397
|
streamRef?.close();
|
|
320
398
|
}
|
|
321
399
|
catch {
|
|
400
|
+
/* noop */
|
|
322
401
|
}
|
|
323
402
|
};
|
|
324
403
|
const full = this.withCwdEnv(command, options?.cwd, options?.env);
|
|
@@ -332,9 +411,10 @@ export class RemoteSshExecutionEnv {
|
|
|
332
411
|
streamRef = stream;
|
|
333
412
|
if (done) {
|
|
334
413
|
try {
|
|
335
|
-
stream.close();
|
|
414
|
+
stream.close(); // consumer already cancelled (.return()/throw) before this callback ran → close the late channel (council #3)
|
|
336
415
|
}
|
|
337
416
|
catch {
|
|
417
|
+
/* noop */
|
|
338
418
|
}
|
|
339
419
|
return;
|
|
340
420
|
}
|
|
@@ -353,7 +433,7 @@ export class RemoteSshExecutionEnv {
|
|
|
353
433
|
lastChunk = Date.now();
|
|
354
434
|
queue.push(chunk);
|
|
355
435
|
if (queue.length >= 512 && !paused) {
|
|
356
|
-
paused = true;
|
|
436
|
+
paused = true; // backpressure: stop a fast producer from growing the queue unbounded (council design #1)
|
|
357
437
|
stream.pause();
|
|
358
438
|
}
|
|
359
439
|
signalReady();
|
|
@@ -362,6 +442,7 @@ export class RemoteSshExecutionEnv {
|
|
|
362
442
|
.on("data", (d) => enqueue({ type: "stdout", data: d.toString() }))
|
|
363
443
|
.on("close", (code, signal) => {
|
|
364
444
|
if (code == null && !signal && !failure) {
|
|
445
|
+
// channel died without an exit status (transport drop) — typed retryable, not a fake exit 0 ([R78]#1)
|
|
365
446
|
failure = new RemoteExecutionError("transport_lost", "ssh channel closed without exit status (transport lost)");
|
|
366
447
|
}
|
|
367
448
|
exitCode = code ?? 0;
|
|
@@ -382,6 +463,7 @@ export class RemoteSshExecutionEnv {
|
|
|
382
463
|
throw failure;
|
|
383
464
|
if (finished)
|
|
384
465
|
break;
|
|
466
|
+
// idle/liveness bound: no chunk within readTimeoutMs → typed timeout (unreachable host)
|
|
385
467
|
const waitMs = idleMs ? Math.max(1, idleMs - (Date.now() - lastChunk)) : 30_000;
|
|
386
468
|
await new Promise((r) => {
|
|
387
469
|
wake = r;
|
|
@@ -398,8 +480,11 @@ export class RemoteSshExecutionEnv {
|
|
|
398
480
|
teardown();
|
|
399
481
|
}
|
|
400
482
|
}
|
|
483
|
+
// ─────────────────────────────── FileSystem (sftp + shell hybrid) ───────────────────────────────
|
|
401
484
|
async sftp(abortSignal) {
|
|
402
|
-
const ready = await this.ensureConnected(abortSignal);
|
|
485
|
+
const ready = await this.ensureConnected(abortSignal); // [1165] fs 面同享懒连接(scratchpad mkdir 是 POC 实红点)
|
|
486
|
+
// 四轮复审:取消跨 sftp 边界保码——aborted 直铸 FileError("aborted")(fileError() 对 FileError 原样
|
|
487
|
+
// 放行),不得降级成 "unknown" 触发上游误重试。
|
|
403
488
|
if (!ready.ok)
|
|
404
489
|
return Promise.reject(ready.error.code === "aborted" ? new FileError("aborted", ready.error.message) : new Error(ready.error.message));
|
|
405
490
|
if (!this.conn)
|
|
@@ -445,9 +530,14 @@ export class RemoteSshExecutionEnv {
|
|
|
445
530
|
}
|
|
446
531
|
async writeFile(p, content, abortSignal) {
|
|
447
532
|
const abs = this.resolve(p);
|
|
533
|
+
// sftp.writeFile does NOT create parent dirs → mkdir -p the parent first (parity with E2B's auto-create).
|
|
448
534
|
const dir = path.posix.dirname(abs);
|
|
449
535
|
if (dir && dir !== "." && dir !== "/") {
|
|
450
536
|
const mk = await this.exec(`mkdir -p -- ${shellQuote(dir)}`, { abortSignal });
|
|
537
|
+
// 🔴 `!mk.ok` 也必须 fail-closed(2026-07-25 补):原来只看 `exitCode !== 0`,于是 **exec 本身失败**
|
|
538
|
+
// (设备/连接在这一拍掉线 ⇒ `transport_lost` / `shell_unavailable`)被整条吞掉,后面照样推载荷,
|
|
539
|
+
// 而整个 `writeFile` 返回 **ok:true** —— 调用方看到的成功与真实故障点完全脱节。同族的
|
|
540
|
+
// local-docker / k8s 用的正是 `if (!mk.ok) return`,这两条腿是漏的。
|
|
451
541
|
if (!mk.ok)
|
|
452
542
|
return { ok: false, error: fileErrorFromExec(mk.error, dir) };
|
|
453
543
|
if (mk.value.exitCode !== 0)
|
|
@@ -496,7 +586,7 @@ export class RemoteSshExecutionEnv {
|
|
|
496
586
|
const sftp = await this.sftp(abortSignal);
|
|
497
587
|
const entries = await new Promise((resolve, reject) => sftp.readdir(abs, (err, list) => (err ? reject(err) : resolve(list))));
|
|
498
588
|
return ok(entries.map((e) => {
|
|
499
|
-
const a = e.attrs;
|
|
589
|
+
const a = e.attrs; // readdir gives raw Attributes (no isDirectory() helper, unlike lstat's Stats) → derive from mode bits
|
|
500
590
|
return { name: e.filename, path: path.posix.join(abs, e.filename), kind: kindFromMode(a.mode), size: a.size ?? 0, mtimeMs: (a.mtime ?? 0) * 1000 };
|
|
501
591
|
}));
|
|
502
592
|
}
|
|
@@ -504,12 +594,13 @@ export class RemoteSshExecutionEnv {
|
|
|
504
594
|
return { ok: false, error: this.fileError(e, abs) };
|
|
505
595
|
}
|
|
506
596
|
}
|
|
597
|
+
// ── design/158 B6:shell-out 文件面委托共享基座(posix-shell-fs.ts;fileInfo/listDir/exists 走 SFTP 原生,不并)──
|
|
507
598
|
posixFsInst;
|
|
508
599
|
get posixFs() {
|
|
509
600
|
return (this.posixFsInst ??= createPosixShellFs({
|
|
510
601
|
run: (cmd, abortSignal) => this.exec(cmd, { abortSignal }),
|
|
511
602
|
resolve: (p) => this.resolve(p),
|
|
512
|
-
tempDir: () => null,
|
|
603
|
+
tempDir: () => null, // R5:ssh 无 workspace 概念,临时件落系统 temp(mktemp -t)
|
|
513
604
|
}));
|
|
514
605
|
}
|
|
515
606
|
async readLink(p, abortSignal) {
|
|
@@ -538,6 +629,7 @@ export class RemoteSshExecutionEnv {
|
|
|
538
629
|
async createTempFile(options) {
|
|
539
630
|
return this.posixFs.createTempFile(options);
|
|
540
631
|
}
|
|
632
|
+
// ─────────────────────────────── helpers ───────────────────────────────
|
|
541
633
|
withCwdEnv(command, cwd, env) {
|
|
542
634
|
const parts = [];
|
|
543
635
|
const dir = cwd ?? (this.cwd !== "." ? this.cwd : undefined);
|
|
@@ -552,7 +644,7 @@ export class RemoteSshExecutionEnv {
|
|
|
552
644
|
connErr(e) {
|
|
553
645
|
const msg = e.message || String(e);
|
|
554
646
|
if (e.level === "client-authentication" || /authentication|all configured authentication methods failed/i.test(msg)) {
|
|
555
|
-
return new RemoteExecutionError("auth_failed", `ssh auth failed: ${msg}`, e);
|
|
647
|
+
return new RemoteExecutionError("auth_failed", `ssh auth failed: ${msg}`, e); // permanent — not retried
|
|
556
648
|
}
|
|
557
649
|
if (e.level === "client-timeout" || /timed out|timeout/i.test(msg))
|
|
558
650
|
return new RemoteExecutionError("timeout", `ssh connect timed out: ${msg}`, e);
|
|
@@ -574,6 +666,11 @@ export class RemoteSshExecutionEnv {
|
|
|
574
666
|
return new FileError("unknown", msg, p);
|
|
575
667
|
}
|
|
576
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* `ExecutionEnvFactory` for the SSH backend — one fresh (unconnected) env per task against the same host
|
|
671
|
+
* (lazy: connects on first use). Config (host/user/key) is control-plane-supplied; the Runner owns the
|
|
672
|
+
* lifetime and calls `destroy()` (= disconnect) on task end.
|
|
673
|
+
*/
|
|
577
674
|
export function sshExecutionEnvFactory(config) {
|
|
578
675
|
return (_ctx) => new RemoteSshExecutionEnv(config);
|
|
579
676
|
}
|
|
@@ -1,8 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [848] remote scratchpad(server 半场)— the REMOTE sandbox lanes' session scratchpad convention.
|
|
3
|
+
*
|
|
4
|
+
* host lane already has one (env-facts.ts ensureScratchpadDir, worker-local under localDataRoot). The remote
|
|
5
|
+
* lanes (e2b/k8s/local-docker/ssh) run their hands OFF this box, so a worker-local dir would be a lie — the
|
|
6
|
+
* convention there is a path INSIDE the sandbox: `/tmp/scratchpad/<sessionId>`. This module decorates the
|
|
7
|
+
* deployment's `executionEnvFactory` so that dir EXISTS by the time the model's first command could use it:
|
|
8
|
+
*
|
|
9
|
+
* - 🔴 LAZY, never factory-time: e2b is a lazy-VM adapter — an exec at factory time would force-boot the
|
|
10
|
+
* VM for every task whether or not it ever runs a command. So the `mkdir -p` rides the env's OWN first
|
|
11
|
+
* `exec` (the original exec, NOT the wrapped one — no recursion), single-flighted via a cached PROMISE
|
|
12
|
+
* (a boolean would let two concurrent first commands both fire the mkdir).
|
|
13
|
+
* - Best-effort: a failed mkdir warns ONCE and the user command still runs (a scratchpad nicety must never
|
|
14
|
+
* fail the task). The settled promise is kept — no retry — so after a failure the ADVERTISED path may not
|
|
15
|
+
* exist; accepted (the model's write then errors like any bad path, same class as a swept host scratchpad).
|
|
16
|
+
* - mkdir runs with the adapter's DEFAULT exec options (no cwd/env/timeout): option semantics differ per
|
|
17
|
+
* adapter and the command is trivial.
|
|
18
|
+
*
|
|
19
|
+
* Lanes: e2b/k8s/local-docker = one sandbox per task; ssh = a shared persistent host, where the
|
|
20
|
+
* sessionId-scoped subdir is exactly what keeps sessions apart. adb is NOT a lane (no standard /tmp on
|
|
21
|
+
* Android); host keeps its own worker-local scratchpad — neither is wrapped (main.ts gates on
|
|
22
|
+
* {@link isRemoteScratchpadLane}).
|
|
23
|
+
*
|
|
24
|
+
* Trust: sessionId is minted by us (uuid), but it is caller-adjacent — defence in depth, the path is only
|
|
25
|
+
* built for `[A-Za-z0-9._-]+` ids ({@link remoteScratchpadDirFor}); anything else ⇒ NOT decorated (+warn),
|
|
26
|
+
* never a quoted-shell gamble. The same helper feeds the envFacts advertisement (main.ts), so the fact and
|
|
27
|
+
* the mkdir can never disagree on the path or on the validity rule.
|
|
28
|
+
*/
|
|
1
29
|
import type { ExecutionEnvFactory } from "@sema-agent/core";
|
|
30
|
+
/** Structured logger surface (matches the service `logger`; calls optional-chained — same shape as the
|
|
31
|
+
* worktree-isolation wrapper's). */
|
|
2
32
|
export interface RemoteScratchpadLogger {
|
|
3
33
|
warn?(event: string, fields?: Record<string, unknown>): void;
|
|
4
34
|
}
|
|
5
35
|
export declare function isRemoteScratchpadLane(provider: string | undefined): boolean;
|
|
36
|
+
/** THE remote scratchpad path convention: `/tmp/scratchpad/<sessionId>` (raw id — the sandbox is already
|
|
37
|
+
* session/task-scoped tenancy, no hash segment needed). Returns undefined for an absent/unsafe id — the
|
|
38
|
+
* single validity rule shared by the factory decorator AND the envFacts advertisement. */
|
|
6
39
|
export declare function remoteScratchpadDirFor(sessionId: string | undefined): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Decorate a factory so each minted env lazily `mkdir -p`s its session scratchpad before its FIRST exec.
|
|
42
|
+
* The env is mutated IN PLACE (rebind `env.exec`, evictLspOnDestroy precedent) — a copy would drop the
|
|
43
|
+
* adapter's prototype methods and identity (registries key on the instance).
|
|
44
|
+
*/
|
|
7
45
|
export declare function withRemoteScratchpad(factory: ExecutionEnvFactory, logger?: RemoteScratchpadLogger): ExecutionEnvFactory;
|
|
8
46
|
//# sourceMappingURL=remote-scratchpad.d.ts.map
|
|
@@ -1,26 +1,46 @@
|
|
|
1
|
+
/** The remote lanes that get the in-sandbox scratchpad convention. adb (no standard /tmp) and host
|
|
2
|
+
* (worker-local scratchpad already exists) are deliberately absent. */
|
|
1
3
|
const REMOTE_SCRATCHPAD_LANES = new Set(["e2b", "k8s", "local-docker", "ssh"]);
|
|
2
4
|
export function isRemoteScratchpadLane(provider) {
|
|
3
5
|
return provider !== undefined && REMOTE_SCRATCHPAD_LANES.has(provider);
|
|
4
6
|
}
|
|
7
|
+
/** Charset gate for embedding the sessionId in a shell command. Dots allowed (unlike the host-lane segment
|
|
8
|
+
* sanitizer, which REWRITES): here an id either passes verbatim or the feature stays off. "/" can't pass, so a
|
|
9
|
+
* multi-segment escape can't — but the two SINGLE-segment escapes `.` (= the shared scratchpad root) and `..`
|
|
10
|
+
* (= /tmp itself) DO match the charset and are rejected explicitly below (codex HIGH: an opaque caller-chosen
|
|
11
|
+
* sessionId of exactly "."/".." would alias another session's root on a shared ssh host). */
|
|
5
12
|
const SESSION_ID_RE = /^[A-Za-z0-9._-]+$/;
|
|
13
|
+
/** THE remote scratchpad path convention: `/tmp/scratchpad/<sessionId>` (raw id — the sandbox is already
|
|
14
|
+
* session/task-scoped tenancy, no hash segment needed). Returns undefined for an absent/unsafe id — the
|
|
15
|
+
* single validity rule shared by the factory decorator AND the envFacts advertisement. */
|
|
6
16
|
export function remoteScratchpadDirFor(sessionId) {
|
|
7
17
|
if (!sessionId || sessionId === "." || sessionId === ".." || !SESSION_ID_RE.test(sessionId))
|
|
8
18
|
return undefined;
|
|
9
19
|
return `/tmp/scratchpad/${sessionId}`;
|
|
10
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Decorate a factory so each minted env lazily `mkdir -p`s its session scratchpad before its FIRST exec.
|
|
23
|
+
* The env is mutated IN PLACE (rebind `env.exec`, evictLspOnDestroy precedent) — a copy would drop the
|
|
24
|
+
* adapter's prototype methods and identity (registries key on the instance).
|
|
25
|
+
*/
|
|
11
26
|
export function withRemoteScratchpad(factory, logger) {
|
|
12
27
|
return async (ctx) => {
|
|
13
28
|
const env = await factory(ctx);
|
|
14
29
|
const dir = remoteScratchpadDirFor(ctx.sessionId);
|
|
15
30
|
if (dir === undefined) {
|
|
31
|
+
// absent id (adhoc no-session spawn) = silently no scratchpad; a NON-empty id failing the charset gate
|
|
32
|
+
// is a wiring anomaly worth a warn (our ids are uuids — this should never fire outside a bug).
|
|
16
33
|
if (ctx.sessionId)
|
|
17
34
|
logger?.warn?.("remote_scratchpad_skipped_unsafe_session_id", { sessionId: ctx.sessionId });
|
|
18
35
|
return env;
|
|
19
36
|
}
|
|
20
37
|
const e = env;
|
|
21
38
|
if (typeof e.exec !== "function")
|
|
22
|
-
return env;
|
|
39
|
+
return env; // probe-guard: no exec face, nothing to ride
|
|
23
40
|
const origExec = e.exec.bind(e);
|
|
41
|
+
// Single-flight via a cached PROMISE (not a boolean): concurrent first commands await the SAME mkdir.
|
|
42
|
+
// The promise never rejects (failure is swallowed into the warn) and stays cached once settled — mkdir
|
|
43
|
+
// runs AT MOST once per env, success or not (best-effort posture, see file header).
|
|
24
44
|
let ensured;
|
|
25
45
|
const ensure = () => (ensured ??= (async () => {
|
|
26
46
|
try {
|
|
@@ -37,11 +57,17 @@ export function withRemoteScratchpad(factory, logger) {
|
|
|
37
57
|
}
|
|
38
58
|
})());
|
|
39
59
|
e.exec = async (command, options) => {
|
|
60
|
+
// Pre-aborted call must NOT trigger the mkdir prelude: on a lazy-VM lane the adapter's own exec guard
|
|
61
|
+
// refuses BEFORE provisioning, but our optionless mkdir would bypass that guard and force-boot a billable
|
|
62
|
+
// VM just to abort (codex M2). Mid-mkdir aborts stay coarse (the command is trivial) — accepted.
|
|
40
63
|
if (options?.abortSignal?.aborted)
|
|
41
64
|
return origExec(command, options);
|
|
42
65
|
await ensure();
|
|
43
66
|
return origExec(command, options);
|
|
44
67
|
};
|
|
68
|
+
// Background leg (codex M1): core's background path calls `spawnBackground`, NOT `exec` — a task whose FIRST
|
|
69
|
+
// command is a bg spawn must ride the SAME single-flight mkdir or its scratchpad write fails. Duck-typed (the
|
|
70
|
+
// capability is optional per hasBackgroundShell); BackgroundSpawnOptions has no abortSignal, so no pre-abort leg.
|
|
45
71
|
const bgHost = e;
|
|
46
72
|
if (typeof bgHost.spawnBackground === "function") {
|
|
47
73
|
const origSpawn = bgHost.spawnBackground.bind(bgHost);
|
|
@@ -50,12 +76,16 @@ export function withRemoteScratchpad(factory, logger) {
|
|
|
50
76
|
return origSpawn(command, options);
|
|
51
77
|
};
|
|
52
78
|
}
|
|
79
|
+
// PAIR-REVIEW F-5:canonicalPath 腿**显式**接 ensure——core 1.351 的 prepare 期 scratchpad 实体化/
|
|
80
|
+
// additionalRoots canonicalize 走 env.canonicalPath;local-docker 的实现私径直调 docker(绕 exec 包装)
|
|
81
|
+
// → 目录未建 not_found → 围栏不 admit(广告了但 Write 恒拒,[1339] 原病残留)。e2b/ssh/k8s 此前只是
|
|
82
|
+
// **恰好**内部走 this.exec 被顺带救活(巧合承重)——此腿把六 adapter 拉到同一显式契约。
|
|
53
83
|
const cpHost = e;
|
|
54
84
|
if (typeof cpHost.canonicalPath === "function") {
|
|
55
85
|
const origCanonical = cpHost.canonicalPath.bind(cpHost);
|
|
56
86
|
cpHost.canonicalPath = async (p, abortSignal) => {
|
|
57
87
|
if (abortSignal?.aborted)
|
|
58
|
-
return origCanonical(p, abortSignal);
|
|
88
|
+
return origCanonical(p, abortSignal); // 同 exec 腿:预中止不强启 VM
|
|
59
89
|
await ensure();
|
|
60
90
|
return origCanonical(p, abortSignal);
|
|
61
91
|
};
|