@sema-agent/server 7.131.0 → 7.133.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +575 -0
- package/README.md +12 -9
- package/README.zh-CN.md +9 -6
- package/USAGE.md +326 -98
- package/deploy/sema-up/README.md +19 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
- package/deploy/sema-up/chart/templates/server.yaml +5 -2
- package/deploy/sema-up/stack/compose.yaml +3 -0
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
- package/dist/adoption/plan.d.ts +3 -2
- package/dist/adoption/plan.js +7 -5
- package/dist/adoption/runner.js +3 -10
- package/dist/adoption/sql.d.ts +3 -2
- package/dist/adoption/sql.js +3 -5
- package/dist/adoption/wire.d.ts +4 -1
- package/dist/adoption/wire.js +1 -0
- package/dist/approval-ask-audit-store.d.ts +31 -13
- package/dist/approval-ask-audit-store.js +272 -74
- package/dist/approval-ask-machine.d.ts +15 -0
- package/dist/approval-ask-machine.js +6 -0
- package/dist/approval-ask-retention.d.ts +136 -0
- package/dist/approval-ask-retention.js +102 -0
- package/dist/approval-card.d.ts +82 -20
- package/dist/approval-card.js +46 -16
- package/dist/approval-deny-reasons.d.ts +1 -1
- package/dist/approval.d.ts +9 -0
- package/dist/approval.js +24 -3
- package/dist/audit.d.ts +33 -19
- package/dist/audit.js +12 -16
- package/dist/auth-bridge.d.ts +4 -0
- package/dist/auth-bridge.js +10 -11
- package/dist/bake-runner/main.js +3 -2
- package/dist/boot/deferred-sandbox-path-env.js +1 -1
- package/dist/boot/deployment-mods.d.ts +31 -0
- package/dist/boot/deployment-mods.js +26 -0
- package/dist/boot/execution-env.d.ts +1 -1
- package/dist/boot/execution-env.js +27 -12
- package/dist/boot/execution-lane.js +11 -0
- package/dist/boot/leader.js +2 -0
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-boundary.js +2 -2
- package/dist/boot/memory-consolidation.d.ts +5 -4
- package/dist/boot/memory-consolidation.js +7 -6
- package/dist/boot/org-delivery.d.ts +28 -0
- package/dist/boot/org-delivery.js +20 -0
- package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
- package/dist/boot/read-deny-anchor-audit.js +36 -0
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +21 -1
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +74 -123
- package/dist/boot/runner-deps.d.ts +19 -8
- package/dist/boot/runner-deps.js +15 -2
- package/dist/boot/session-faces.d.ts +2 -7
- package/dist/boot/session-faces.js +4 -2
- package/dist/boot/stage-02-web-admin-pull.js +4 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -4
- package/dist/boot/stage-05-execution-env.js +7 -10
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +11 -7
- package/dist/boot/stage-07-capability-layer.d.ts +4 -2
- package/dist/boot/stage-07-capability-layer.js +27 -6
- package/dist/boot/stage-08-reapers.d.ts +4 -2
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +6 -9
- package/dist/boot/stage-10-http-server.d.ts +6 -9
- package/dist/boot/stage-10-http-server.js +7 -9
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +7 -8
- package/dist/boot/web-admin.d.ts +4 -3
- package/dist/boot/web-admin.js +186 -98
- package/dist/boot/workflow-orchestration.d.ts +2 -0
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/brain.js +25 -25
- package/dist/breaker-key.d.ts +6 -0
- package/dist/breaker-key.js +29 -0
- package/dist/budget.js +5 -4
- package/dist/caller-text-echo.d.ts +23 -0
- package/dist/caller-text-echo.js +21 -0
- package/dist/capabilities/brand-identity.d.ts +26 -0
- package/dist/capabilities/brand-identity.js +27 -0
- package/dist/capabilities/center-plugins.d.ts +7 -0
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/center-prompts.d.ts +32 -59
- package/dist/capabilities/center-prompts.js +18 -32
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/engine-capabilities.js +2 -1
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +30 -6
- package/dist/capabilities/hands-lane.js +11 -1
- package/dist/capabilities/mcp-probe.d.ts +48 -3
- package/dist/capabilities/mcp-probe.js +47 -135
- package/dist/capabilities/scenarios.d.ts +95 -45
- package/dist/capabilities/scenarios.js +53 -38
- package/dist/capabilities/skills.d.ts +19 -4
- package/dist/capabilities/skills.js +30 -10
- package/dist/capability-facts.d.ts +34 -4
- package/dist/capability-facts.js +28 -2
- package/dist/center-config-tier.d.ts +69 -0
- package/dist/center-config-tier.js +42 -0
- package/dist/checkpoint-identity.d.ts +16 -0
- package/dist/checkpoint-identity.js +19 -0
- package/dist/config-catalog.d.ts +17 -10
- package/dist/config-catalog.js +47 -32
- package/dist/config-d-migration.d.ts +3 -1
- package/dist/config-d-migration.js +2 -2
- package/dist/config-dir-trust.d.ts +114 -0
- package/dist/config-dir-trust.js +388 -0
- package/dist/config-invariants.d.ts +18 -5
- package/dist/config-invariants.js +35 -3
- package/dist/config-lkg.d.ts +9 -9
- package/dist/config-lkg.js +20 -4
- package/dist/config-provider.d.ts +9 -2
- package/dist/config-provider.js +18 -6
- package/dist/config-types.d.ts +75 -20
- package/dist/config-types.js +8 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +137 -62
- package/dist/delegated-session-owner.d.ts +185 -0
- package/dist/delegated-session-owner.js +119 -0
- package/dist/device-protocol.d.ts +5 -3
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.js +21 -2
- package/dist/device-ws-protocol.d.ts +70 -6
- package/dist/device-ws-protocol.js +50 -22
- package/dist/elicitation.js +3 -2
- package/dist/entry-problems.d.ts +34 -0
- package/dist/entry-problems.js +14 -0
- package/dist/env-name-allowlist-knobs.d.ts +12 -2
- package/dist/env-name-allowlist-knobs.js +5 -4
- package/dist/exec-bounds.d.ts +15 -3
- package/dist/exec-bounds.js +4 -0
- package/dist/execution-lane-caps.d.ts +56 -10
- package/dist/execution-lane-caps.js +50 -0
- package/dist/field-conflict.d.ts +43 -0
- package/dist/field-conflict.js +20 -0
- package/dist/fleet/fleet-bus.d.ts +80 -6
- package/dist/fleet/fleet-bus.js +41 -9
- package/dist/hooks/classic-stations.d.ts +91 -0
- package/dist/hooks/classic-stations.js +27 -0
- package/dist/hooks/command-spawn.d.ts +57 -0
- package/dist/hooks/command-spawn.js +139 -0
- package/dist/hooks/hook-llm.d.ts +8 -5
- package/dist/hooks/hook-llm.js +79 -36
- package/dist/hooks/hook-runner.d.ts +349 -95
- package/dist/hooks/hook-runner.js +855 -568
- package/dist/hooks/hook-source.d.ts +44 -0
- package/dist/hooks/hook-source.js +40 -0
- package/dist/http/active-run-conflict.d.ts +5 -13
- package/dist/http/active-run-conflict.js +17 -27
- package/dist/http/admission.d.ts +8 -2
- package/dist/http/admission.js +188 -101
- package/dist/http/approval-sweeps.js +99 -21
- package/dist/http/caller-trust.d.ts +77 -0
- package/dist/http/caller-trust.js +143 -0
- package/dist/http/checkpoint-ctx.d.ts +41 -24
- package/dist/http/checkpoint-ctx.js +23 -16
- package/dist/http/claimed-run.d.ts +14 -0
- package/dist/http/claimed-run.js +15 -0
- package/dist/http/device-placement.d.ts +2 -4
- package/dist/http/device-placement.js +7 -11
- package/dist/http/dispatch.d.ts +0 -4
- package/dist/http/dispatch.js +15 -13
- package/dist/http/durable-run-leg.d.ts +6 -33
- package/dist/http/durable-run-leg.js +36 -10
- package/dist/http/health-self-report.d.ts +7 -5
- package/dist/http/health-self-report.js +4 -18
- package/dist/http/owner-access.d.ts +56 -0
- package/dist/http/owner-access.js +42 -0
- package/dist/http/principal-gate.d.ts +70 -28
- package/dist/http/principal-gate.js +61 -15
- package/dist/http/readiness.d.ts +11 -5
- package/dist/http/readiness.js +10 -3
- package/dist/http/refusal-body.d.ts +21 -0
- package/dist/http/refusal-body.js +4 -0
- package/dist/http/request-guards.d.ts +43 -23
- package/dist/http/request-guards.js +53 -17
- package/dist/http/resume-ctx-read.d.ts +2 -5
- package/dist/http/resume-ctx-read.js +7 -4
- package/dist/http/resume-legs.d.ts +28 -4
- package/dist/http/resume-legs.js +169 -91
- package/dist/http/retired-service-deps.d.ts +43 -0
- package/dist/http/retired-service-deps.js +18 -0
- package/dist/http/route-ctx.d.ts +44 -28
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/a2a-serve.js +3 -4
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.js +1 -1
- package/dist/http/routes/admin-memory-optout.js +5 -1
- package/dist/http/routes/adoption.js +2 -9
- package/dist/http/routes/approvals-assistant.js +49 -48
- package/dist/http/routes/background.js +3 -2
- package/dist/http/routes/capabilities.js +21 -5
- package/dist/http/routes/config-catalog.d.ts +3 -3
- package/dist/http/routes/config-catalog.js +1 -1
- package/dist/http/routes/devices.js +3 -17
- package/dist/http/routes/diagnostics.js +6 -2
- package/dist/http/routes/images.js +6 -7
- package/dist/http/routes/leader.js +1 -8
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +3 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-consolidation.js +1 -1
- package/dist/http/routes/memory-origin.js +1 -1
- package/dist/http/routes/memory-policy.d.ts +1 -1
- package/dist/http/routes/memory-policy.js +3 -2
- package/dist/http/routes/notify-wake.js +8 -21
- package/dist/http/routes/observability.js +2 -2
- package/dist/http/routes/principal-inbox.d.ts +39 -0
- package/dist/http/routes/principal-inbox.js +76 -0
- package/dist/http/routes/retention-ops.js +2 -9
- package/dist/http/routes/rules.js +5 -18
- package/dist/http/routes/runs.d.ts +5 -0
- package/dist/http/routes/runs.js +34 -107
- package/dist/http/routes/session-sync.js +15 -26
- package/dist/http/routes/sessions.js +30 -40
- package/dist/http/routes/shared-memory.js +2 -13
- package/dist/http/routes/side-query.js +8 -7
- package/dist/http/routes/tasks.js +67 -21
- package/dist/http/routes/workflows.d.ts +351 -0
- package/dist/http/routes/workflows.js +153 -119
- package/dist/http/send.d.ts +33 -10
- package/dist/http/send.js +21 -4
- package/dist/http/server.d.ts +27 -18
- package/dist/http/server.js +34 -6
- package/dist/http/stored-body-replay.js +17 -4
- package/dist/http/submit-ctx.d.ts +25 -0
- package/dist/http/submit-ctx.js +40 -0
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +5 -4
- package/dist/http/wire-types.d.ts +11 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -2
- package/dist/lane-home-probe.d.ts +112 -0
- package/dist/lane-home-probe.js +101 -0
- package/dist/leader/endpoint.d.ts +4 -1
- package/dist/leader/endpoint.js +11 -10
- package/dist/limit-refusal.d.ts +59 -0
- package/dist/limit-refusal.js +15 -0
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +32 -19
- package/dist/mcp-assertion-header.js +10 -9
- package/dist/mcp-tool-faces.d.ts +5 -0
- package/dist/mcp-tool-faces.js +6 -4
- package/dist/memory-project-layer.d.ts +37 -0
- package/dist/memory-project-layer.js +46 -0
- package/dist/memory-scope.d.ts +24 -17
- package/dist/memory-scope.js +24 -8
- package/dist/memory-sync-client.d.ts +27 -13
- package/dist/memory-sync-client.js +107 -18
- package/dist/model-entry-refusal.d.ts +9 -1
- package/dist/model-entry-refusal.js +6 -1
- package/dist/model-plane-face.d.ts +51 -0
- package/dist/model-plane-face.js +24 -0
- package/dist/model-provider.d.ts +44 -12
- package/dist/model-provider.js +21 -2
- package/dist/model-route-endpoint.d.ts +9 -12
- package/dist/model-route-endpoint.js +8 -11
- package/dist/model-route-env.d.ts +22 -0
- package/dist/model-route-env.js +2 -0
- package/dist/model-route-gap.d.ts +60 -30
- package/dist/model-route-gap.js +21 -15
- package/dist/observability/corrupt-read-seat.d.ts +7 -0
- package/dist/observability/corrupt-read-seat.js +9 -0
- package/dist/observability/cost-taxonomy.d.ts +7 -7
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +18 -4
- package/dist/observability/fail-open.d.ts +76 -14
- package/dist/observability/fail-open.js +76 -14
- package/dist/observability/metrics.js +4 -1
- package/dist/observability/prompt-manifest.d.ts +33 -82
- package/dist/observability/prompt-manifest.js +122 -17
- package/dist/observability/run-terminal.js +14 -6
- package/dist/observability/secret-env-scrub.d.ts +13 -1
- package/dist/observability/secret-env-scrub.js +13 -1
- package/dist/observability/tool-trace.d.ts +16 -21
- package/dist/observability/tool-trace.js +58 -49
- package/dist/operator-ask.d.ts +10 -0
- package/dist/operator-ask.js +3 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
- package/dist/orchestration/workflow-agent-session-index.js +9 -13
- package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
- package/dist/orchestration/workflow-completion-inbox.js +13 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
- package/dist/orchestration/workflow-notify-journal.js +27 -34
- package/dist/orchestration/workflow-park-decision.d.ts +89 -0
- package/dist/orchestration/workflow-park-decision.js +118 -0
- package/dist/org-delivery-gate.d.ts +81 -0
- package/dist/org-delivery-gate.js +59 -0
- package/dist/panel-tool-names.d.ts +23 -0
- package/dist/panel-tool-names.js +8 -0
- package/dist/parked-decide.d.ts +39 -6
- package/dist/parked-decide.js +98 -51
- package/dist/parked-revive-surface.d.ts +9 -2
- package/dist/parked-revive-surface.js +11 -8
- package/dist/per-task-image.d.ts +7 -1
- package/dist/per-task-image.js +3 -3
- package/dist/permission-rule-list.d.ts +1 -1
- package/dist/permission-rule-list.js +2 -2
- package/dist/plain-string-record.d.ts +25 -0
- package/dist/plain-string-record.js +32 -0
- package/dist/plugins/approval-ask-store-file.d.ts +16 -1
- package/dist/plugins/approval-ask-store-file.js +21 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
- package/dist/plugins/approval-ask-store-memory.js +34 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
- package/dist/plugins/approval-ask-store-sql.js +37 -1
- package/dist/plugins/blob-backend.d.ts +83 -15
- package/dist/plugins/blob-backend.js +68 -15
- package/dist/plugins/breaker-state-sql.d.ts +2 -2
- package/dist/plugins/breaker-state-sql.js +4 -4
- package/dist/plugins/caching-session-store.d.ts +3 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +28 -4
- package/dist/plugins/checkpoint-store-sql.js +59 -28
- package/dist/plugins/file-history-store-sql.d.ts +5 -2
- package/dist/plugins/file-history-store-sql.js +3 -1
- package/dist/plugins/file-run-store.d.ts +5 -13
- package/dist/plugins/file-run-store.js +9 -0
- package/dist/plugins/fork-routing-session-store.d.ts +9 -1
- package/dist/plugins/fork-routing-session-store.js +12 -1
- package/dist/plugins/host-platform.d.ts +2 -0
- package/dist/plugins/host-platform.js +2 -1
- package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
- package/dist/plugins/k8s-bg-scripts.js +22 -0
- package/dist/plugins/local-checkpoint-store.d.ts +2 -0
- package/dist/plugins/local-checkpoint-store.js +11 -3
- package/dist/plugins/local-session-store.d.ts +1 -1
- package/dist/plugins/local-session-store.js +1 -1
- package/dist/plugins/local-session-window.d.ts +39 -0
- package/dist/plugins/local-session-window.js +54 -0
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
- package/dist/plugins/mailbox-store-sql.d.ts +30 -48
- package/dist/plugins/mailbox-store-sql.js +77 -52
- package/dist/plugins/memory-embedder-fingerprint.js +2 -1
- package/dist/plugins/memory-run-store.d.ts +5 -13
- package/dist/plugins/memory-run-store.js +8 -0
- package/dist/plugins/one-time-migrations.d.ts +245 -93
- package/dist/plugins/one-time-migrations.js +668 -207
- package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
- package/dist/plugins/org-delivery-quota-store.js +228 -0
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
- package/dist/plugins/permission-rule-store-sql.js +4 -18
- package/dist/plugins/pg-pool.js +39 -7
- package/dist/plugins/pg-session-storage.d.ts +20 -8
- package/dist/plugins/pg-session-storage.js +34 -21
- package/dist/plugins/remote-env-adb.d.ts +12 -12
- package/dist/plugins/remote-env-adb.js +21 -6
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +1 -1
- package/dist/plugins/remote-env-e2b.js +21 -9
- package/dist/plugins/remote-env-host.d.ts +9 -9
- package/dist/plugins/remote-env-host.js +10 -32
- package/dist/plugins/remote-env-k8s.d.ts +103 -38
- package/dist/plugins/remote-env-k8s.js +288 -95
- package/dist/plugins/remote-env-local-docker.d.ts +27 -13
- package/dist/plugins/remote-env-local-docker.js +82 -11
- package/dist/plugins/remote-env-ssh.d.ts +21 -12
- package/dist/plugins/remote-env-ssh.js +117 -5
- package/dist/plugins/remote-shell.d.ts +0 -1
- package/dist/plugins/remote-shell.js +1 -2
- package/dist/plugins/retention-store-sql.js +6 -2
- package/dist/plugins/run-store-sql.d.ts +16 -14
- package/dist/plugins/run-store-sql.js +22 -2
- package/dist/plugins/schema-consistency.d.ts +54 -0
- package/dist/plugins/schema-consistency.js +52 -0
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-user-file.js +2 -2
- package/dist/plugins/session-epoch-anchor.d.ts +39 -0
- package/dist/plugins/session-epoch-anchor.js +22 -0
- package/dist/plugins/session-store.d.ts +18 -8
- package/dist/plugins/session-store.js +15 -4
- package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
- package/dist/plugins/shared-memory-store-sql.js +5 -16
- package/dist/plugins/sql-driver.d.ts +205 -1
- package/dist/plugins/sql-driver.js +325 -13
- package/dist/plugins/sql-errors.d.ts +64 -0
- package/dist/plugins/sql-errors.js +57 -0
- package/dist/plugins/store-backend.d.ts +36 -1
- package/dist/plugins/store-backend.js +42 -15
- package/dist/plugins/store-contracts.d.ts +42 -0
- package/dist/plugins/store-contracts.js +1 -0
- package/dist/plugins/tidb-pool.d.ts +6 -1
- package/dist/plugins/tidb-pool.js +44 -10
- package/dist/plugins/tidb-session-storage.d.ts +8 -11
- package/dist/plugins/tidb-session-storage.js +6 -16
- package/dist/plugins/tidb-session-store.d.ts +24 -4
- package/dist/plugins/tidb-session-store.js +29 -5
- package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
- package/dist/plugins/workflow-journal-store-sql.js +19 -6
- package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
- package/dist/plugins/workflow-run-store-sql.js +50 -9
- package/dist/posture-source.d.ts +18 -10
- package/dist/posture-source.js +4 -1
- package/dist/project-identity.d.ts +45 -6
- package/dist/project-identity.js +162 -8
- package/dist/project-layer-key.d.ts +28 -0
- package/dist/project-layer-key.js +29 -0
- package/dist/prompts-domain-validate.d.ts +17 -61
- package/dist/prompts-domain-validate.js +9 -119
- package/dist/question.js +3 -2
- package/dist/read-deny-entry.d.ts +25 -0
- package/dist/read-deny-entry.js +12 -0
- package/dist/read-face-posture.d.ts +37 -11
- package/dist/read-face-posture.js +32 -3
- package/dist/request-key-closure.d.ts +9 -2
- package/dist/request-key-closure.js +11 -5
- package/dist/request-value-closure.d.ts +5 -0
- package/dist/request-value-closure.js +11 -3
- package/dist/rule-entry-list.d.ts +38 -11
- package/dist/rule-entry-list.js +106 -30
- package/dist/rules-consent.js +3 -4
- package/dist/run-acceptance.d.ts +19 -3
- package/dist/run-acceptance.js +13 -1
- package/dist/run-local.js +54 -24
- package/dist/runs.d.ts +15 -8
- package/dist/runs.js +35 -1
- package/dist/runtime-caps-resolver.d.ts +16 -6
- package/dist/runtime-caps-resolver.js +18 -5
- package/dist/sandbox-pkg-source.d.ts +19 -1
- package/dist/sandbox-pkg-source.js +29 -7
- package/dist/security.d.ts +87 -9
- package/dist/security.js +52 -9
- package/dist/server-secret-env.d.ts +44 -18
- package/dist/server-secret-env.js +49 -8
- package/dist/session-error-wire.d.ts +19 -2
- package/dist/session-error-wire.js +5 -1
- package/dist/session-incarnation.d.ts +15 -5
- package/dist/session-incarnation.js +16 -9
- package/dist/session-titled-frame.d.ts +33 -0
- package/dist/session-titled-frame.js +4 -0
- package/dist/session-titler.d.ts +9 -1
- package/dist/session-titler.js +27 -6
- package/dist/spec-fields.d.ts +28 -16
- package/dist/spec-fields.js +66 -54
- package/dist/sql-engine-posture.d.ts +31 -4
- package/dist/sql-engine-posture.js +36 -1
- package/dist/stored-request-tool-axes-m19.d.ts +19 -4
- package/dist/stored-request-tool-axes-m19.js +30 -2
- package/dist/task-a2a.d.ts +9 -1
- package/dist/task-a2a.js +73 -47
- package/dist/task-cwd.d.ts +1 -1
- package/dist/task-mcp.d.ts +46 -58
- package/dist/task-mcp.js +82 -107
- package/dist/task-settings.d.ts +95 -108
- package/dist/task-settings.js +77 -98
- package/dist/text-bidi.d.ts +1 -48
- package/dist/text-bidi.js +3 -5
- package/dist/tighten-only-seats.d.ts +150 -0
- package/dist/tighten-only-seats.js +196 -0
- package/dist/tool-approval.d.ts +304 -88
- package/dist/tool-approval.js +623 -239
- package/dist/trace/core-keyset-guard.d.ts +122 -20
- package/dist/trace/engine-notice-wire.d.ts +11 -3
- package/dist/trace/engine-notice-wire.js +8 -1
- package/dist/trace/exit-seat.d.ts +28 -0
- package/dist/trace/exit-seat.js +76 -0
- package/dist/trace/ledger-events.d.ts +6 -3
- package/dist/trace/ledger-sink.js +4 -1
- package/dist/trace/model-segment.js +1 -1
- package/dist/trace/project.d.ts +98 -17
- package/dist/trace/project.js +98 -50
- package/dist/trace/projection-table.d.ts +100 -0
- package/dist/trace/projection-table.js +130 -0
- package/dist/trace/redact.d.ts +54 -0
- package/dist/trace/redact.js +778 -152
- package/dist/trace/self-minted-wire.d.ts +345 -1
- package/dist/trace/self-minted-wire.js +117 -1
- package/dist/trace/wire-projection-faces.d.ts +377 -3
- package/dist/trace/wire-projection-faces.js +164 -1
- package/dist/url-refusal.js +2 -0
- package/dist/web-admin-client/apply-effective.d.ts +12 -0
- package/dist/web-admin-client/apply-effective.js +56 -5
- package/dist/web-admin-client/candidate-gate.d.ts +6 -6
- package/dist/web-admin-client/candidate-gate.js +3 -3
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/http-client.js +0 -6
- package/dist/web-admin-client/read-face.js +5 -9
- package/dist/web-admin-client/read-warnings.d.ts +13 -0
- package/dist/web-admin-client/read-warnings.js +10 -0
- package/dist/web-admin-client/skills-mcp.d.ts +3 -0
- package/dist/web-admin-client/skills-mcp.js +11 -9
- package/dist/web-admin-client/types.d.ts +2 -3
- package/dist/workspace-cache.d.ts +35 -0
- package/dist/workspace-cache.js +36 -0
- package/dist/workspace-space.d.ts +3 -2
- package/dist/workspace-space.js +2 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +65 -10
- package/package.json +8 -4
- package/scripts/drain-check-local.mjs +34 -1
- package/scripts/drain-check.mysql.sql +30 -2
- package/scripts/drain-check.pg.sql +29 -2
- package/dist/capabilities/memory-notice.d.ts +0 -138
- package/dist/capabilities/memory-notice.js +0 -64
- package/dist/capabilities/prompt.d.ts +0 -17
- package/dist/capabilities/prompt.js +0 -6
- package/dist/mcp-content-origin.d.ts +0 -34
- package/dist/mcp-content-origin.js +0 -10
- package/dist/tool-axis-words.d.ts +0 -27
- package/dist/tool-axis-words.js +0 -9
- package/dist/wire-name-bound.d.ts +0 -14
- package/dist/wire-name-bound.js +0 -13
|
@@ -11,13 +11,14 @@ import { spaceCommitAlive } from "../workspace-space-commit.js";
|
|
|
11
11
|
import { incarnationLogPrefix, redactIncarnations } from "../session-incarnation.js";
|
|
12
12
|
import { WORKSPACE_CACHE_SPACE_LABEL, workspaceCacheClaimOf, workspaceCacheEnv } from "../workspace-cache.js";
|
|
13
13
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
14
|
-
import { commandBoundsOf, laneLivenessOf,
|
|
14
|
+
import { buildLivenessCut, commandBoundsOf, laneLivenessOf, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
|
|
15
15
|
import { SANDBOX_IPV6_DEFAULT, sandboxEgressGaps, sandboxPodSysctls } from "../sandbox-egress-policy.js";
|
|
16
16
|
import { FileError, ExecutionError, RemoteExecutionError, withRetry, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
|
|
17
17
|
import { fileErrorFromExec, classifyFsStderr } from "./remote-env-file-error.js";
|
|
18
18
|
import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
19
19
|
import { randomUUID } from "node:crypto";
|
|
20
20
|
import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
|
|
21
|
+
import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
|
|
21
22
|
const PROVIDER = "k8s";
|
|
22
23
|
const K8S_BG_DIR_ROOT = "/tmp/.sema-bg";
|
|
23
24
|
const K8S_BG_MAX_CONCURRENT = numEnvOr("K8S_BG_MAX_CONCURRENT", 8, 1);
|
|
@@ -26,8 +27,8 @@ const K8S_BG_MAX_TIMEOUT_SEC = numEnvOr("K8S_BG_MAX_TIMEOUT_SEC", 1800, K8S_BG_D
|
|
|
26
27
|
const K8S_BG_READ_CAP = numEnvOr("K8S_BG_READ_CAP", 1024 * 1024, 64 * 1024);
|
|
27
28
|
const K8S_BG_FILE_CAP = numEnvOr("K8S_BG_FILE_CAP", 64 * 1024 * 1024, K8S_BG_READ_CAP);
|
|
28
29
|
const K8S_BG_MEM_CAP = numEnvOr("K8S_BG_MEM_CAP", 8 * 1024 * 1024, 64 * 1024);
|
|
29
|
-
import { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, parseBgPollOutput, } from "./k8s-bg-scripts.js";
|
|
30
|
-
export { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, parseBgPollOutput, } from "./k8s-bg-scripts.js";
|
|
30
|
+
import { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, buildKillAllScript, KILL_ALL_RECHECK_MS, parseBgPollOutput, } from "./k8s-bg-scripts.js";
|
|
31
|
+
export { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, buildKillAllScript, parseBgPollOutput, } from "./k8s-bg-scripts.js";
|
|
31
32
|
const ok = (value) => ({ ok: true, value });
|
|
32
33
|
const unsupported = (op) => ({
|
|
33
34
|
ok: false,
|
|
@@ -116,6 +117,45 @@ class FenceCallAborted extends Error {
|
|
|
116
117
|
function dataTimeoutMsOf(config) {
|
|
117
118
|
return config.dataTimeoutMs ?? WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT;
|
|
118
119
|
}
|
|
120
|
+
const RPC_TIMEOUT_MS_DEFAULT = 30_000;
|
|
121
|
+
export function rpcTimeoutMsOf(config) {
|
|
122
|
+
return config.rpcTimeoutMs ?? RPC_TIMEOUT_MS_DEFAULT;
|
|
123
|
+
}
|
|
124
|
+
export function boundedStoreCall(op, bound) {
|
|
125
|
+
const signal = bound.abort?.signal;
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
if (signal?.aborted)
|
|
128
|
+
return reject(bound.abort.error());
|
|
129
|
+
const settle = () => {
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
signal?.removeEventListener("abort", onAbort);
|
|
132
|
+
};
|
|
133
|
+
const onAbort = () => {
|
|
134
|
+
settle();
|
|
135
|
+
reject(bound.abort.error());
|
|
136
|
+
};
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
settle();
|
|
139
|
+
reject(bound.onTimeout());
|
|
140
|
+
}, bound.ms);
|
|
141
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
142
|
+
let pending;
|
|
143
|
+
try {
|
|
144
|
+
pending = op();
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
settle();
|
|
148
|
+
return reject(e);
|
|
149
|
+
}
|
|
150
|
+
pending.then((v) => {
|
|
151
|
+
settle();
|
|
152
|
+
resolve(v);
|
|
153
|
+
}, (e) => {
|
|
154
|
+
settle();
|
|
155
|
+
reject(e);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
119
159
|
import { execSocketTlsOptions, exitCodeFromStatus } from "./k8s-exec-protocol.js";
|
|
120
160
|
export { execSocketTlsOptions } from "./k8s-exec-protocol.js";
|
|
121
161
|
export function errorMessageOf(e) {
|
|
@@ -134,10 +174,14 @@ export class RemoteK8sExecutionEnv {
|
|
|
134
174
|
podIP;
|
|
135
175
|
destroyed = false;
|
|
136
176
|
handleSnapshotId;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
177
|
+
inFlight = new Set();
|
|
178
|
+
idleWaiters = [];
|
|
179
|
+
connectInFlight;
|
|
180
|
+
connectRefusal;
|
|
181
|
+
get homeDir() {
|
|
182
|
+
return this.homeSeat.home;
|
|
183
|
+
}
|
|
184
|
+
homeSeat;
|
|
141
185
|
cdPath = "unknown";
|
|
142
186
|
cfg;
|
|
143
187
|
deps;
|
|
@@ -150,6 +194,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
150
194
|
cacheUnavailableSaid = false;
|
|
151
195
|
logger;
|
|
152
196
|
onWorkspaceSnapshot;
|
|
197
|
+
cacheMisses;
|
|
153
198
|
constructor(config, deps) {
|
|
154
199
|
this.cfg = {
|
|
155
200
|
apiUrl: config.apiUrl,
|
|
@@ -162,7 +207,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
162
207
|
mountPath: config.mountPath ?? K8S_WORKSPACE_MOUNT_DEFAULT,
|
|
163
208
|
timeoutMs: config.timeoutMs ?? 30 * 60_000,
|
|
164
209
|
readyTimeoutMs: config.readyTimeoutMs ?? 120_000,
|
|
165
|
-
rpcTimeoutMs: config
|
|
210
|
+
rpcTimeoutMs: rpcTimeoutMsOf(config),
|
|
166
211
|
livenessMs: config.livenessMs ?? LIVENESS_MS_DEFAULT,
|
|
167
212
|
dataTimeoutMs: dataTimeoutMsOf(config),
|
|
168
213
|
cpu: config.cpu ?? "1",
|
|
@@ -176,7 +221,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
176
221
|
...(config.snapshotExclude && config.snapshotExclude.length > 0 ? { snapshotExclude: config.snapshotExclude } : {}),
|
|
177
222
|
};
|
|
178
223
|
this.cwd = this.cfg.mountPath;
|
|
179
|
-
this.
|
|
224
|
+
this.homeSeat = new LaneHomeSeat({ lane: PROVIDER, declared: config.homeDir, verifiesDeclared: true, onAbsent: config.onHomeAbsent });
|
|
180
225
|
this.deps = deps ?? {};
|
|
181
226
|
this.laneLiveness = laneLivenessOf(this.cfg.livenessMs, "K8S_LIVENESS_MS");
|
|
182
227
|
const occupant = occupantKeyOf(config.spaceOccupant);
|
|
@@ -185,6 +230,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
185
230
|
this.cacheLane = cacheLaneOf(config.cache, config.spaceOccupant);
|
|
186
231
|
this.logger = config.logger;
|
|
187
232
|
this.onWorkspaceSnapshot = config.onWorkspaceSnapshot;
|
|
233
|
+
this.cacheMisses = config.cacheMisses;
|
|
188
234
|
this.capabilities = { isolation: true, suspendable: !!this.cfg.s3Snapshot, canonicalPathAuthoritative: true };
|
|
189
235
|
const bgMax = Math.max(1, Math.min(K8S_BG_MAX_TIMEOUT_SEC, Math.floor(this.cfg.timeoutMs / 1000)));
|
|
190
236
|
this.backgroundCapabilities = {
|
|
@@ -283,7 +329,15 @@ export class RemoteK8sExecutionEnv {
|
|
|
283
329
|
...(url.startsWith("wss:") ? execSocketTlsOptions(ca, this.cfg.insecureTls) : {}),
|
|
284
330
|
});
|
|
285
331
|
}
|
|
286
|
-
|
|
332
|
+
connect(config) {
|
|
333
|
+
this.connectInFlight ??= this.establish(config).finally(() => {
|
|
334
|
+
this.connectInFlight = undefined;
|
|
335
|
+
});
|
|
336
|
+
return this.connectInFlight;
|
|
337
|
+
}
|
|
338
|
+
async establish(config) {
|
|
339
|
+
if (this.connectRefusal)
|
|
340
|
+
return this.connectRefusal;
|
|
287
341
|
const signal = config?.abortSignal;
|
|
288
342
|
const lane = this.spaceLane;
|
|
289
343
|
switch (lane.state) {
|
|
@@ -295,8 +349,6 @@ export class RemoteK8sExecutionEnv {
|
|
|
295
349
|
case "unreadable":
|
|
296
350
|
return this.provision(signal);
|
|
297
351
|
case "open": {
|
|
298
|
-
if (this.restoreRefusal)
|
|
299
|
-
return this.restoreRefusal;
|
|
300
352
|
const settled = await this.awaitSpaceCommit(lane.space, signal);
|
|
301
353
|
if (!settled.ok)
|
|
302
354
|
return settled;
|
|
@@ -322,27 +374,10 @@ export class RemoteK8sExecutionEnv {
|
|
|
322
374
|
await this.fenceCall(() => space.fence.end(), "delete").catch(() => undefined);
|
|
323
375
|
}
|
|
324
376
|
fenceCall(op, what, signal) {
|
|
325
|
-
return
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
clearTimeout(timer);
|
|
330
|
-
reject(new FenceCallAborted());
|
|
331
|
-
};
|
|
332
|
-
const timer = setTimeout(() => {
|
|
333
|
-
signal?.removeEventListener("abort", onAbort);
|
|
334
|
-
reject(new Error(`workspace space commit state ${what} did not answer within ${this.cfg.rpcTimeoutMs}ms`));
|
|
335
|
-
}, this.cfg.rpcTimeoutMs);
|
|
336
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
337
|
-
op().then((v) => {
|
|
338
|
-
clearTimeout(timer);
|
|
339
|
-
signal?.removeEventListener("abort", onAbort);
|
|
340
|
-
resolve(v);
|
|
341
|
-
}, (e) => {
|
|
342
|
-
clearTimeout(timer);
|
|
343
|
-
signal?.removeEventListener("abort", onAbort);
|
|
344
|
-
reject(e);
|
|
345
|
-
});
|
|
377
|
+
return boundedStoreCall(op, {
|
|
378
|
+
ms: this.cfg.rpcTimeoutMs,
|
|
379
|
+
onTimeout: () => new Error(`workspace space commit state ${what} did not answer within ${this.cfg.rpcTimeoutMs}ms`),
|
|
380
|
+
...(signal ? { abort: { signal, error: () => new FenceCallAborted() } } : {}),
|
|
346
381
|
});
|
|
347
382
|
}
|
|
348
383
|
sayUnavailable(reason) {
|
|
@@ -355,6 +390,21 @@ export class RemoteK8sExecutionEnv {
|
|
|
355
390
|
effect: "WORKSPACE_PERSIST=space, but the session store has no occupant (incarnation) for this run's session — its /workspace is neither restored nor saved (it is never keyed by the session id instead)",
|
|
356
391
|
});
|
|
357
392
|
}
|
|
393
|
+
holdRefusal(error) {
|
|
394
|
+
this.connectRefusal = { ok: false, error };
|
|
395
|
+
return this.connectRefusal;
|
|
396
|
+
}
|
|
397
|
+
refusePod(pod, phase, reason, message) {
|
|
398
|
+
this.logger?.warn("sandbox_pod_refused", {
|
|
399
|
+
pod,
|
|
400
|
+
phase,
|
|
401
|
+
reason: reason ?? "?",
|
|
402
|
+
...(message ? { detail: message.slice(0, 300) } : {}),
|
|
403
|
+
...(this.cfg.sessionId ? { sessionId: this.cfg.sessionId } : {}),
|
|
404
|
+
effect: "the cluster created this run's sandbox pod and it ended before it ran — this run's operations answer the same refusal without creating another pod; a retry of the run builds a fresh sandbox",
|
|
405
|
+
});
|
|
406
|
+
return this.holdRefusal(new RemoteExecutionError("connect_failed", `pod entered ${phase} before Running (${reason ?? "?"}) — the cluster refused this run's sandbox pod; the refusal stands for the run (no pod is created again), a retry of the run asks the cluster afresh`));
|
|
407
|
+
}
|
|
358
408
|
refuseUnreadableOccupant(code) {
|
|
359
409
|
const ways = [...(this.spaceLane.state === "unreadable" ? ["WORKSPACE_PERSIST=off"] : []), ...(this.cacheLane.state === "unreadable" ? ["WORKSPACE_CACHE=off"] : [])].join(" and ");
|
|
360
410
|
if (!this.unreadableOccupantSaid) {
|
|
@@ -374,6 +424,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
374
424
|
provision(signal) {
|
|
375
425
|
const space = this.spaceLane;
|
|
376
426
|
const cache = this.cacheLane;
|
|
427
|
+
if (this.connectRefusal)
|
|
428
|
+
return Promise.resolve(this.connectRefusal);
|
|
377
429
|
if (space.state === "unreadable")
|
|
378
430
|
return Promise.resolve(this.refuseUnreadableOccupant(space.code));
|
|
379
431
|
if (cache.state === "unreadable")
|
|
@@ -384,19 +436,23 @@ export class RemoteK8sExecutionEnv {
|
|
|
384
436
|
backoffMs: (failed) => this.cfg.retry.backoffMs * failed,
|
|
385
437
|
};
|
|
386
438
|
const once = this.deps.connectOnce ?? ((s) => this.connectOnce(cache, s));
|
|
387
|
-
|
|
439
|
+
const refused = new AbortController();
|
|
440
|
+
const halt = signal ? AbortSignal.any([signal, refused.signal]) : refused.signal;
|
|
441
|
+
return withRetry(async () => {
|
|
442
|
+
const r = await once(signal);
|
|
443
|
+
if (!r.ok && this.connectRefusal)
|
|
444
|
+
refused.abort();
|
|
445
|
+
return r;
|
|
446
|
+
}, policy, { signal: halt });
|
|
388
447
|
}
|
|
389
448
|
async ensureConnected() {
|
|
390
|
-
if (this.podName)
|
|
391
|
-
return ok(undefined);
|
|
392
449
|
if (this.destroyed)
|
|
393
450
|
return { ok: false, error: new ExecutionError("shell_unavailable", "k8s sandbox destroyed") };
|
|
394
|
-
this.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
451
|
+
if (this.podName && this.connectInFlight === undefined)
|
|
452
|
+
return ok(undefined);
|
|
453
|
+
const r = await this.connect();
|
|
454
|
+
if (!r.ok)
|
|
398
455
|
return { ok: false, error: new ExecutionError("shell_unavailable", `k8s sandbox connect failed: ${r.error.message}`, r.error) };
|
|
399
|
-
}
|
|
400
456
|
return ok(undefined);
|
|
401
457
|
}
|
|
402
458
|
async connectOnce(cacheLane, signal) {
|
|
@@ -409,6 +465,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
409
465
|
if (!("cacheMiss" in first))
|
|
410
466
|
return first;
|
|
411
467
|
this.cacheUnavailable(first.cacheMiss, first.detail, cache);
|
|
468
|
+
if (cache)
|
|
469
|
+
this.cacheMisses?.remember(cache.spaceKey, { reason: first.cacheMiss, detail: first.detail }, this.cfg.dataTimeoutMs);
|
|
412
470
|
const second = await this.createPod(signal, undefined);
|
|
413
471
|
return "cacheMiss" in second ? { ok: false, error: new RemoteExecutionError("connect_failed", "unreachable: a pod without the cache volume was given up for it") } : second;
|
|
414
472
|
}
|
|
@@ -420,6 +478,11 @@ export class RemoteK8sExecutionEnv {
|
|
|
420
478
|
this.cacheUnavailable(lane.reason, "the session store has no occupant (incarnation) for this run's session — no space key to find its cache volume by (never keyed by the session id instead)");
|
|
421
479
|
return undefined;
|
|
422
480
|
case "open": {
|
|
481
|
+
const remembered = this.cacheMisses?.recall(lane.cache.spaceKey);
|
|
482
|
+
if (remembered !== undefined) {
|
|
483
|
+
this.cacheUnavailable(remembered.reason, `${remembered.detail} — remembered from an earlier run of this space; not asked for again within ${this.cfg.dataTimeoutMs}ms`, lane.cache);
|
|
484
|
+
return undefined;
|
|
485
|
+
}
|
|
423
486
|
const ensured = await this.ensureCacheClaim(lane.cache, signal);
|
|
424
487
|
if (!ensured.ok) {
|
|
425
488
|
this.cacheUnavailable("claim_failed", ensured.detail, lane.cache);
|
|
@@ -586,7 +649,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
586
649
|
}
|
|
587
650
|
if (phase === "Failed" || phase === "Succeeded") {
|
|
588
651
|
await this.deletePod(name).catch(() => { });
|
|
589
|
-
return
|
|
652
|
+
return this.refusePod(name, phase, status?.reason, status?.message);
|
|
590
653
|
}
|
|
591
654
|
const waiting = status?.containerStatuses?.[0]?.state?.waiting;
|
|
592
655
|
if (waiting?.reason === "ImagePullBackOff" || waiting?.reason === "ErrImagePull") {
|
|
@@ -616,13 +679,24 @@ export class RemoteK8sExecutionEnv {
|
|
|
616
679
|
}
|
|
617
680
|
this.podName = name;
|
|
618
681
|
this.podIP = podIP;
|
|
619
|
-
const
|
|
682
|
+
const askHome = this.homeSeat.asks;
|
|
683
|
+
const mk = await this.runExec(`mkdir -p ${shellQuote(this.cfg.mountPath)}${askHome ? ` && { ${LANE_HOME_PROBE_COMMAND} || true; }` : ""}`);
|
|
620
684
|
if (!mk.ok || mk.value.exitCode !== 0) {
|
|
621
685
|
await this.deletePod(name).catch(() => { });
|
|
622
686
|
this.podName = undefined;
|
|
623
687
|
this.podIP = undefined;
|
|
624
688
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace mkdir failed: ${mk.ok ? mk.value.stderr : mk.error.message}`) };
|
|
625
689
|
}
|
|
690
|
+
if (askHome) {
|
|
691
|
+
const verdict = this.homeSeat.settle({ ok: true, stdout: mk.value.stdout });
|
|
692
|
+
if (verdict !== undefined) {
|
|
693
|
+
await this.deletePod(name).catch(() => { });
|
|
694
|
+
this.podName = undefined;
|
|
695
|
+
this.podIP = undefined;
|
|
696
|
+
const error = new RemoteExecutionError("connect_failed", verdict.refusal);
|
|
697
|
+
return verdict.answered ? this.holdRefusal(error) : { ok: false, error };
|
|
698
|
+
}
|
|
699
|
+
}
|
|
626
700
|
if (cache?.fresh)
|
|
627
701
|
this.logger?.warn("workspace_cache_fresh", { space: incarnationLogPrefix(cache.spaceKey), claim: cache.claim.name, size: cache.size });
|
|
628
702
|
return ok(this.workspaceHandle());
|
|
@@ -864,12 +938,15 @@ export class RemoteK8sExecutionEnv {
|
|
|
864
938
|
await this.endSpace(this.spaceLane.space);
|
|
865
939
|
return r;
|
|
866
940
|
}
|
|
867
|
-
async uploadSnapshot(target) {
|
|
868
|
-
if (this.
|
|
941
|
+
async uploadSnapshot(target, boundMs = this.cfg.dataTimeoutMs) {
|
|
942
|
+
if (target.lane === "park" && this.inFlight.size > 0)
|
|
869
943
|
return { ok: false, error: new RemoteExecutionError("command_in_flight", "cannot suspend while a command is in flight (would snapshot a torn workspace)") };
|
|
870
944
|
const snapshotId = mintSnapshotId(this.occupantIncarnation);
|
|
871
|
-
if (!this.podName)
|
|
872
|
-
return
|
|
945
|
+
if (!this.podName) {
|
|
946
|
+
return target.lane === "space"
|
|
947
|
+
? { ok: false, error: new RemoteExecutionError("connect_failed", "the sandbox pod was gone when the run-end snapshot reached the upload — nothing was saved (the workspace could not be read), and `latest` keeps naming the previous snapshot") }
|
|
948
|
+
: ok(snapshotId);
|
|
949
|
+
}
|
|
873
950
|
const presigned = this.presignSnapshot({ ...target, method: "PUT", snapshotId });
|
|
874
951
|
if (!presigned.ok)
|
|
875
952
|
return presigned;
|
|
@@ -878,7 +955,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
878
955
|
const excludes = (this.cfg.snapshotExclude ?? []).map((item) => ` --exclude=${shellQuote(`./${item}`)}`).join("");
|
|
879
956
|
const cmd = `tar czf ${tmp}${excludes} -C ${shellQuote(this.cfg.mountPath)} . && curl -fsS -X PUT -T ${tmp} ${shellQuote(url)}; ` +
|
|
880
957
|
`rc=$?; rm -f ${tmp}; exit $rc`;
|
|
881
|
-
const r = await this.runExec(cmd, { timeoutMs:
|
|
958
|
+
const r = await this.runExec(cmd, { timeoutMs: boundMs });
|
|
882
959
|
if (!r.ok)
|
|
883
960
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace snapshot upload failed: ${this.redactUrl(r.error.message, url)}`, r.error) };
|
|
884
961
|
if (r.value.exitCode !== 0)
|
|
@@ -941,13 +1018,9 @@ export class RemoteK8sExecutionEnv {
|
|
|
941
1018
|
case "space": {
|
|
942
1019
|
const tries = attempt === 1 ? "1 attempt" : `${attempt} attempts`;
|
|
943
1020
|
this.spaceLine("workspace_restore_failed", target.space, { snapshotId, class: failure.cls, attempts: attempt, boundMs: this.cfg.dataTimeoutMs, error: failure.detail });
|
|
944
|
-
this.
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
"so this run does not start on an empty /workspace. Ways out: raise WORKSPACE_SNAPSHOT_TIMEOUT_MS if the archive is large, " +
|
|
948
|
-
"or set WORKSPACE_PERSIST=off for one run (it starts from an empty /workspace and deletes no snapshot)", failure.cause),
|
|
949
|
-
};
|
|
950
|
-
return this.restoreRefusal;
|
|
1021
|
+
return this.holdRefusal(new RemoteExecutionError("connect_failed", `${failure.detail} — the workspace's latest snapshot (${snapshotId}) is in the store but did not come back (${failure.cls}, ${tries}, bound ${this.cfg.dataTimeoutMs}ms each), ` +
|
|
1022
|
+
"so this run does not start on an empty /workspace. Ways out: raise WORKSPACE_SNAPSHOT_TIMEOUT_MS if the archive is large, " +
|
|
1023
|
+
"or set WORKSPACE_PERSIST=off for one run (it starts from an empty /workspace and deletes no snapshot)", failure.cause));
|
|
951
1024
|
}
|
|
952
1025
|
default: {
|
|
953
1026
|
const unreachable = target;
|
|
@@ -987,6 +1060,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
987
1060
|
return ok(undefined);
|
|
988
1061
|
}
|
|
989
1062
|
async reconnect(_sessionToken) {
|
|
1063
|
+
if (this.connectInFlight)
|
|
1064
|
+
return this.connectInFlight;
|
|
990
1065
|
if (this.podName) {
|
|
991
1066
|
const alive = await this.request("GET", `/api/v1/namespaces/${this.cfg.namespace}/pods/${this.podName}`).catch(() => undefined);
|
|
992
1067
|
const phase = alive?.json?.status?.phase;
|
|
@@ -1032,6 +1107,63 @@ export class RemoteK8sExecutionEnv {
|
|
|
1032
1107
|
async cleanup() {
|
|
1033
1108
|
return this.destroy();
|
|
1034
1109
|
}
|
|
1110
|
+
beginExec() {
|
|
1111
|
+
const ac = new AbortController();
|
|
1112
|
+
this.inFlight.add(ac);
|
|
1113
|
+
return ac;
|
|
1114
|
+
}
|
|
1115
|
+
endExec(ac) {
|
|
1116
|
+
this.inFlight.delete(ac);
|
|
1117
|
+
if (this.inFlight.size > 0)
|
|
1118
|
+
return;
|
|
1119
|
+
for (const wake of this.idleWaiters.splice(0))
|
|
1120
|
+
wake();
|
|
1121
|
+
}
|
|
1122
|
+
whenIdle(ms) {
|
|
1123
|
+
if (this.inFlight.size === 0)
|
|
1124
|
+
return Promise.resolve(true);
|
|
1125
|
+
return new Promise((resolve) => {
|
|
1126
|
+
const idle = () => {
|
|
1127
|
+
clearTimeout(timer);
|
|
1128
|
+
resolve(true);
|
|
1129
|
+
};
|
|
1130
|
+
const timer = setTimeout(() => {
|
|
1131
|
+
this.idleWaiters = this.idleWaiters.filter((w) => w !== idle);
|
|
1132
|
+
resolve(false);
|
|
1133
|
+
}, ms);
|
|
1134
|
+
this.idleWaiters.push(idle);
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
async quiesce(space) {
|
|
1138
|
+
const inFlight = this.inFlight.size;
|
|
1139
|
+
if (inFlight === 0 && !this.podName)
|
|
1140
|
+
return { ok: true, waitedMs: 0 };
|
|
1141
|
+
const startedAt = Date.now();
|
|
1142
|
+
const ended = await this.whenIdle(this.cfg.rpcTimeoutMs);
|
|
1143
|
+
const killed = await this.killPodProcesses();
|
|
1144
|
+
for (const ac of [...this.inFlight])
|
|
1145
|
+
ac.abort();
|
|
1146
|
+
const waitedMs = Date.now() - startedAt;
|
|
1147
|
+
if (!ended) {
|
|
1148
|
+
this.spaceLine("workspace_snapshot_commands_cut", space, {
|
|
1149
|
+
inFlight,
|
|
1150
|
+
waitedMs,
|
|
1151
|
+
killed,
|
|
1152
|
+
effect: killed
|
|
1153
|
+
? "the run is over but its commands were still running after the wait — their processes were killed (the sandbox is deleted right after) so the run-end snapshot reads a still tree; the workspace is saved as it stood"
|
|
1154
|
+
: "the run is over but its commands were still running after the wait and the pod's processes could not be confirmed stopped — the workspace is NOT saved (it may still be changing); `latest` keeps naming the previous snapshot",
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
return killed
|
|
1158
|
+
? { ok: true, waitedMs }
|
|
1159
|
+
: { ok: false, error: new RemoteExecutionError("command_in_flight", "the pod's processes could not be confirmed stopped when the run ended — not saving a workspace that may still be changing") };
|
|
1160
|
+
}
|
|
1161
|
+
async killPodProcesses() {
|
|
1162
|
+
if (!this.podName)
|
|
1163
|
+
return false;
|
|
1164
|
+
const r = await this.runExec(buildKillAllScript(), { timeoutMs: this.cfg.rpcTimeoutMs + KILL_ALL_RECHECK_MS });
|
|
1165
|
+
return r.ok && r.value.exitCode === 0;
|
|
1166
|
+
}
|
|
1035
1167
|
async persistWorkspace(space) {
|
|
1036
1168
|
const failed = (stage, error) => {
|
|
1037
1169
|
this.spaceLine("workspace_snapshot_failed", space, { stage, code: error.code, error: error.message });
|
|
@@ -1052,7 +1184,10 @@ export class RemoteK8sExecutionEnv {
|
|
|
1052
1184
|
try {
|
|
1053
1185
|
let snap;
|
|
1054
1186
|
try {
|
|
1055
|
-
|
|
1187
|
+
const quiet = await this.quiesce(space);
|
|
1188
|
+
if (!quiet.ok)
|
|
1189
|
+
return failed("upload", quiet.error);
|
|
1190
|
+
snap = await this.uploadSnapshot({ lane: "space", space }, Math.max(1, this.cfg.dataTimeoutMs - quiet.waitedMs));
|
|
1056
1191
|
}
|
|
1057
1192
|
catch (e) {
|
|
1058
1193
|
return failed("upload", new RemoteExecutionError("unknown", errorMessageOf(e)));
|
|
@@ -1142,7 +1277,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
1142
1277
|
clearTimeout(livenessTimer);
|
|
1143
1278
|
const idle = Date.now() - lastProgress;
|
|
1144
1279
|
if (idle >= liveness.ms) {
|
|
1145
|
-
|
|
1280
|
+
const cut = buildLivenessCut(liveness);
|
|
1281
|
+
finish({ ok: false, error: new ExecutionError(cut.code, cut.message, undefined, undefined, cut.noOutputSec) });
|
|
1146
1282
|
return;
|
|
1147
1283
|
}
|
|
1148
1284
|
livenessTimer = setTimeout(armLiveness, liveness.ms - idle);
|
|
@@ -1276,11 +1412,12 @@ export class RemoteK8sExecutionEnv {
|
|
|
1276
1412
|
if (!c.ok)
|
|
1277
1413
|
return c;
|
|
1278
1414
|
const detachSignal = options?.detachSignal;
|
|
1279
|
-
this.
|
|
1415
|
+
const inflight = this.beginExec();
|
|
1416
|
+
const abortSignal = options?.abortSignal ? AbortSignal.any([options.abortSignal, inflight.signal]) : inflight.signal;
|
|
1280
1417
|
try {
|
|
1281
1418
|
if (!detachSignal) {
|
|
1282
1419
|
return call.settle(await this.runExec(call.command, {
|
|
1283
|
-
signal:
|
|
1420
|
+
signal: abortSignal,
|
|
1284
1421
|
timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
|
|
1285
1422
|
boundOutput: true,
|
|
1286
1423
|
onStdout: options?.onStdout,
|
|
@@ -1292,7 +1429,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1292
1429
|
const dir = `${K8S_BG_DIR_ROOT}/exec-${adoptId}`;
|
|
1293
1430
|
const tee = { out: seedMemStream(""), err: seedMemStream("") };
|
|
1294
1431
|
return call.settle(await this.runExec(buildDetachCapableExec(dir, call.command), {
|
|
1295
|
-
signal:
|
|
1432
|
+
signal: abortSignal,
|
|
1296
1433
|
timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
|
|
1297
1434
|
boundOutput: true,
|
|
1298
1435
|
onStdout: options?.onStdout,
|
|
@@ -1316,7 +1453,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1316
1453
|
}));
|
|
1317
1454
|
}
|
|
1318
1455
|
finally {
|
|
1319
|
-
this.
|
|
1456
|
+
this.endExec(inflight);
|
|
1320
1457
|
}
|
|
1321
1458
|
}
|
|
1322
1459
|
async getHost(port) {
|
|
@@ -1337,7 +1474,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1337
1474
|
const c = await this.ensureConnected();
|
|
1338
1475
|
if (!c.ok)
|
|
1339
1476
|
throw new RemoteExecutionError("connect_failed", c.error.message, c.error);
|
|
1340
|
-
this.
|
|
1477
|
+
const ac = this.beginExec();
|
|
1341
1478
|
const queue = [];
|
|
1342
1479
|
let finished = false;
|
|
1343
1480
|
let failure;
|
|
@@ -1350,7 +1487,6 @@ export class RemoteK8sExecutionEnv {
|
|
|
1350
1487
|
};
|
|
1351
1488
|
const cap = options?.maxOutputBytes;
|
|
1352
1489
|
let bytes = 0;
|
|
1353
|
-
const ac = new AbortController();
|
|
1354
1490
|
const signal = options?.signal ? AbortSignal.any([options.signal, ac.signal]) : ac.signal;
|
|
1355
1491
|
const done = this.runExec(buildCwdEnvCommand(command, this.workingDirOf(options?.cwd), options?.env, "raw").command, {
|
|
1356
1492
|
signal,
|
|
@@ -1375,9 +1511,11 @@ export class RemoteK8sExecutionEnv {
|
|
|
1375
1511
|
? "transport_lost"
|
|
1376
1512
|
: r.error.code === "timeout"
|
|
1377
1513
|
? "timeout"
|
|
1378
|
-
: r.error.code === "
|
|
1379
|
-
? "
|
|
1380
|
-
: "
|
|
1514
|
+
: r.error.code === "liveness_no_output"
|
|
1515
|
+
? "liveness_no_output"
|
|
1516
|
+
: r.error.code === "spawn_error" || r.error.code === "shell_unavailable"
|
|
1517
|
+
? "unknown"
|
|
1518
|
+
: "connect_failed";
|
|
1381
1519
|
failure = new RemoteExecutionError(code, r.error.message, r.error);
|
|
1382
1520
|
}
|
|
1383
1521
|
finished = true;
|
|
@@ -1409,7 +1547,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1409
1547
|
finally {
|
|
1410
1548
|
ac.abort();
|
|
1411
1549
|
await done.catch(() => { });
|
|
1412
|
-
this.
|
|
1550
|
+
this.endExec(ac);
|
|
1413
1551
|
}
|
|
1414
1552
|
}
|
|
1415
1553
|
backgroundCapabilities;
|
|
@@ -1626,42 +1764,97 @@ export class RemoteK8sExecutionEnv {
|
|
|
1626
1764
|
return dir ? this.resolve(dir) : undefined;
|
|
1627
1765
|
}
|
|
1628
1766
|
}
|
|
1767
|
+
const SPACE_RECLAIM_CONCURRENCY = 4;
|
|
1768
|
+
function createGate(limit) {
|
|
1769
|
+
let active = 0;
|
|
1770
|
+
const waiting = [];
|
|
1771
|
+
const release = () => {
|
|
1772
|
+
const next = waiting.shift();
|
|
1773
|
+
if (next)
|
|
1774
|
+
next();
|
|
1775
|
+
else
|
|
1776
|
+
active--;
|
|
1777
|
+
};
|
|
1778
|
+
return async (task) => {
|
|
1779
|
+
if (active < limit)
|
|
1780
|
+
active++;
|
|
1781
|
+
else
|
|
1782
|
+
await new Promise((resolve) => waiting.push(resolve));
|
|
1783
|
+
try {
|
|
1784
|
+
return await task();
|
|
1785
|
+
}
|
|
1786
|
+
finally {
|
|
1787
|
+
release();
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function tallyFailure(classes, key, at, pos, sample) {
|
|
1792
|
+
const seen = classes.get(key);
|
|
1793
|
+
if (seen === undefined)
|
|
1794
|
+
classes.set(key, { count: 1, at, pos, sample });
|
|
1795
|
+
else {
|
|
1796
|
+
seen.count++;
|
|
1797
|
+
if (at < seen.at || (at === seen.at && pos < seen.pos)) {
|
|
1798
|
+
seen.at = at;
|
|
1799
|
+
seen.pos = pos;
|
|
1800
|
+
seen.sample = sample;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
function failureClassesInOrder(classes) {
|
|
1805
|
+
return [...classes.values()].sort((a, b) => a.at - b.at || a.pos - b.pos);
|
|
1806
|
+
}
|
|
1629
1807
|
export function createSpaceReclaimer(config, deps) {
|
|
1808
|
+
const gate = createGate(SPACE_RECLAIM_CONCURRENCY);
|
|
1630
1809
|
return {
|
|
1631
1810
|
async reclaim(spaces) {
|
|
1632
1811
|
const deadline = AbortSignal.timeout(dataTimeoutMsOf(config));
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
const
|
|
1639
|
-
if (
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
reason
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
error: redactIncarnations(r.detail),
|
|
1646
|
-
effect: `the ended space's cache volume stays — delete it by label ${WORKSPACE_CACHE_SPACE_LABEL}=<label> (the deletion it belongs to went ahead)`,
|
|
1647
|
-
});
|
|
1812
|
+
const volumeFailures = new Map();
|
|
1813
|
+
const objectFailures = new Map();
|
|
1814
|
+
const settled = await Promise.allSettled(spaces.map(({ sessionId, spaceKey }, at) => spaceKey === ""
|
|
1815
|
+
? Promise.resolve()
|
|
1816
|
+
: gate(async () => {
|
|
1817
|
+
const env = new RemoteK8sExecutionEnv({ ...config, sessionId }, deps);
|
|
1818
|
+
if (config.cache !== undefined) {
|
|
1819
|
+
const r = await env.reclaimSpaceCache(spaceKey, deadline).catch((e) => ({ ok: false, reason: "delete_failed", detail: errorMessageOf(e) }));
|
|
1820
|
+
if (!r.ok) {
|
|
1821
|
+
recordFailOpen("server.workspace-cache.reclaim-failed", `reason=${r.reason} label=${workspaceCacheClaimOf(spaceKey).label}`);
|
|
1822
|
+
tallyFailure(volumeFailures, r.reason, at, 0, { spaceKey, failure: r });
|
|
1823
|
+
}
|
|
1648
1824
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
config.logger?.warn("workspace_space_reclaim_failed", {
|
|
1655
|
-
space: incarnationLogPrefix(spaceKey),
|
|
1656
|
-
sessionId,
|
|
1657
|
-
lane: f.lane,
|
|
1658
|
-
stage: f.stage,
|
|
1659
|
-
error: redactIncarnations(f.detail),
|
|
1660
|
-
effect: `the ended space's ${f.lane === "space" ? "workspace snapshots" : "park snapshots"} may stay in the bucket (unreadable through the API; the bucket's lifecycle rule is the fallback) — the deletion it belongs to went ahead`,
|
|
1825
|
+
if (config.s3Snapshot !== undefined) {
|
|
1826
|
+
const failures = await env.reclaimSpaceSnapshots(spaceKey, deadline).catch((e) => [{ lane: "space", stage: "delete", detail: errorMessageOf(e) }]);
|
|
1827
|
+
failures.forEach((f, pos) => {
|
|
1828
|
+
recordFailOpen("server.workspace-space.reclaim-failed", `lane=${f.lane} stage=${f.stage}`);
|
|
1829
|
+
tallyFailure(objectFailures, `${f.lane}/${f.stage}`, at, pos, { sessionId, spaceKey, failure: f });
|
|
1661
1830
|
});
|
|
1662
1831
|
}
|
|
1663
|
-
}
|
|
1832
|
+
})));
|
|
1833
|
+
for (const { count, sample } of failureClassesInOrder(volumeFailures)) {
|
|
1834
|
+
config.logger?.warn("workspace_cache_reclaim_failed", {
|
|
1835
|
+
reason: sample.failure.reason,
|
|
1836
|
+
count,
|
|
1837
|
+
space: incarnationLogPrefix(sample.spaceKey),
|
|
1838
|
+
label: workspaceCacheClaimOf(sample.spaceKey).label,
|
|
1839
|
+
error: redactIncarnations(sample.failure.detail),
|
|
1840
|
+
effect: `the ended space's cache volume stays — delete it by label ${WORKSPACE_CACHE_SPACE_LABEL}=<label> (the deletion it belongs to went ahead)${count > 1 ? `; ${count} spaces of this call met the same failure, the line names the first` : ""}`,
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
for (const { count, sample } of failureClassesInOrder(objectFailures)) {
|
|
1844
|
+
const { sessionId, spaceKey, failure: f } = sample;
|
|
1845
|
+
config.logger?.warn("workspace_space_reclaim_failed", {
|
|
1846
|
+
space: incarnationLogPrefix(spaceKey),
|
|
1847
|
+
sessionId,
|
|
1848
|
+
lane: f.lane,
|
|
1849
|
+
stage: f.stage,
|
|
1850
|
+
count,
|
|
1851
|
+
error: redactIncarnations(f.detail),
|
|
1852
|
+
effect: `the ended space's ${f.lane === "space" ? "workspace snapshots" : "park snapshots"} may stay in the bucket (unreadable through the API; the bucket's lifecycle rule is the fallback) — the deletion it belongs to went ahead${count > 1 ? `; ${count} spaces of this call met the same failure, the line names the first` : ""}`,
|
|
1853
|
+
});
|
|
1664
1854
|
}
|
|
1855
|
+
const threw = settled.find((r) => r.status === "rejected");
|
|
1856
|
+
if (threw)
|
|
1857
|
+
throw threw.reason;
|
|
1665
1858
|
},
|
|
1666
1859
|
};
|
|
1667
1860
|
}
|