@sema-agent/server 7.149.0 → 7.151.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 +307 -9
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +92 -40
- package/deploy/sema-up/README.md +7 -1
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
- package/deploy/sema-up/chart/templates/server.yaml +5 -3
- package/deploy/sema-up/chart/values-full-single.yaml +3 -1
- package/deploy/sema-up/chart/values.yaml +8 -1
- package/deploy/sema-up/cluster-up.sh +1 -0
- package/deploy/sema-up/kube-up.sh +1 -1
- package/deploy/sema-up/lib/flagshape.sh +1 -1
- package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
- package/deploy/sema-up/lib/helm-values-kube.py +2 -0
- package/dist/adoption/import.js +3 -3
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +3 -3
- package/dist/approval-gate-currency.d.ts +2 -2
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +20 -0
- package/dist/auth-bridge.js +9 -0
- package/dist/background-session-reap.d.ts +2 -2
- package/dist/bake-runner/main.d.ts +4 -2
- package/dist/bind-host.d.ts +2 -3
- package/dist/bind-host.js +2 -4
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/engine-lease.d.ts +5 -0
- package/dist/boot/engine-lease.js +1 -0
- package/dist/boot/execution-env.d.ts +8 -14
- package/dist/boot/execution-env.js +54 -17
- package/dist/boot/execution-lane.js +32 -5
- package/dist/boot/late-binds.d.ts +8 -4
- package/dist/boot/late-binds.js +7 -1
- package/dist/boot/memory-consolidation.d.ts +3 -2
- package/dist/boot/memory-consolidation.js +6 -6
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/reapers.d.ts +5 -0
- package/dist/boot/reapers.js +12 -1
- package/dist/boot/resolve-spec.d.ts +7 -8
- package/dist/boot/resolve-spec.js +80 -35
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/runner-deps.d.ts +9 -2
- package/dist/boot/runner-deps.js +3 -3
- package/dist/boot/session-age-out.d.ts +36 -0
- package/dist/boot/session-age-out.js +101 -0
- package/dist/boot/session-faces.d.ts +5 -14
- package/dist/boot/session-faces.js +28 -24
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +5 -3
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +7 -4
- package/dist/boot/stage-06-runners.js +16 -8
- package/dist/boot/stage-07-capability-layer.d.ts +7 -4
- package/dist/boot/stage-07-capability-layer.js +3 -2
- package/dist/boot/stage-08-reapers.d.ts +7 -4
- package/dist/boot/stage-08-reapers.js +4 -2
- package/dist/boot/stage-09-leader.d.ts +11 -5
- package/dist/boot/stage-09-leader.js +12 -3
- package/dist/boot/stage-10-http-server.d.ts +44 -6
- package/dist/boot/stage-10-http-server.js +12 -10
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/boot/web-admin.js +3 -1
- package/dist/boot/workflow-orchestration.d.ts +7 -2
- package/dist/boot/workflow-orchestration.js +24 -7
- package/dist/bounded-rotation.d.ts +29 -0
- package/dist/bounded-rotation.js +24 -0
- package/dist/capability-facts.d.ts +84 -66
- package/dist/capability-facts.js +58 -41
- package/dist/center-credential.d.ts +25 -6
- package/dist/center-credential.js +25 -5
- package/dist/config-catalog.d.ts +26 -7
- package/dist/config-catalog.js +15 -12
- package/dist/config-invariants.d.ts +2 -1
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +22 -15
- package/dist/config.d.ts +51 -2
- package/dist/config.js +35 -24
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/deployment-shape.d.ts +9 -1
- package/dist/deployment-shape.js +2 -1
- package/dist/device-protocol.d.ts +5 -4
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +66 -29
- package/dist/device-ws-protocol.d.ts +79 -12
- package/dist/device-ws-protocol.js +64 -26
- package/dist/engine-identity.d.ts +23 -5
- package/dist/engine-identity.js +13 -0
- package/dist/entry-mode-clamp-notice.d.ts +2 -1
- package/dist/execution-env-refusal.d.ts +23 -0
- package/dist/execution-env-refusal.js +5 -0
- package/dist/execution-lane-caps.d.ts +3 -1
- package/dist/field-conflict.d.ts +3 -3
- package/dist/file-history-capture.d.ts +11 -4
- package/dist/file-history-capture.js +9 -6
- package/dist/file-history-disclosure.d.ts +7 -5
- package/dist/file-history-disclosure.js +7 -5
- package/dist/fleet/drain-entry.d.ts +4 -0
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/host-lsp-manager.d.ts +2 -2
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +19 -4
- package/dist/http/approval-sweeps.js +7 -6
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +20 -63
- package/dist/http/checkpoint-ctx.js +2 -24
- package/dist/http/claim-heartbeat.d.ts +4 -4
- package/dist/http/claim-heartbeat.js +3 -3
- package/dist/http/dispatch.d.ts +2 -1
- package/dist/http/dispatch.js +36 -29
- package/dist/http/durable-run-leg.js +17 -17
- package/dist/http/health-self-report.d.ts +9 -1
- package/dist/http/health-self-report.js +2 -0
- package/dist/http/owner-access.d.ts +264 -20
- package/dist/http/owner-access.js +215 -5
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.js +38 -1
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +273 -89
- package/dist/http/route-ctx.d.ts +41 -19
- package/dist/http/route-table.d.ts +1 -1
- package/dist/http/routes/a2a-serve.js +15 -14
- package/dist/http/routes/admin-memory-optout.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +12 -12
- package/dist/http/routes/background.d.ts +3 -3
- package/dist/http/routes/capabilities.js +10 -6
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/fleet.js +10 -8
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/projects.d.ts +4 -2
- package/dist/http/routes/projects.js +7 -6
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +125 -66
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.d.ts +4 -3
- package/dist/http/routes/session-sync.js +174 -83
- package/dist/http/routes/sessions-list.js +13 -7
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +26 -48
- package/dist/http/routes/tasks.js +38 -23
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +28 -30
- package/dist/http/server.js +7 -3
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +1 -2
- package/dist/http/submit-ctx.js +8 -6
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +19 -6
- package/dist/identity-source.d.ts +20 -3
- package/dist/identity-source.js +14 -0
- package/dist/index.d.ts +4 -0
- package/dist/lane-home-probe.d.ts +3 -7
- package/dist/lane-home-probe.js +0 -1
- package/dist/leader/endpoint.js +9 -8
- package/dist/leader/leader.d.ts +2 -1
- package/dist/leg-facts.d.ts +19 -5
- package/dist/leg-facts.js +3 -1
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-scope.d.ts +5 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/memory-sync-client.d.ts +30 -2
- package/dist/memory-sync-client.js +16 -9
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/err-text.d.ts +7 -0
- package/dist/observability/err-text.js +5 -0
- package/dist/observability/fail-open.d.ts +12 -8
- package/dist/observability/fail-open.js +12 -8
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
- package/dist/orchestration/workflow-notify-journal.js +12 -1
- package/dist/orchestration/workflow-park-decision.d.ts +70 -18
- package/dist/orchestration/workflow-park-decision.js +49 -10
- package/dist/orchestration/workflow-park-holder.d.ts +65 -0
- package/dist/orchestration/workflow-park-holder.js +98 -0
- package/dist/orchestration/workflow-record-fence.d.ts +36 -0
- package/dist/orchestration/workflow-record-fence.js +42 -0
- package/dist/owner-faces.d.ts +28 -4
- package/dist/owner-faces.js +2 -0
- package/dist/parked-decide.d.ts +104 -53
- package/dist/parked-decide.js +153 -117
- package/dist/peer-directory.d.ts +2 -2
- package/dist/peer-inbound-face.d.ts +2 -6
- package/dist/peer-inbound-face.js +0 -3
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/permission-mode-admission.d.ts +41 -5
- package/dist/permission-mode-admission.js +29 -3
- package/dist/plugins/adoption-log-sql.d.ts +3 -1
- package/dist/plugins/adoption-log-sql.js +7 -1
- package/dist/plugins/approval-ask-store-file.d.ts +10 -2
- package/dist/plugins/approval-ask-store-file.js +8 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
- package/dist/plugins/approval-ask-store-memory.js +28 -9
- package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
- package/dist/plugins/approval-ask-store-sql.js +34 -5
- package/dist/plugins/approval-exemption-store.js +3 -0
- package/dist/plugins/approval-nonce-store.js +2 -0
- package/dist/plugins/breaker-state-sql.js +2 -0
- package/dist/plugins/caching-session-store.d.ts +9 -15
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/device-store-sql.js +6 -0
- package/dist/plugins/error-origin.d.ts +18 -0
- package/dist/plugins/error-origin.js +10 -0
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/execution-placement-sql.js +6 -6
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/image-bake-store-sql.js +6 -0
- package/dist/plugins/image-index-sql.js +4 -0
- package/dist/plugins/landed-attendants.d.ts +212 -0
- package/dist/plugins/landed-attendants.js +161 -0
- package/dist/plugins/leader-run-store-sql.js +4 -0
- package/dist/plugins/local-checkpoint-store.d.ts +4 -1
- package/dist/plugins/local-checkpoint-store.js +3 -0
- package/dist/plugins/local-session-live-reference.d.ts +40 -0
- package/dist/plugins/local-session-live-reference.js +39 -0
- package/dist/plugins/local-session-store.d.ts +71 -33
- package/dist/plugins/local-session-store.js +129 -27
- package/dist/plugins/memory-engine-pg.js +4 -0
- package/dist/plugins/memory-engine-sql-apply.js +5 -2
- package/dist/plugins/memory-engine-tidb.js +4 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/memory-sync-store-pg.js +5 -0
- package/dist/plugins/memory-sync-store-tidb.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +80 -61
- package/dist/plugins/one-time-migrations.js +241 -104
- package/dist/plugins/org-delivery-quota-store.js +4 -1
- package/dist/plugins/outcome-ledger-sql.js +2 -0
- package/dist/plugins/permission-rule-store-sql.js +11 -4
- package/dist/plugins/pg-cost-quota.js +4 -0
- package/dist/plugins/pg-pool.js +28 -1
- package/dist/plugins/pg-rate-limiter.js +2 -0
- package/dist/plugins/posix-shell-fs.d.ts +6 -1
- package/dist/plugins/posix-shell-fs.js +8 -3
- package/dist/plugins/remote-env-adb.d.ts +19 -15
- package/dist/plugins/remote-env-adb.js +44 -17
- package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
- package/dist/plugins/remote-env-connect-seat.js +72 -0
- package/dist/plugins/remote-env-device.d.ts +22 -5
- package/dist/plugins/remote-env-device.js +114 -14
- package/dist/plugins/remote-env-e2b.d.ts +18 -7
- package/dist/plugins/remote-env-e2b.js +53 -40
- package/dist/plugins/remote-env-file-error.js +6 -2
- package/dist/plugins/remote-env-host.d.ts +18 -1
- package/dist/plugins/remote-env-host.js +48 -47
- package/dist/plugins/remote-env-k8s.d.ts +41 -39
- package/dist/plugins/remote-env-k8s.js +56 -32
- package/dist/plugins/remote-env-local-docker.d.ts +13 -8
- package/dist/plugins/remote-env-local-docker.js +37 -43
- package/dist/plugins/remote-env-ssh.d.ts +18 -12
- package/dist/plugins/remote-env-ssh.js +64 -62
- package/dist/plugins/remote-shell.d.ts +101 -2
- package/dist/plugins/remote-shell.js +221 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
- package/dist/plugins/resume-anchor-store-sql.js +12 -5
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +4 -0
- package/dist/plugins/retention-sql.d.ts +20 -2
- package/dist/plugins/retention-sql.js +11 -0
- package/dist/plugins/run-store-sql.d.ts +22 -3
- package/dist/plugins/run-store-sql.js +33 -9
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
- package/dist/plugins/schema-consistency.d.ts +9 -5
- package/dist/plugins/schema-consistency.js +48 -18
- package/dist/plugins/schema-drift-sentence.js +1 -1
- package/dist/plugins/send-file-ledger.js +3 -0
- package/dist/plugins/serving-requirement.d.ts +68 -0
- package/dist/plugins/serving-requirement.js +60 -0
- package/dist/plugins/session-birth.d.ts +6 -2
- package/dist/plugins/session-capture-record-store-sql.js +2 -0
- package/dist/plugins/session-ext-table.d.ts +14 -1
- package/dist/plugins/session-ext-table.js +5 -1
- package/dist/plugins/session-row-sql.d.ts +49 -2
- package/dist/plugins/session-row-sql.js +56 -13
- package/dist/plugins/session-store-sql.d.ts +34 -18
- package/dist/plugins/session-store-sql.js +98 -17
- package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
- package/dist/plugins/shared-memory-store-sql.js +6 -2
- package/dist/plugins/sql-driver.d.ts +2 -1
- package/dist/plugins/sql-driver.js +18 -3
- package/dist/plugins/sql-errors.d.ts +11 -6
- package/dist/plugins/sql-errors.js +5 -6
- package/dist/plugins/stop-then-start-sentence.d.ts +48 -3
- package/dist/plugins/stop-then-start-sentence.js +48 -0
- package/dist/plugins/stop-then-start.d.ts +109 -0
- package/dist/plugins/stop-then-start.js +104 -0
- package/dist/plugins/store-backend.d.ts +40 -13
- package/dist/plugins/store-backend.js +169 -27
- package/dist/plugins/store-boundary.d.ts +57 -0
- package/dist/plugins/store-boundary.js +139 -0
- package/dist/plugins/store-contracts.d.ts +29 -2
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
- package/dist/plugins/store-fault-sentence-m53.js +25 -0
- package/dist/plugins/store-fault.d.ts +26 -7
- package/dist/plugins/store-fault.js +18 -2
- package/dist/plugins/task-attachment-store.js +9 -0
- package/dist/plugins/task-list-store-sql.d.ts +2 -2
- package/dist/plugins/task-list-store-sql.js +10 -8
- package/dist/plugins/tidb-cost-quota.js +4 -0
- package/dist/plugins/tidb-pool.js +6 -1
- package/dist/plugins/tidb-rate-limiter.js +4 -0
- package/dist/plugins/user-input-ledger-sql.js +4 -0
- package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
- package/dist/plugins/workflow-run-store-sql.js +64 -4
- package/dist/plugins/workspace-fill.d.ts +4 -7
- package/dist/plugins/workspace-fill.js +2 -1
- package/dist/plugins/write-behind-counter.js +5 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +64 -0
- package/dist/project-context.js +66 -0
- package/dist/project-registry.d.ts +46 -0
- package/dist/project-registry.js +44 -0
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +4 -0
- package/dist/request-value-closure.js +10 -0
- package/dist/run-acceptance.d.ts +3 -2
- package/dist/run-acceptance.js +4 -6
- package/dist/run-cancel-card.d.ts +1 -1
- package/dist/run-cancel-card.js +2 -1
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +37 -8
- package/dist/runs.js +53 -12
- package/dist/runtime-governance.d.ts +5 -4
- package/dist/security.d.ts +31 -33
- package/dist/security.js +41 -7
- package/dist/server-secret-env.d.ts +12 -10
- package/dist/server-secret-env.js +24 -15
- package/dist/session-agent.d.ts +1 -10
- package/dist/session-agent.js +0 -6
- package/dist/session-bindings.d.ts +82 -0
- package/dist/session-bindings.js +18 -0
- package/dist/session-children-retire.d.ts +2 -2
- package/dist/session-children-retire.js +2 -6
- package/dist/session-deletion.d.ts +16 -1
- package/dist/session-error-wire.d.ts +3 -3
- package/dist/session-error-wire.js +3 -3
- package/dist/session-incarnation.d.ts +28 -4
- package/dist/session-incarnation.js +14 -2
- package/dist/session-kind.d.ts +57 -0
- package/dist/session-kind.js +22 -0
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-row.d.ts +21 -1
- package/dist/session-row.js +7 -1
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync-kernel.d.ts +24 -14
- package/dist/session-sync-kernel.js +3 -6
- package/dist/session-sync.d.ts +4 -98
- package/dist/session-sync.js +4 -74
- package/dist/session-titler.d.ts +10 -3
- package/dist/session-titler.js +19 -6
- package/dist/ssh-host-key.d.ts +5 -0
- package/dist/ssh-host-key.js +3 -0
- package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
- package/dist/stores-sql/checkpoint-store-sql.js +106 -18
- package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
- package/dist/stores-sql/file-history-store-sql.js +108 -70
- package/dist/stores-sql/pg-session-storage.d.ts +34 -7
- package/dist/stores-sql/pg-session-storage.js +37 -13
- package/dist/stores-sql/retention-store-sql.d.ts +2 -10
- package/dist/stores-sql/retention-store-sql.js +2 -10
- package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
- package/dist/stores-sql/session-policy-store-sql.js +61 -44
- package/dist/stores-sql/session-write-hooks.d.ts +11 -0
- package/dist/stores-sql/tidb-session-store.d.ts +58 -7
- package/dist/stores-sql/tidb-session-store.js +37 -13
- package/dist/submission-exit.d.ts +3 -2
- package/dist/submission-exit.js +9 -1
- package/dist/task-cwd.d.ts +6 -5
- package/dist/task-settings.d.ts +19 -5
- package/dist/task-settings.js +13 -7
- package/dist/terminal.d.ts +33 -2
- package/dist/terminal.js +139 -9
- package/dist/tool-approval.d.ts +147 -12
- package/dist/tool-approval.js +206 -109
- package/dist/trace/engine-notice-wire.d.ts +14 -0
- package/dist/trace/engine-notice-wire.js +18 -1
- package/dist/trace/project.d.ts +4 -3
- package/dist/trace/project.js +3 -2
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/self-minted-wire.d.ts +422 -26
- package/dist/trace/self-minted-wire.js +149 -23
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +120 -41
- package/package.json +3 -3
- package/wire-changes/server@7.150.0.json +335 -0
- package/wire-changes/server@7.151.0.json +762 -0
- package/dist/http/session-inherit-env.d.ts +0 -36
- package/dist/http/session-inherit-env.js +0 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { anchoredPath, joinedPath } from "./exec-path-law.js";
|
|
2
3
|
import { readFileSync } from "node:fs";
|
|
3
4
|
import https from "node:https";
|
|
4
5
|
import http from "node:http";
|
|
@@ -10,7 +11,7 @@ import { WORKSPACE_SNAPSHOT_BUCKET_DEFAULT } from "../object-store-buckets.js";
|
|
|
10
11
|
import { SNAPSHOT_BUCKET_MISSING, isNoSuchBucketAnswer } from "./s3-answer.js";
|
|
11
12
|
import { K8S_WORKSPACE_MOUNT_DEFAULT, SNAPSHOT_KEY_SEGMENT, WORKSPACE_SNAPSHOT_PREFIX_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, mintSnapshotId, snapshotIdHolds, snapshotKeyPrefixProblem, snapshotNameOwnedBy } from "../workspace-space.js";
|
|
12
13
|
import { spaceCommitAlive } from "../workspace-space-commit.js";
|
|
13
|
-
import { incarnationLogPrefix, redactIncarnations } from "../session-incarnation.js";
|
|
14
|
+
import { SESSION_INCARNATION_MISSING, incarnationLogPrefix, redactIncarnations } from "../session-incarnation.js";
|
|
14
15
|
import { WORKSPACE_CACHE_SPACE_LABEL, workspaceCacheClaimOf, workspaceCacheEnv } from "../workspace-cache.js";
|
|
15
16
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
16
17
|
import { PodDeleteRefused, reportPodDeleteFailed } from "./k8s-pod-delete-failure.js";
|
|
@@ -26,6 +27,7 @@ import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
|
26
27
|
import { randomUUID } from "node:crypto";
|
|
27
28
|
import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
|
|
28
29
|
import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
|
|
30
|
+
import { connectRefusal, createConnectSeat } from "./remote-env-connect-seat.js";
|
|
29
31
|
const PROVIDER = "k8s";
|
|
30
32
|
const K8S_BG_DIR_ROOT = "/tmp/.sema-bg";
|
|
31
33
|
const K8S_BG_MAX_CONCURRENT = numEnvOr("K8S_BG_MAX_CONCURRENT", 8, 1);
|
|
@@ -91,6 +93,10 @@ function spaceLaneOf(policy, occupant) {
|
|
|
91
93
|
function spaceReadFailed(what, detail, cause) {
|
|
92
94
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace space ${what} read failed${detail}`, cause instanceof Error ? cause : undefined) };
|
|
93
95
|
}
|
|
96
|
+
function isQuotaExceeded(json) {
|
|
97
|
+
const message = typeof json === "object" && json !== null ? json.message : undefined;
|
|
98
|
+
return typeof message === "string" && /\bexceeded quota\b/.test(message);
|
|
99
|
+
}
|
|
94
100
|
function restoreFailureIsTransient(cls) {
|
|
95
101
|
switch (cls) {
|
|
96
102
|
case "timeout":
|
|
@@ -221,7 +227,18 @@ export class RemoteK8sExecutionEnv {
|
|
|
221
227
|
idleWaiters = [];
|
|
222
228
|
connectInFlight;
|
|
223
229
|
resumeInFlight;
|
|
224
|
-
|
|
230
|
+
seat = createConnectSeat({
|
|
231
|
+
live: () => (this.podName !== undefined && this.connectInFlight === undefined && !this.destroyed ? this.workspaceHandle() : undefined),
|
|
232
|
+
inFlight: () => this.connectInFlight,
|
|
233
|
+
establish: (config) => {
|
|
234
|
+
const walk = this.establish(config).finally(() => {
|
|
235
|
+
if (this.connectInFlight === walk)
|
|
236
|
+
this.connectInFlight = undefined;
|
|
237
|
+
});
|
|
238
|
+
this.connectInFlight = walk;
|
|
239
|
+
return walk;
|
|
240
|
+
},
|
|
241
|
+
});
|
|
225
242
|
get homeDir() {
|
|
226
243
|
return this.homeSeat.home;
|
|
227
244
|
}
|
|
@@ -300,7 +317,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
300
317
|
return this.cfg.timeoutMs;
|
|
301
318
|
}
|
|
302
319
|
resolve(p) {
|
|
303
|
-
return
|
|
320
|
+
return anchoredPath(path.posix, this.cwd, p);
|
|
304
321
|
}
|
|
305
322
|
workspaceHandle() {
|
|
306
323
|
return { sandboxId: this.podName ?? "(unconnected)", provider: PROVIDER, mountPath: this.cwd, ...(this.handleSnapshotId ? { snapshotId: this.handleSnapshotId } : {}) };
|
|
@@ -385,18 +402,12 @@ export class RemoteK8sExecutionEnv {
|
|
|
385
402
|
});
|
|
386
403
|
}
|
|
387
404
|
connect(config) {
|
|
388
|
-
|
|
389
|
-
return this.connectInFlight;
|
|
390
|
-
const walk = this.establish(config).finally(() => {
|
|
391
|
-
if (this.connectInFlight === walk)
|
|
392
|
-
this.connectInFlight = undefined;
|
|
393
|
-
});
|
|
394
|
-
this.connectInFlight = walk;
|
|
395
|
-
return walk;
|
|
405
|
+
return this.seat.connect(config);
|
|
396
406
|
}
|
|
397
407
|
async establish(config) {
|
|
398
|
-
|
|
399
|
-
|
|
408
|
+
const held = this.seat.held;
|
|
409
|
+
if (held)
|
|
410
|
+
return held;
|
|
400
411
|
const signal = config?.abortSignal;
|
|
401
412
|
const lane = this.spaceLane;
|
|
402
413
|
switch (lane.state) {
|
|
@@ -433,7 +444,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
433
444
|
const filled = await fill.fill(this.fillChannel(), this.cwd, c.origin);
|
|
434
445
|
if (!filled.ok) {
|
|
435
446
|
await this.releasePod();
|
|
436
|
-
return
|
|
447
|
+
return this.seat.answer(filled);
|
|
437
448
|
}
|
|
438
449
|
}
|
|
439
450
|
if (this.destroyed) {
|
|
@@ -487,10 +498,6 @@ export class RemoteK8sExecutionEnv {
|
|
|
487
498
|
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)",
|
|
488
499
|
});
|
|
489
500
|
}
|
|
490
|
-
holdRefusal(error) {
|
|
491
|
-
this.connectRefusal = { ok: false, error };
|
|
492
|
-
return this.connectRefusal;
|
|
493
|
-
}
|
|
494
501
|
refusePod(pod, phase, reason, message) {
|
|
495
502
|
this.logger?.warn("sandbox_pod_refused", {
|
|
496
503
|
pod,
|
|
@@ -500,19 +507,24 @@ export class RemoteK8sExecutionEnv {
|
|
|
500
507
|
...(this.cfg.sessionId ? { sessionId: this.cfg.sessionId } : {}),
|
|
501
508
|
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",
|
|
502
509
|
});
|
|
503
|
-
return this.
|
|
510
|
+
return this.seat.answer(connectRefusal("pod_admission_refused", `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`));
|
|
504
511
|
}
|
|
505
512
|
refuseUnreadableOccupant(code) {
|
|
506
513
|
const ways = [...(this.spaceLane.state === "unreadable" ? ["WORKSPACE_PERSIST=off"] : []), ...(this.cacheLane.state === "unreadable" ? ["WORKSPACE_CACHE=off"] : [])].join(" and ");
|
|
514
|
+
const missing = code === SESSION_INCARNATION_MISSING;
|
|
507
515
|
if (!this.unreadableOccupantSaid) {
|
|
508
516
|
this.unreadableOccupantSaid = true;
|
|
509
517
|
this.logger?.warn("workspace_space_unreadable_occupant", {
|
|
510
518
|
sessionId: this.cfg.sessionId,
|
|
511
519
|
code,
|
|
512
|
-
effect:
|
|
520
|
+
effect: missing
|
|
521
|
+
? `the session row for this run carries no occupant (incarnation) token — this run is refused rather than started on a workspace that is neither restored nor saved; ${ways} runs without it`
|
|
522
|
+
: `the session store did not answer for this run's session occupant (incarnation) — this run is refused rather than started on a workspace that is neither restored nor saved; ${ways} runs without it`,
|
|
513
523
|
});
|
|
514
524
|
}
|
|
515
|
-
return
|
|
525
|
+
return missing
|
|
526
|
+
? this.seat.answer(connectRefusal("occupant_missing", `workspace space occupant read failed (${code}) — the session row carries no occupant token, so this run is not started on a workspace that could be neither restored nor saved; give the row its token or delete the session, or set ${ways}`))
|
|
527
|
+
: connectRefusal("occupant_unreadable", `workspace space occupant read failed (${code}) — refusing to start this run on a workspace that could be neither restored nor saved; retry the run once the session store answers, or set ${ways}`);
|
|
516
528
|
}
|
|
517
529
|
spaceLine(event, space, fields) {
|
|
518
530
|
this.snapshotLine(event, { space: incarnationLogPrefix(space.incarnation), ...fields });
|
|
@@ -523,8 +535,9 @@ export class RemoteK8sExecutionEnv {
|
|
|
523
535
|
provision(signal) {
|
|
524
536
|
const space = this.spaceLane;
|
|
525
537
|
const cache = this.cacheLane;
|
|
526
|
-
|
|
527
|
-
|
|
538
|
+
const held = this.seat.held;
|
|
539
|
+
if (held)
|
|
540
|
+
return Promise.resolve(held);
|
|
528
541
|
if (space.state === "unreadable")
|
|
529
542
|
return Promise.resolve(this.refuseUnreadableOccupant(space.code));
|
|
530
543
|
if (cache.state === "unreadable")
|
|
@@ -539,7 +552,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
539
552
|
const halt = signal ? AbortSignal.any([signal, refused.signal]) : refused.signal;
|
|
540
553
|
return withRetry(async () => {
|
|
541
554
|
const r = await once(signal);
|
|
542
|
-
if (!r.ok && this.
|
|
555
|
+
if (!r.ok && this.seat.held)
|
|
543
556
|
refused.abort();
|
|
544
557
|
return r;
|
|
545
558
|
}, policy, { signal: halt });
|
|
@@ -715,7 +728,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
715
728
|
const leaseName = broker !== undefined ? `${SANDBOX_LEASE_NAME_PREFIX}${randomUUID()}` : undefined;
|
|
716
729
|
const placeholder = broker !== undefined ? buildSandboxLeaseFile(broker.origins, placeholderLeaseEntries(broker.origins)) : undefined;
|
|
717
730
|
if (placeholder !== undefined && !placeholder.ok) {
|
|
718
|
-
return this.
|
|
731
|
+
return this.seat.answer(connectRefusal("credential_broker_refused", `the member-credential broker cannot be set up for this pod: ${placeholder.problem}`));
|
|
719
732
|
}
|
|
720
733
|
const layered = { ...(cache ? workspaceCacheEnv(cache.mount) : {}), ...(this.cfg.podEnv ?? {}) };
|
|
721
734
|
const envEntries = [
|
|
@@ -769,7 +782,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
769
782
|
applyPodSpecPatch(pod, this.cfg.podSpecPatch, { broker: true });
|
|
770
783
|
}
|
|
771
784
|
catch (e) {
|
|
772
|
-
return this.
|
|
785
|
+
return this.seat.answer(connectRefusal("credential_broker_refused", `${errorMessageOf(e)} — no pod is created (the member-credential broker never shares a pod with a patch that could reach it)`));
|
|
773
786
|
}
|
|
774
787
|
}
|
|
775
788
|
else if (this.cfg.podSpecPatch)
|
|
@@ -782,7 +795,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
782
795
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `pod create RPC failed: ${e instanceof Error ? e.message : String(e)}`, e instanceof Error ? e : undefined) };
|
|
783
796
|
}
|
|
784
797
|
if (created.status === 401 || created.status === 403) {
|
|
785
|
-
|
|
798
|
+
const denied = `pod create denied (${created.status}): ${JSON.stringify(created.json).slice(0, 200)}`;
|
|
799
|
+
return created.status === 403 && isQuotaExceeded(created.json) ? connectRefusal("pod_quota_exceeded", denied) : { ok: false, error: new RemoteExecutionError("auth_failed", denied) };
|
|
786
800
|
}
|
|
787
801
|
if (created.status >= 300) {
|
|
788
802
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `pod create failed (${created.status}): ${JSON.stringify(created.json).slice(0, 300)}`) };
|
|
@@ -875,8 +889,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
875
889
|
if (verdict !== undefined) {
|
|
876
890
|
await this.deletePod(name).catch(() => { });
|
|
877
891
|
this.podName = undefined;
|
|
878
|
-
|
|
879
|
-
return verdict.answered ? this.holdRefusal(error) : { ok: false, error };
|
|
892
|
+
return verdict.answered ? this.seat.answer(connectRefusal("home_unconfirmed", verdict.refusal)) : { ok: false, error: new RemoteExecutionError("connect_failed", verdict.refusal) };
|
|
880
893
|
}
|
|
881
894
|
}
|
|
882
895
|
if (cache?.fresh)
|
|
@@ -1128,7 +1141,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1128
1141
|
const detail = ` (${SNAPSHOT_BUCKET_MISSING}): the workspace snapshot bucket "${bucket}" (WORKSPACE_SNAPSHOT_BUCKET; default ${WORKSPACE_SNAPSHOT_BUCKET_DEFAULT}) does not exist in the object store — ` +
|
|
1129
1142
|
`refusing to start this run on a workspace that could be neither restored nor saved. Ways out: create the bucket, or set WORKSPACE_SNAPSHOT_BUCKET to one that exists, ` +
|
|
1130
1143
|
"or set WORKSPACE_PERSIST=off for a deployment that keeps no workspace between runs";
|
|
1131
|
-
return this.
|
|
1144
|
+
return this.seat.answer(connectRefusal("snapshot_bucket_missing", read === "pointer" ? `workspace space pointer read failed${detail}` : `workspace snapshot archive read failed${detail}`));
|
|
1132
1145
|
}
|
|
1133
1146
|
async readLatest(space, signal) {
|
|
1134
1147
|
const r = await this.readPointer(space, "latest", signal);
|
|
@@ -1390,7 +1403,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1390
1403
|
case "space": {
|
|
1391
1404
|
const tries = attempt === 1 ? "1 attempt" : `${attempt} attempts`;
|
|
1392
1405
|
this.spaceLine("workspace_restore_failed", target.space, { snapshotId, class: failure.cls, attempts: attempt, boundMs: this.cfg.dataTimeoutMs, error: failure.detail });
|
|
1393
|
-
return this.
|
|
1406
|
+
return this.seat.answer(connectRefusal("archive_unrecoverable", `${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), ` +
|
|
1394
1407
|
"so this run does not start on an empty /workspace. Ways out: raise WORKSPACE_SNAPSHOT_TIMEOUT_MS if the archive is large, " +
|
|
1395
1408
|
"or set WORKSPACE_PERSIST=off for one run (it starts from an empty /workspace and deletes no snapshot)", failure.cause));
|
|
1396
1409
|
}
|
|
@@ -2196,7 +2209,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
2196
2209
|
return ok(this.resolve(p));
|
|
2197
2210
|
}
|
|
2198
2211
|
async joinPath(parts) {
|
|
2199
|
-
return ok(path.posix
|
|
2212
|
+
return ok(joinedPath(path.posix, parts));
|
|
2200
2213
|
}
|
|
2201
2214
|
async readBinaryFile(p, abortSignal) {
|
|
2202
2215
|
const abs = this.resolve(p);
|
|
@@ -2262,6 +2275,14 @@ export class RemoteK8sExecutionEnv {
|
|
|
2262
2275
|
run: (cmd, abortSignal) => this.fsExec(cmd, abortSignal),
|
|
2263
2276
|
resolve: (p) => this.resolve(p),
|
|
2264
2277
|
tempDir: () => this.cwd,
|
|
2278
|
+
boundLane: {
|
|
2279
|
+
run: async (cmd, _cap, abortSignal) => {
|
|
2280
|
+
const r = await this.execData(cmd, abortSignal);
|
|
2281
|
+
return r.ok ? { ok: true, value: { stdout: Buffer.from(r.value.stdout, "utf8"), stderr: r.value.stderr, exitCode: r.value.exitCode } } : r;
|
|
2282
|
+
},
|
|
2283
|
+
payload: "base64",
|
|
2284
|
+
boundSec: this.cfg.dataTimeoutMs / 1000,
|
|
2285
|
+
},
|
|
2265
2286
|
}));
|
|
2266
2287
|
}
|
|
2267
2288
|
async fileInfo(p, abortSignal) {
|
|
@@ -2276,6 +2297,9 @@ export class RemoteK8sExecutionEnv {
|
|
|
2276
2297
|
async canonicalChain(p, abortSignal) {
|
|
2277
2298
|
return this.posixFs.canonicalChain(p, abortSignal);
|
|
2278
2299
|
}
|
|
2300
|
+
async openBound(p, abortSignal) {
|
|
2301
|
+
return this.posixFs.openBound(p, abortSignal);
|
|
2302
|
+
}
|
|
2279
2303
|
async canonicalPath(p, abortSignal) {
|
|
2280
2304
|
return this.posixFs.canonicalPath(p, abortSignal);
|
|
2281
2305
|
}
|
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { type LaneHomeConfig } from "../lane-home-probe.js";
|
|
3
3
|
import { type LaneLogger } from "./remote-shell.js";
|
|
4
4
|
import { type CustodyMember } from "./workspace-custody.js";
|
|
5
|
-
import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory, type ExecutionEnvCapabilities } from "@sema-agent/core";
|
|
5
|
+
import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type BoundFileObject, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory, type ExecutionEnvCapabilities } from "@sema-agent/core";
|
|
6
6
|
import { type SandboxReadFloorKnob } from "../execution-lane-caps.js";
|
|
7
7
|
import { type SandboxReadFloor } from "./sandbox-read-floor.js";
|
|
8
8
|
import type { WorkspaceFill, WorkspaceFillOf } from "./workspace-fill.js";
|
|
@@ -140,9 +140,12 @@ export declare class RemoteLocalDockerExecutionEnv implements CustodyMember<Loca
|
|
|
140
140
|
private destroyed;
|
|
141
141
|
/** In-flight connect, memoized so concurrent lazy first-use starts exactly one container (no double-run). */
|
|
142
142
|
private connecting?;
|
|
143
|
-
/**
|
|
144
|
-
*
|
|
145
|
-
|
|
143
|
+
/**
|
|
144
|
+
* 建连座(`remote-env-connect-seat.ts`):`connect` 的唯一次序(记住的拒 ⇒ 原样再答 · 起容器在飞 ⇒ 等那一次 · 已连 ⇒ 当下的把手 · 否则起或附着一只),并持有这只环境记住的拒 ——
|
|
145
|
+
* 记一次、此后每次连接原样回它(同一镜像同一答案:不再起容器、不再问)。两件立定的事实写它:家目录(环境答了而与声明不一致;`env.home_unconfirmed`)
|
|
146
|
+
* 与恢复点的填充(换一只容器也是同一个答的拒:种子不是项目包、摘要不符、镜像没有 `tar`、镜像预置撞名 —— `FillOutcome.standing`;`env.sandbox_refused`)。
|
|
147
|
+
*/
|
|
148
|
+
private readonly seat;
|
|
146
149
|
/** 家目录的座(声明在场 ⇒ 开环境那一拍对证 · 否则开容器那一拍实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。与其余读得出家目录的车道同一只判官、同一句拒。 */
|
|
147
150
|
private readonly homeSeat;
|
|
148
151
|
/** 本环境的生命期信号:{@link destroy} 先断它 —— 在帽前等空位的那一次起动立刻收场(不拖满预算)。 */
|
|
@@ -171,7 +174,7 @@ export declare class RemoteLocalDockerExecutionEnv implements CustodyMember<Loca
|
|
|
171
174
|
ok: false;
|
|
172
175
|
error: RemoteExecutionError;
|
|
173
176
|
}>;
|
|
174
|
-
/** 一只在飞的连接(并发的首用调用方 / 附着都 join 它 —— 只起一只容器、只附着一次)
|
|
177
|
+
/** 一只在飞的连接(并发的首用调用方 / 附着都 join 它 —— 只起一只容器、只附着一次);落定即清(按身份):连上 = `containerId`,失败 ⇒ 下一次重来。 */
|
|
175
178
|
private connectOnce;
|
|
176
179
|
/**
|
|
177
180
|
* 先问 daemon 这只身份的容器({@link sightContainer}),再按它的样子(状态闭集,穷举):
|
|
@@ -214,8 +217,8 @@ export declare class RemoteLocalDockerExecutionEnv implements CustodyMember<Loca
|
|
|
214
217
|
private fillChannel;
|
|
215
218
|
/**
|
|
216
219
|
* 家目录:座要问({@link LaneHomeSeat.asks}:声明在场 ⇒ 对证;没声明 ⇒ 实测)⇒ 在本容器里问一次 `$HOME`(与命令同一条 `docker exec` 通道、同一份 `-e` env),读数交座。
|
|
217
|
-
* 声明在场:环境答了而不一致 / 不是绝对路径 ⇒
|
|
218
|
-
*
|
|
220
|
+
* 声明在场:环境答了而不一致 / 不是绝对路径 ⇒ 立定的拒(`env.home_unconfirmed`,建连座记住它:同一镜像同一答案,此后每次连接原样回它,不再起容器、不再问);
|
|
221
|
+
* 没答上(exec 失败 / 超时 / 非零退出)⇒ 这一次同样不开(`connect_failed`),但不记(与任何一次起容器失败同:下一次连接重问)。没声明:座收下绝对路径;答不上 / 不是绝对路径 ⇒ 座缺席 + 一行 warn,环境照开(没声明就没有声明可拒)。
|
|
219
222
|
* 调用方中止 ⇒ `aborted`、不记、不交座。容器由调用方收。
|
|
220
223
|
*/
|
|
221
224
|
private homeVerdict;
|
|
@@ -306,7 +309,7 @@ export declare class RemoteLocalDockerExecutionEnv implements CustodyMember<Loca
|
|
|
306
309
|
private get posixFs();
|
|
307
310
|
/** 文件面的一次容器内 shell 往返(基座、暂存写的准备 / 上传 / 提交 / 清理共用)。承 fsReady 契约(其余三腿走公有 exec、守卫天然在;本腿直用私有
|
|
308
311
|
* docker,必须自带):destroyed 守卫 + 懒连接,fail closed 不抛 —— 消息保留 "destroyed" 字样(测试钉的就是它)。`input` 在场 ⇒ 同一只 argv 多一个
|
|
309
|
-
* `-i`、载荷走 stdin({@link pipeIn})
|
|
312
|
+
* `-i`、载荷走 stdin({@link pipeIn});身份与工作目录与不带载荷的往返逐字相同。`binary` ⇒ stdout 逐字节收(`stdoutBytes`,绑定读的载荷;与 readBinaryFile 同一收集器)。 */
|
|
310
313
|
private fsExec;
|
|
311
314
|
fileInfo(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;
|
|
312
315
|
listDir(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;
|
|
@@ -315,6 +318,8 @@ export declare class RemoteLocalDockerExecutionEnv implements CustodyMember<Loca
|
|
|
315
318
|
* 循环(上限 64、只锚不折、遇环即止),脚本与读法的单一属主在 `remote-shell.ts`,四条 shell 腿共用同一份。
|
|
316
319
|
* 缺席读法用不上(本腿在场);`ok:false` 由 core 读作「这一次没有跳」,永不是拒绝。 */
|
|
317
320
|
canonicalChain(p: string, abortSignal?: AbortSignal): Promise<Result<SymlinkChain, FileError>>;
|
|
321
|
+
/** 绑定读(逐答重绑,Linux 读法;脚本与读法的单一属主在 `remote-shell.ts`,四条 shell 腿共用同一份)。 */
|
|
322
|
+
openBound(p: string, abortSignal?: AbortSignal): Promise<Result<BoundFileObject, FileError>>;
|
|
318
323
|
canonicalPath(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
319
324
|
exists(p: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
|
|
320
325
|
createDir(p: string, options?: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { anchoredPath, joinedPath } from "./exec-path-law.js";
|
|
2
3
|
import { spawn } from "node:child_process";
|
|
3
4
|
import { hostSubprocessEnv } from "../server-secret-env.js";
|
|
4
5
|
import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
|
|
@@ -12,6 +13,7 @@ import { FileError, ExecutionError, RemoteExecutionError, RollingTailBuffer, mar
|
|
|
12
13
|
import { capabilitiesFor } from "../execution-lane-caps.js";
|
|
13
14
|
import { ADAPTER_START, BOOTSTRAP_START, createSandboxReadFloor, dockerIdentityFlags, engineStart, floorArgv, } from "./sandbox-read-floor.js";
|
|
14
15
|
import { fileErrorFromExec, classifyFsStderr } from "./remote-env-file-error.js";
|
|
16
|
+
import { connectRefusal, createConnectSeat, withdrawOnAbort } from "./remote-env-connect-seat.js";
|
|
15
17
|
import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
16
18
|
const PROVIDER = "local-docker";
|
|
17
19
|
const DEFAULT_MOUNT_PATH = "/workspace";
|
|
@@ -48,7 +50,11 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
48
50
|
handle;
|
|
49
51
|
destroyed = false;
|
|
50
52
|
connecting;
|
|
51
|
-
|
|
53
|
+
seat = createConnectSeat({
|
|
54
|
+
live: () => (this.containerId !== undefined ? this.workspaceHandle() : undefined),
|
|
55
|
+
inFlight: () => this.connecting,
|
|
56
|
+
establish: (config) => this.connectOnce(config, "start-or-attach"),
|
|
57
|
+
});
|
|
52
58
|
homeSeat;
|
|
53
59
|
lifecycle = new AbortController();
|
|
54
60
|
capacityDeadline;
|
|
@@ -89,7 +95,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
89
95
|
return this.teardownSettled && this.leftover === undefined;
|
|
90
96
|
}
|
|
91
97
|
resolve(p) {
|
|
92
|
-
return
|
|
98
|
+
return anchoredPath(path.posix, this.cwd, p);
|
|
93
99
|
}
|
|
94
100
|
workspaceHandle() {
|
|
95
101
|
return this.handle ?? { sandboxId: "(unconnected)", provider: PROVIDER, mountPath: this.cfg.mountPath };
|
|
@@ -99,20 +105,15 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
99
105
|
return unsupported("connect(snapshotId)");
|
|
100
106
|
if (this.destroyed)
|
|
101
107
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
|
|
102
|
-
|
|
103
|
-
return { ok: false, error: this.connectRefusal };
|
|
104
|
-
if (config?.abortSignal?.aborted)
|
|
105
|
-
return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted") };
|
|
106
|
-
if (this.containerId)
|
|
107
|
-
return ok(this.workspaceHandle());
|
|
108
|
-
return this.connectOnce(config, "start-or-attach");
|
|
108
|
+
return this.seat.connect(config);
|
|
109
109
|
}
|
|
110
110
|
async connectOnce(config, mode) {
|
|
111
111
|
if (this.connecting)
|
|
112
112
|
return this.connecting;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
const mine = this.doConnect(config, mode);
|
|
114
|
+
this.connecting = mine;
|
|
115
|
+
const r = await mine;
|
|
116
|
+
if (this.connecting === mine)
|
|
116
117
|
this.connecting = undefined;
|
|
117
118
|
return r;
|
|
118
119
|
}
|
|
@@ -229,11 +230,8 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
229
230
|
if (this.destroyed)
|
|
230
231
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
|
|
231
232
|
const filled = await this.fillWorkspace("restored");
|
|
232
|
-
if (!filled.ok)
|
|
233
|
-
|
|
234
|
-
this.connectRefusal = filled.error;
|
|
235
|
-
return { ok: false, error: filled.error };
|
|
236
|
-
}
|
|
233
|
+
if (!filled.ok)
|
|
234
|
+
return this.seat.answer(filled);
|
|
237
235
|
if (this.destroyed)
|
|
238
236
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
|
|
239
237
|
this.containerId = id;
|
|
@@ -298,9 +296,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
298
296
|
const filled = await this.fillWorkspace("minted");
|
|
299
297
|
if (!filled.ok || this.destroyed) {
|
|
300
298
|
await this.removeInstance(id);
|
|
301
|
-
|
|
302
|
-
this.connectRefusal = filled.error;
|
|
303
|
-
return { ok: false, error: filled.ok ? new RemoteExecutionError("connect_failed", "execution env destroyed during connect") : filled.error };
|
|
299
|
+
return filled.ok ? { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") } : this.seat.answer(filled);
|
|
304
300
|
}
|
|
305
301
|
this.containerId = id;
|
|
306
302
|
this.handle = { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
|
|
@@ -355,10 +351,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
355
351
|
const verdict = this.homeSeat.settle(probe.ok && probe.value.exitCode === 0 ? { ok: true, stdout: probe.value.stdout } : { ok: false, reason: probe.ok ? `exit ${probe.value.exitCode}` : probe.error.message });
|
|
356
352
|
if (verdict === undefined)
|
|
357
353
|
return undefined;
|
|
358
|
-
|
|
359
|
-
if (verdict.answered)
|
|
360
|
-
this.connectRefusal = error;
|
|
361
|
-
return error;
|
|
354
|
+
return verdict.answered ? this.seat.answer(connectRefusal("home_unconfirmed", verdict.refusal)).error : new RemoteExecutionError("connect_failed", verdict.refusal);
|
|
362
355
|
}
|
|
363
356
|
async suspendVM(_options) {
|
|
364
357
|
return unsupported("suspendVM");
|
|
@@ -372,8 +365,9 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
372
365
|
async reconnect(sessionToken) {
|
|
373
366
|
if (this.destroyed)
|
|
374
367
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
|
|
375
|
-
|
|
376
|
-
|
|
368
|
+
const held = this.seat.held;
|
|
369
|
+
if (held)
|
|
370
|
+
return held;
|
|
377
371
|
if (sessionToken && sessionToken !== this.containerName) {
|
|
378
372
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `reconnect token '${String(sessionToken)}' does not match this env's container '${this.containerName}'`) };
|
|
379
373
|
}
|
|
@@ -618,7 +612,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
618
612
|
return ok(this.resolve(p));
|
|
619
613
|
}
|
|
620
614
|
async joinPath(parts) {
|
|
621
|
-
return ok(path.posix
|
|
615
|
+
return ok(joinedPath(path.posix, parts));
|
|
622
616
|
}
|
|
623
617
|
async readBinaryFile(p, abortSignal) {
|
|
624
618
|
const ready = await this.fsReady(p, abortSignal);
|
|
@@ -665,7 +659,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
665
659
|
return appendFileStaged(this.stagedLane(content), abs, abortSignal, readyUpload((dest, signal) => this.pipeIn(content, dest, signal, ">>")));
|
|
666
660
|
}
|
|
667
661
|
async pipeIn(content, dest, abortSignal, redirect = ">") {
|
|
668
|
-
const r = await this.fsExec(`cat ${redirect} ${shellQuote(dest)}`, abortSignal, typeof content === "string" ? Buffer.from(content, "utf8") : content);
|
|
662
|
+
const r = await this.fsExec(`cat ${redirect} ${shellQuote(dest)}`, abortSignal, { input: typeof content === "string" ? Buffer.from(content, "utf8") : content });
|
|
669
663
|
if (!r.ok)
|
|
670
664
|
return { ok: false, error: fileErrorFromExec(r.error, dest) };
|
|
671
665
|
if (r.value.exitCode !== 0)
|
|
@@ -678,9 +672,18 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
678
672
|
run: (cmd, abortSignal) => this.fsExec(cmd, abortSignal),
|
|
679
673
|
resolve: (p) => this.resolve(p),
|
|
680
674
|
tempDir: () => this.cwd,
|
|
675
|
+
boundLane: {
|
|
676
|
+
run: async (cmd, _cap, abortSignal) => {
|
|
677
|
+
const r = await this.fsExec(cmd, abortSignal, { binary: true });
|
|
678
|
+
return r.ok ? { ok: true, value: { stdout: r.value.stdoutBytes ?? new Uint8Array(), stderr: r.value.stderr, exitCode: r.value.exitCode } } : r;
|
|
679
|
+
},
|
|
680
|
+
payload: "raw",
|
|
681
|
+
boundSec: this.cfg.commandTimeoutMs / 1000,
|
|
682
|
+
},
|
|
681
683
|
}));
|
|
682
684
|
}
|
|
683
|
-
async fsExec(cmd, abortSignal,
|
|
685
|
+
async fsExec(cmd, abortSignal, o = {}) {
|
|
686
|
+
const input = o.input;
|
|
684
687
|
if (this.destroyed)
|
|
685
688
|
return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
|
|
686
689
|
if (!this.containerId) {
|
|
@@ -693,7 +696,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
693
696
|
const argv = this.startArgv(ADAPTER_START, cmd, { stdin: input !== undefined });
|
|
694
697
|
if (!argv.ok)
|
|
695
698
|
return argv;
|
|
696
|
-
return this.docker(argv.value, { signal: abortSignal, timeoutMs: this.cfg.commandTimeoutMs, ...(input !== undefined ? { input } : {}) });
|
|
699
|
+
return this.docker(argv.value, { signal: abortSignal, timeoutMs: this.cfg.commandTimeoutMs, ...(input !== undefined ? { input } : {}), ...(o.binary === true ? { binary: true } : {}) });
|
|
697
700
|
}
|
|
698
701
|
async fileInfo(p, abortSignal) {
|
|
699
702
|
return this.posixFs.statFileInfo(p, abortSignal);
|
|
@@ -707,6 +710,9 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
707
710
|
async canonicalChain(p, abortSignal) {
|
|
708
711
|
return this.posixFs.canonicalChain(p, abortSignal);
|
|
709
712
|
}
|
|
713
|
+
async openBound(p, abortSignal) {
|
|
714
|
+
return this.posixFs.openBound(p, abortSignal);
|
|
715
|
+
}
|
|
710
716
|
async canonicalPath(p, abortSignal) {
|
|
711
717
|
return this.posixFs.canonicalPath(p, abortSignal);
|
|
712
718
|
}
|
|
@@ -881,19 +887,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
881
887
|
});
|
|
882
888
|
}
|
|
883
889
|
joinConnect(signal) {
|
|
884
|
-
|
|
885
|
-
if (signal === undefined)
|
|
886
|
-
return shared;
|
|
887
|
-
if (signal.aborted)
|
|
888
|
-
return Promise.resolve({ ok: false, error: new RemoteExecutionError("aborted", "aborted while connecting") });
|
|
889
|
-
return new Promise((resolve) => {
|
|
890
|
-
const onAbort = () => resolve({ ok: false, error: new RemoteExecutionError("aborted", "aborted while connecting") });
|
|
891
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
892
|
-
void shared.then((r) => {
|
|
893
|
-
signal.removeEventListener("abort", onAbort);
|
|
894
|
-
resolve(r);
|
|
895
|
-
});
|
|
896
|
-
});
|
|
890
|
+
return withdrawOnAbort(this.connect(), signal);
|
|
897
891
|
}
|
|
898
892
|
containerShell = "/bin/sh";
|
|
899
893
|
containerShellReady;
|
|
@@ -2,6 +2,7 @@ import { type SshHostKeyPolicy } from "../ssh-host-key.js";
|
|
|
2
2
|
import { type LaneLogger } from "./remote-shell.js";
|
|
3
3
|
import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory } from "@sema-agent/core";
|
|
4
4
|
import { type LaneHomeConfig } from "../lane-home-probe.js";
|
|
5
|
+
import { type ConnectResult } from "./remote-env-connect-seat.js";
|
|
5
6
|
type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
|
|
6
7
|
export interface SshEnvConfig extends LaneHomeConfig {
|
|
7
8
|
host: string;
|
|
@@ -40,13 +41,6 @@ export interface SshEnvConfig extends LaneHomeConfig {
|
|
|
40
41
|
/** 运维日志座(起服装配递本服务的 logger):暂存写「已有目标的权限位没带过去」那一行经它(缺席 ⇒ 暂存编排自建一只,见 `remote-shell.ts` `StagedWriteLane.logger`)。 */
|
|
41
42
|
logger?: LaneLogger;
|
|
42
43
|
}
|
|
43
|
-
type ConnectResult = {
|
|
44
|
-
ok: true;
|
|
45
|
-
value: WorkspaceHandle;
|
|
46
|
-
} | {
|
|
47
|
-
ok: false;
|
|
48
|
-
error: RemoteExecutionError;
|
|
49
|
-
};
|
|
50
44
|
/**
|
|
51
45
|
* Test seam ([ref] §9.3 fault-injection): inject a `connectOnce` that replays an error sequence, so the
|
|
52
46
|
* retry policy (retryable codes / maxAttempts / "first fail → second success") is deterministically testable
|
|
@@ -80,8 +74,12 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
80
74
|
private readonly homeSeat;
|
|
81
75
|
/** 家目录声明 × 实测的判定,按连接记(同一条连接只问一次;重连换了连接再问 —— 登录用户不变时答案相同,代价一次往返;没声明且已量到绝对路径之后不再问)。 */
|
|
82
76
|
private homeChecked?;
|
|
83
|
-
/**
|
|
84
|
-
|
|
77
|
+
/**
|
|
78
|
+
* 建连座(`remote-env-connect-seat.ts`):`connect` 的唯一次序(记住的拒 ⇒ 原样再答 · 同代际的连接在飞 ⇒ 等它,调用方的取消只退它自己的等待 · 已连 ⇒ 当下的把手 · 否则起一条),
|
|
79
|
+
* 并持有这只环境记住的拒 —— 两件立定的事实:目标机答了家目录而与声明不一致(`env.home_unconfirmed`;同一目标机同一登录用户同一答案:不再问、不再发命令),
|
|
80
|
+
* 目标机出示的主机密钥过不了部署钉的判(`env.host_key_rejected`;同一配置同一主机每次同答:不再拨号)。此后每个面原样回它。
|
|
81
|
+
*/
|
|
82
|
+
private readonly seat;
|
|
85
83
|
/** core 7.49.0 [ref]:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在远端登录 shell 里(目标机的 profile / env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
|
|
86
84
|
* 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
|
|
87
85
|
readonly cdPath: "unknown";
|
|
@@ -100,8 +98,8 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
100
98
|
ok: false;
|
|
101
99
|
error: RemoteExecutionError;
|
|
102
100
|
}>;
|
|
103
|
-
/**
|
|
104
|
-
private
|
|
101
|
+
/** 一只建连的拒在 exec / 文件面上的那一句:拒句原文(cause = 那只拒,文件面据它答 `target_unavailable`)。 */
|
|
102
|
+
private refusalOnExecFace;
|
|
105
103
|
/** [ref] 四轮复审:三路(connect/reconnect/懒连接)唯一的连接发起点——同代际在飞记录 join,settle
|
|
106
104
|
* 身份清;代际抓拍一次贯穿全部重试轮,每轮起跳先查(陈旧/destroyed=不再开 socket,用不可重试的
|
|
107
105
|
* aborted 码即刻收敛 withRetry)。五轮复审:共享尝试零 caller 信号(取消面全在 per-caller race)。 */
|
|
@@ -162,8 +160,16 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
|
|
|
162
160
|
/** 文件面探针的 exec(车PL 验修 ③):只用绝对路径,**不进工作目录**、没有 cwd 守卫 —— 工作目录进不去时探针照样答真话(`permission_denied` /
|
|
163
161
|
* `not_found` 由命令自己的 stderr 归),core 的 `isDeadCwd` 不会把「进不去」读成「没了」。 */
|
|
164
162
|
private fsExec;
|
|
165
|
-
/** buffered exec
|
|
163
|
+
/** buffered exec(命令面 + 文件面探针):stdout 走 8 MiB 滚动尾(模型的 `yes` / `cat big` 撑不爆 worker),截掉的字节在结果里如实标出。 */
|
|
166
164
|
private execCall;
|
|
165
|
+
/**
|
|
166
|
+
* 绑定读一答的往返(`remote-shell.ts` 的脚本经它跑):与文件面探针同一起动(不进工作目录、没有 cwd 守卫)、数据界;stdout **逐字节**收、只留开头
|
|
167
|
+
* `cap + 1` 字节 —— 本答该有多长由读法定(`cap`),比它长的答由读法拒,从不解码一段尾巴(命令面那只 8 MiB 滚动尾在这里不许用)。
|
|
168
|
+
*/
|
|
169
|
+
private boundExec;
|
|
170
|
+
/** buffered exec 的唯一体:`call` 定了整串、stderr 的 started 行剥法与收口(`remote-shell.ts` `createCwdGuardedCall`);stdout 的收法由 `sink` 定
|
|
171
|
+
* (命令面 = 滚动尾;绑定读 = 封顶的逐字节头)。`sink.tail` = 切断时随错误带出的 stdout(绑定读不带)。 */
|
|
172
|
+
private runBuffered;
|
|
167
173
|
execStream(command: string, options?: ExecStreamOptions): AsyncIterable<OutputChunk>;
|
|
168
174
|
private sftp;
|
|
169
175
|
absolutePath(p: string): Promise<Result<string, FileError>>;
|