@sema-agent/server 7.150.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 +185 -0
- package/USAGE.md +53 -25
- 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 +2 -2
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/auth-bridge.d.ts +20 -0
- package/dist/auth-bridge.js +9 -0
- package/dist/background-session-reap.d.ts +1 -1
- 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/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/reapers.d.ts +5 -0
- package/dist/boot/reapers.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -3
- package/dist/boot/resolve-spec.js +31 -22
- 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 +9 -5
- package/dist/boot/stage-01-config.js +2 -0
- 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 +15 -6
- package/dist/boot/stage-07-capability-layer.d.ts +7 -4
- package/dist/boot/stage-07-capability-layer.js +2 -1
- 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 +11 -5
- package/dist/boot/stage-10-http-server.js +3 -6
- 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 +22 -13
- package/dist/capability-facts.js +28 -18
- 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 +14 -11
- package/dist/config-invariants.d.ts +2 -1
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +10 -4
- package/dist/config.d.ts +51 -2
- package/dist/config.js +35 -24
- 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.js +3 -2
- package/dist/device-ws-protocol.d.ts +63 -10
- package/dist/device-ws-protocol.js +56 -25
- package/dist/engine-identity.d.ts +19 -7
- package/dist/engine-identity.js +10 -2
- 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 +1 -1
- 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/hooks/hook-runner.js +2 -2
- package/dist/host-lsp-manager.d.ts +2 -2
- package/dist/http/admission.js +17 -2
- package/dist/http/approval-sweeps.js +5 -5
- package/dist/http/checkpoint-ctx.d.ts +2 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/dispatch.d.ts +2 -1
- package/dist/http/dispatch.js +34 -27
- package/dist/http/durable-run-leg.js +16 -16
- 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 +31 -11
- package/dist/http/owner-access.js +37 -6
- package/dist/http/resume-ctx-read.js +25 -3
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +259 -79
- package/dist/http/route-ctx.d.ts +18 -8
- package/dist/http/route-table.d.ts +1 -1
- 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.js +4 -3
- package/dist/http/routes/background.d.ts +3 -3
- package/dist/http/routes/capabilities.js +8 -5
- package/dist/http/routes/fleet.js +10 -8
- package/dist/http/routes/projects.d.ts +3 -2
- package/dist/http/routes/projects.js +4 -5
- package/dist/http/routes/runs.js +102 -36
- package/dist/http/routes/session-sync.d.ts +4 -3
- package/dist/http/routes/session-sync.js +172 -77
- package/dist/http/routes/sessions-list.js +13 -7
- package/dist/http/routes/sessions.js +10 -6
- package/dist/http/routes/tasks.js +30 -16
- package/dist/http/routes/trace-usage.js +2 -2
- package/dist/http/server.d.ts +21 -27
- package/dist/http/server.js +2 -2
- package/dist/http/submit-ctx.js +5 -4
- package/dist/http/wire-types.d.ts +14 -4
- 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/leader.d.ts +2 -1
- package/dist/leg-facts.d.ts +13 -5
- package/dist/leg-facts.js +2 -1
- package/dist/memory-scope.d.ts +5 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-sync-client.d.ts +30 -2
- package/dist/memory-sync-client.js +16 -9
- package/dist/observability/err-text.d.ts +7 -0
- package/dist/observability/err-text.js +5 -0
- package/dist/observability/fail-open.d.ts +7 -3
- package/dist/observability/fail-open.js +7 -3
- package/dist/observability/metrics.js +1 -0
- 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 +1 -1
- 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/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/execution-placement-sql.js +6 -6
- 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-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-sync-store-pg.js +5 -0
- package/dist/plugins/memory-sync-store-tidb.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +57 -63
- package/dist/plugins/one-time-migrations.js +162 -90
- 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 +24 -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 +4 -1
- package/dist/plugins/remote-env-adb.d.ts +19 -15
- package/dist/plugins/remote-env-adb.js +41 -15
- 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 +106 -8
- package/dist/plugins/remote-env-e2b.d.ts +15 -6
- package/dist/plugins/remote-env-e2b.js +42 -32
- package/dist/plugins/remote-env-file-error.js +6 -2
- package/dist/plugins/remote-env-host.d.ts +17 -0
- package/dist/plugins/remote-env-host.js +30 -38
- package/dist/plugins/remote-env-k8s.d.ts +41 -39
- package/dist/plugins/remote-env-k8s.js +53 -30
- package/dist/plugins/remote-env-local-docker.d.ts +13 -8
- package/dist/plugins/remote-env-local-docker.js +34 -41
- package/dist/plugins/remote-env-ssh.d.ts +18 -12
- package/dist/plugins/remote-env-ssh.js +60 -59
- package/dist/plugins/remote-shell.d.ts +98 -1
- package/dist/plugins/remote-shell.js +218 -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.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 +21 -2
- package/dist/plugins/run-store-sql.js +31 -7
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +1 -1
- package/dist/plugins/scenario-prefix-switch-m42.js +2 -2
- package/dist/plugins/schema-consistency.d.ts +4 -2
- package/dist/plugins/schema-consistency.js +19 -2
- package/dist/plugins/send-file-ledger.js +3 -0
- package/dist/plugins/serving-requirement.d.ts +26 -7
- package/dist/plugins/serving-requirement.js +11 -4
- 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 +10 -4
- package/dist/plugins/stop-then-start-sentence.js +14 -10
- 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 +17 -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 +2 -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/project-context.d.ts +0 -2
- package/dist/project-context.js +4 -8
- package/dist/project-registry.d.ts +46 -0
- package/dist/project-registry.js +44 -0
- 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-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 +30 -32
- 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-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 +10 -0
- package/dist/session-incarnation.js +10 -1
- package/dist/session-kind.d.ts +57 -0
- package/dist/session-kind.js +22 -0
- package/dist/session-row.d.ts +21 -1
- package/dist/session-row.js +7 -1
- 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 +1 -71
- 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/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 +1 -9
- 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 +2 -2
- package/dist/task-settings.d.ts +6 -5
- package/dist/terminal.d.ts +33 -2
- package/dist/terminal.js +139 -9
- package/dist/tool-approval.d.ts +140 -6
- package/dist/tool-approval.js +165 -72
- package/dist/trace/self-minted-wire.d.ts +290 -27
- package/dist/trace/self-minted-wire.js +101 -13
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +78 -27
- package/package.json +2 -2
- 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
|
@@ -2,11 +2,12 @@ import { type SpaceReclaimer, type WorkspaceSpacePolicy, type CacheReclaimReason
|
|
|
2
2
|
import { type SpaceCommitFence } from "../workspace-space-commit.js";
|
|
3
3
|
import { type CacheMissMemory, type WorkspaceCacheCapability } from "../workspace-cache.js";
|
|
4
4
|
import { type SandboxEgressGap, type SandboxIpv6 } from "../sandbox-egress-policy.js";
|
|
5
|
-
import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type ExecResult, 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 BackgroundShellCapability, type BackgroundShellId, type BackgroundShellError, type BackgroundSpawnOptions, type BackgroundPoll, type ExecutionEnvCapabilities } from "@sema-agent/core";
|
|
5
|
+
import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type ExecResult, 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 BackgroundShellCapability, type BackgroundShellId, type BackgroundShellError, type BackgroundSpawnOptions, type BackgroundPoll, 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 { PodSpecPatch } from "./k8s-exec-protocol.js";
|
|
9
9
|
import { type LaneHomeConfig } from "../lane-home-probe.js";
|
|
10
|
+
import { type ConnectResult } from "./remote-env-connect-seat.js";
|
|
10
11
|
type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
|
|
11
12
|
export { buildBgRunnerScript, buildBgLauncherScript, buildBgPollScript, buildBgKillScript, buildBgDisposeScript, buildDetachCapableExec, buildKillAllScript, parseBgPollOutput, type ParsedBgPoll, } from "./k8s-bg-scripts.js";
|
|
12
13
|
export interface K8sEnvConfig extends LaneHomeConfig {
|
|
@@ -175,13 +176,6 @@ export { type PodSpecPatch, applyPodSpecPatch } from "./k8s-exec-protocol.js";
|
|
|
175
176
|
import { type SandboxPodApi, type SandboxPodListing } from "./k8s-pod-reaper.js";
|
|
176
177
|
import { type SandboxLeaseEntryInput } from "../sandbox-git-lease.js";
|
|
177
178
|
import type { WorkspaceFill } from "./workspace-fill.js";
|
|
178
|
-
type ConnectResult = {
|
|
179
|
-
ok: true;
|
|
180
|
-
value: WorkspaceHandle;
|
|
181
|
-
} | {
|
|
182
|
-
ok: false;
|
|
183
|
-
error: RemoteExecutionError;
|
|
184
|
-
};
|
|
185
179
|
/** Minimal exec-socket surface (what we use of `ws`) so tests can inject a fake. */
|
|
186
180
|
export interface ExecSocket {
|
|
187
181
|
on(event: "open" | "message" | "close" | "error", listener: (...args: unknown[]) => void): void;
|
|
@@ -325,11 +319,15 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
325
319
|
private connectInFlight?;
|
|
326
320
|
/** The resume among them, with the snapshot it restores: a second `resumeVM` of the same snapshot shares it (one restore, one pod). */
|
|
327
321
|
private resumeInFlight?;
|
|
328
|
-
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
|
|
322
|
+
/**
|
|
323
|
+
* This env's connect seat (`remote-env-connect-seat.ts`): the one order of `connect` — a refusal the env holds ⇒ answered as it stands · a walk in flight
|
|
324
|
+
* ({@link connectInFlight}) ⇒ its answer · a pod already holding the workspace ⇒ its handle · otherwise one walk ({@link establish}). The seat holds the env's
|
|
325
|
+
* refusal — a standing one, spelled by the seat's refusal table (a missing snapshot bucket, an archive that is there and did not come back, the cluster
|
|
326
|
+
* refusing the pod at admission, the member-credential broker that cannot be set up, a fill refused the same on any pod, a home the pod answered against
|
|
327
|
+
* the claim, a session row with no occupant token): a fact of this env until `destroy`, answered to every later connect and every later pod creation
|
|
328
|
+
* ({@link provision}) — zero pods, zero store requests, no second line. A refusal that heals by itself (a quota, a store that did not answer) is not held.
|
|
329
|
+
*/
|
|
330
|
+
private readonly seat;
|
|
333
331
|
/** core `ExecutionEnv.homeDir`([ref]):pod 里执行用户的 home —— 声明在场 ⇒ 声明(开环境那一拍与 `$HOME` 对证过),否则 pod 就绪那一拍实测的 `$HOME`,见 {@link LaneHomeSeat}。读时才取(没声明时 pod 起来之前缺席)。 */
|
|
334
332
|
get homeDir(): string | undefined;
|
|
335
333
|
/** 家目录的座(声明在场 ⇒ 开环境那一拍对证 · 否则 pod 就绪那一拍实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。与其余读得出家目录的车道同一只判官、同一句拒。 */
|
|
@@ -381,18 +379,20 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
381
379
|
* ({@link awaitSpaceCommit}: the run store's commit row), then read `<prefix>/<incarnation>/space/latest`: present ⇒
|
|
382
380
|
* restore it through {@link restoreSnapshot} (the one tar download, shared with `resumeVM`; an archive that is gone ⇒
|
|
383
381
|
* loud `workspace_latest_dangling`, the pointer deleted, an empty workspace); absent (404 — the key, with the bucket there) ⇒ a fresh pod; a read that
|
|
384
|
-
* FAILS ⇒ a loud
|
|
382
|
+
* FAILS ⇒ a loud refusal — `connect_failed` (that attempt's: the next connect reads again), or, for a 404 `NoSuchBucket`, the env's standing
|
|
383
|
+
* `env.snapshot_bucket_missing` ({@link refuseMissingBucket}) — never a silent fresh workspace (a run that believes it resumed while it did not is
|
|
385
384
|
* the worst form). Once the pod holds the workspace, this env arms its fence seat (its destroy will commit).
|
|
386
385
|
* The same walk ({@link establish}) is the restore point of a parked run's resumed leg whose park packed nothing ({@link resumeVM}).
|
|
387
386
|
* Persistence off / no store ⇒ a fresh pod, exactly as before. Persistence on but no such session (the occupant absent) ⇒ a
|
|
388
|
-
* fresh pod and one `workspace_space_unavailable` line (no restore by any other key). The occupant read FAULTED ⇒
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
387
|
+
* fresh pod and one `workspace_space_unavailable` line (no restore by any other key). The occupant read FAULTED ⇒ a refusal worded as a
|
|
388
|
+
* failed `latest` read, no pod ({@link provision} refuses it — every entry that would create one; {@link refuseUnreadableOccupant} spells its two facts).
|
|
389
|
+
* `connect` is idempotent — the one order of the env's {@link seat}: a refusal this env holds (a restore that did not come back, a pod the cluster refused at
|
|
390
|
+
* admission, a missing bucket …) is answered as it stands to every later connect on every lane — the previous commit is not waited on again, `latest` is not
|
|
391
|
+
* read again, no pod is created; a walk in flight (the explicit connect, the lazy one of the first operation, `reconnect`, a resume) ⇒ THAT walk's answer (one pod
|
|
392
|
+
* creation, one restore, one refusal line), a caller's abort withdrawing only its own wait; a pod already holding the workspace ⇒ its handle, nothing created
|
|
393
|
+
* (the engine's restore seat is commonly not the first connection: a delegated child touched the env during its placement check); otherwise one walk. A
|
|
394
|
+
* failure that is not held (a pod that could not be created, a space read that failed) ⇒ the next connect walks afresh. The abort signal of the connect that
|
|
395
|
+
* started the walk is the one in force for the walk.
|
|
396
396
|
*/
|
|
397
397
|
connect(config?: RemoteConnectConfig): Promise<ConnectResult>;
|
|
398
398
|
/** The one walk of a connect ({@link connect} runs it once at a time) — also the resumed leg's walk when its park packed nothing ({@link restorePointOf},
|
|
@@ -402,7 +402,7 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
402
402
|
* The restore point's fill (`workspace-fill.ts`): a pod whose workspace this env just minted or restored is handed over only once the fill seat placed
|
|
403
403
|
* its content — through {@link fillChannel} (this adapter's own exec, never the public face: inside a connect the public face waits on that very connect).
|
|
404
404
|
* A fill that does not complete releases the pod (no tool and no run-end snapshot may see a half-filled workspace); a refusal that would answer the same
|
|
405
|
-
* again (`standing`) is held for the env ({@link
|
|
405
|
+
* again (`standing`) is held for the env (its {@link seat}). No seat ⇒ the pod as it came. `origin` tells the seat whether the workspace was minted
|
|
406
406
|
* (a new pod with nothing restored: the space off / unavailable / without `latest`, or a dangling restore point — `latest` or a park snapshot whose archive is gone)
|
|
407
407
|
* or restored (the space's `latest`, or a park snapshot whose archive came back). A park that packed nothing resumes through a new connect, so it reads as that connect does.
|
|
408
408
|
*/
|
|
@@ -430,21 +430,22 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
430
430
|
/** The space lane is on but this run's session has no occupant (no such session — {@link SpaceOccupant}) — said once per env
|
|
431
431
|
* (at connect, or at a destroy with a pod). `reason` = the reading's word. */
|
|
432
432
|
private sayUnavailable;
|
|
433
|
-
/** Hold `error` as this env's refusal ({@link connectRefusal}) and answer it — the ONE writer of the slot (the restore and the pod's admission both write through it). */
|
|
434
|
-
private holdRefusal;
|
|
435
433
|
/**
|
|
436
434
|
* The cluster created this env's pod and it ended `Failed` (or `Succeeded`) before it ever ran — the kubelet refused it at admission
|
|
437
435
|
* (`SysctlForbidden` when `K8S_SANDBOX_IPV6=disabled` meets a kubelet that does not allow the two sysctls, a node-affinity miss, no capacity on the
|
|
438
|
-
* node …): the env's refusal ({@link
|
|
436
|
+
* node …): the env's refusal (held by its {@link seat}: `env.sandbox_refused`, standing) with the kubelet's reason in the sentence — so the operation that meets
|
|
439
437
|
* it, and every later one, says what the cluster said, instead of a new pod per operation. One `sandbox_pod_refused{pod, phase, reason, detail}` line per env
|
|
440
438
|
* (`detail` = the kubelet's own text, cut). A retry of the run builds a fresh env, which asks the cluster afresh.
|
|
441
439
|
*/
|
|
442
440
|
private refusePod;
|
|
443
441
|
/**
|
|
444
|
-
* A lane that is on is keyed by an occupant read that FAULTED (`unreadable`) ⇒ this env creates no pod: the refusal,
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
442
|
+
* A lane that is on is keyed by an occupant read that FAULTED (`unreadable`) ⇒ this env creates no pod: the refusal, worded as the space-read failures are
|
|
443
|
+
* (a failed `latest` / commit-row read says the same head), naming the knob of every lane that refuses; one `workspace_space_unreadable_occupant{sessionId, code}`
|
|
444
|
+
* line per env. `code` = the store error's code, never its text. Not a knob: there is no honest way to run a space we could not read. Two facts, by the code:
|
|
445
|
+
* - `session.incarnation_missing` — the session row is there and carries no occupant token: a data defect that answers the same to every read ⇒ standing
|
|
446
|
+
* (`env.occupant_missing`, held by the {@link seat}); the way out is the row (give it its token, or delete the session) or the knob;
|
|
447
|
+
* - any other code (the read timed out, the store failed, an answer the projection could not read) ⇒ it heals by itself: a retry of the run reads it again
|
|
448
|
+
* (`connect_failed`). The reading is this env's, fixed at construction, so this env answers it to every operation all the same.
|
|
448
449
|
*/
|
|
449
450
|
private refuseUnreadableOccupant;
|
|
450
451
|
/** Every space-lane operator line: the space is named by its incarnation's log prefix only ({@link snapshotLine} does the rest). */
|
|
@@ -457,12 +458,11 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
457
458
|
* here, before any request ({@link refuseUnreadableOccupant}): never retried (the reading is this env's, fixed at construction). */
|
|
458
459
|
private provision;
|
|
459
460
|
/**
|
|
460
|
-
* Lazy-connect on first use —
|
|
461
|
-
* the
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
* next op's connect answers that same refusal at once.
|
|
461
|
+
* Lazy-connect on first use — an operation may meet the env before anything connected it (a delegated child's placement check, a factory layer writing into
|
|
462
|
+
* the env before handing it over), so the adapter connects itself; the engine's explicit connect at the leg's restore seat then finds it connected (the
|
|
463
|
+
* {@link seat}'s idempotent order). `destroyed` is terminal. Concurrent first ops share ONE pod create — through {@link connect}'s one walk in flight, the same
|
|
464
|
+
* seat the explicit connect shares. How long a failure stands is the seat's: a failure of that attempt (a pod that could not be created, a space read that
|
|
465
|
+
* failed) ⇒ the next op connects afresh; the env's held refusal ⇒ the next op's connect answers that same refusal at once.
|
|
466
466
|
*/
|
|
467
467
|
private ensureConnected;
|
|
468
468
|
/**
|
|
@@ -591,8 +591,8 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
591
591
|
* The store answered `404 NoSuchBucket` — to the space's `latest` read (`read: "pointer"`) or to a restore's archive GET (`read: "archive"`, either lane:
|
|
592
592
|
* the pod relays the store's answer, {@link restoreSnapshot}): the workspace snapshot bucket does not exist, so this workspace can be neither restored nor
|
|
593
593
|
* saved — a run started on it would work on an empty /workspace and lose everything it wrote. One fact, one consequence on both lanes and both reads: the
|
|
594
|
-
* env's refusal ({@link
|
|
595
|
-
* the archive read in the same words — and one `workspace_snapshot_bucket_missing{bucket, code, effect}` line per env, carrying `space` on the space lane and
|
|
594
|
+
* env's refusal (`env.snapshot_bucket_missing`, standing, held by the {@link seat}) naming the bucket and the knob that picks it — the pointer read worded as the
|
|
595
|
+
* space-read failures are ({@link spaceReadFailed}), the archive read in the same words — and one `workspace_snapshot_bucket_missing{bucket, code, effect}` line per env, carrying `space` on the space lane and
|
|
596
596
|
* `sessionId` on the park lane (the refusal stands until destroy: later connects and operations answer it without another read or line; a retry of the run
|
|
597
597
|
* builds a fresh env, which asks the store afresh — a bucket created in between is found). The boot probe (`snapshot-bucket-presence.ts`) refuses a boot
|
|
598
598
|
* where the bucket is already missing; this is the same judgement ({@link isNoSuchBucketAnswer}) for a bucket that went missing afterwards or that the probe
|
|
@@ -706,7 +706,7 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
706
706
|
* - space: the archive `latest` names is THERE and did not come back ({@link RestoreFailureClass}): the run is refused all the
|
|
707
707
|
* same — never an empty workspace (the user's changes are still in the bucket) — with exactly one `workspace_restore_failed`
|
|
708
708
|
* line (its `attempts`; an attempt that failed before one that succeeded is no line) and a refusal naming the two ways out,
|
|
709
|
-
* held for the env
|
|
709
|
+
* held for the env by its {@link seat} (`env.sandbox_refused`, standing).
|
|
710
710
|
* A pod that could not be created is that attempt's failure, not the archive's: returned as it is — no line, nothing held, the
|
|
711
711
|
* next connect tries again. An abort during the pause ends the restore `aborted` (not the archive's either).
|
|
712
712
|
*/
|
|
@@ -877,6 +877,8 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
877
877
|
* 循环(上限 64、只锚不折、遇环即止),脚本与读法的单一属主在 `remote-shell.ts`,四条 shell 腿共用同一份。
|
|
878
878
|
* 缺席读法用不上(本腿在场);`ok:false` 由 core 读作「这一次没有跳」,永不是拒绝。 */
|
|
879
879
|
canonicalChain(p: string, abortSignal?: AbortSignal): Promise<Result<SymlinkChain, FileError>>;
|
|
880
|
+
/** 绑定读(逐答重绑,Linux 读法;脚本与读法的单一属主在 `remote-shell.ts`,四条 shell 腿共用同一份)。 */
|
|
881
|
+
openBound(p: string, abortSignal?: AbortSignal): Promise<Result<BoundFileObject, FileError>>;
|
|
880
882
|
canonicalPath(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
881
883
|
exists(p: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
|
|
882
884
|
createDir(p: string, options?: {
|
|
@@ -11,7 +11,7 @@ import { WORKSPACE_SNAPSHOT_BUCKET_DEFAULT } from "../object-store-buckets.js";
|
|
|
11
11
|
import { SNAPSHOT_BUCKET_MISSING, isNoSuchBucketAnswer } from "./s3-answer.js";
|
|
12
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";
|
|
13
13
|
import { spaceCommitAlive } from "../workspace-space-commit.js";
|
|
14
|
-
import { incarnationLogPrefix, redactIncarnations } from "../session-incarnation.js";
|
|
14
|
+
import { SESSION_INCARNATION_MISSING, incarnationLogPrefix, redactIncarnations } from "../session-incarnation.js";
|
|
15
15
|
import { WORKSPACE_CACHE_SPACE_LABEL, workspaceCacheClaimOf, workspaceCacheEnv } from "../workspace-cache.js";
|
|
16
16
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
17
17
|
import { PodDeleteRefused, reportPodDeleteFailed } from "./k8s-pod-delete-failure.js";
|
|
@@ -27,6 +27,7 @@ import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
|
27
27
|
import { randomUUID } from "node:crypto";
|
|
28
28
|
import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
|
|
29
29
|
import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
|
|
30
|
+
import { connectRefusal, createConnectSeat } from "./remote-env-connect-seat.js";
|
|
30
31
|
const PROVIDER = "k8s";
|
|
31
32
|
const K8S_BG_DIR_ROOT = "/tmp/.sema-bg";
|
|
32
33
|
const K8S_BG_MAX_CONCURRENT = numEnvOr("K8S_BG_MAX_CONCURRENT", 8, 1);
|
|
@@ -92,6 +93,10 @@ function spaceLaneOf(policy, occupant) {
|
|
|
92
93
|
function spaceReadFailed(what, detail, cause) {
|
|
93
94
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace space ${what} read failed${detail}`, cause instanceof Error ? cause : undefined) };
|
|
94
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
|
+
}
|
|
95
100
|
function restoreFailureIsTransient(cls) {
|
|
96
101
|
switch (cls) {
|
|
97
102
|
case "timeout":
|
|
@@ -222,7 +227,18 @@ export class RemoteK8sExecutionEnv {
|
|
|
222
227
|
idleWaiters = [];
|
|
223
228
|
connectInFlight;
|
|
224
229
|
resumeInFlight;
|
|
225
|
-
|
|
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
|
+
});
|
|
226
242
|
get homeDir() {
|
|
227
243
|
return this.homeSeat.home;
|
|
228
244
|
}
|
|
@@ -386,18 +402,12 @@ export class RemoteK8sExecutionEnv {
|
|
|
386
402
|
});
|
|
387
403
|
}
|
|
388
404
|
connect(config) {
|
|
389
|
-
|
|
390
|
-
return this.connectInFlight;
|
|
391
|
-
const walk = this.establish(config).finally(() => {
|
|
392
|
-
if (this.connectInFlight === walk)
|
|
393
|
-
this.connectInFlight = undefined;
|
|
394
|
-
});
|
|
395
|
-
this.connectInFlight = walk;
|
|
396
|
-
return walk;
|
|
405
|
+
return this.seat.connect(config);
|
|
397
406
|
}
|
|
398
407
|
async establish(config) {
|
|
399
|
-
|
|
400
|
-
|
|
408
|
+
const held = this.seat.held;
|
|
409
|
+
if (held)
|
|
410
|
+
return held;
|
|
401
411
|
const signal = config?.abortSignal;
|
|
402
412
|
const lane = this.spaceLane;
|
|
403
413
|
switch (lane.state) {
|
|
@@ -434,7 +444,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
434
444
|
const filled = await fill.fill(this.fillChannel(), this.cwd, c.origin);
|
|
435
445
|
if (!filled.ok) {
|
|
436
446
|
await this.releasePod();
|
|
437
|
-
return
|
|
447
|
+
return this.seat.answer(filled);
|
|
438
448
|
}
|
|
439
449
|
}
|
|
440
450
|
if (this.destroyed) {
|
|
@@ -488,10 +498,6 @@ export class RemoteK8sExecutionEnv {
|
|
|
488
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)",
|
|
489
499
|
});
|
|
490
500
|
}
|
|
491
|
-
holdRefusal(error) {
|
|
492
|
-
this.connectRefusal = { ok: false, error };
|
|
493
|
-
return this.connectRefusal;
|
|
494
|
-
}
|
|
495
501
|
refusePod(pod, phase, reason, message) {
|
|
496
502
|
this.logger?.warn("sandbox_pod_refused", {
|
|
497
503
|
pod,
|
|
@@ -501,19 +507,24 @@ export class RemoteK8sExecutionEnv {
|
|
|
501
507
|
...(this.cfg.sessionId ? { sessionId: this.cfg.sessionId } : {}),
|
|
502
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",
|
|
503
509
|
});
|
|
504
|
-
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`));
|
|
505
511
|
}
|
|
506
512
|
refuseUnreadableOccupant(code) {
|
|
507
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;
|
|
508
515
|
if (!this.unreadableOccupantSaid) {
|
|
509
516
|
this.unreadableOccupantSaid = true;
|
|
510
517
|
this.logger?.warn("workspace_space_unreadable_occupant", {
|
|
511
518
|
sessionId: this.cfg.sessionId,
|
|
512
519
|
code,
|
|
513
|
-
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`,
|
|
514
523
|
});
|
|
515
524
|
}
|
|
516
|
-
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}`);
|
|
517
528
|
}
|
|
518
529
|
spaceLine(event, space, fields) {
|
|
519
530
|
this.snapshotLine(event, { space: incarnationLogPrefix(space.incarnation), ...fields });
|
|
@@ -524,8 +535,9 @@ export class RemoteK8sExecutionEnv {
|
|
|
524
535
|
provision(signal) {
|
|
525
536
|
const space = this.spaceLane;
|
|
526
537
|
const cache = this.cacheLane;
|
|
527
|
-
|
|
528
|
-
|
|
538
|
+
const held = this.seat.held;
|
|
539
|
+
if (held)
|
|
540
|
+
return Promise.resolve(held);
|
|
529
541
|
if (space.state === "unreadable")
|
|
530
542
|
return Promise.resolve(this.refuseUnreadableOccupant(space.code));
|
|
531
543
|
if (cache.state === "unreadable")
|
|
@@ -540,7 +552,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
540
552
|
const halt = signal ? AbortSignal.any([signal, refused.signal]) : refused.signal;
|
|
541
553
|
return withRetry(async () => {
|
|
542
554
|
const r = await once(signal);
|
|
543
|
-
if (!r.ok && this.
|
|
555
|
+
if (!r.ok && this.seat.held)
|
|
544
556
|
refused.abort();
|
|
545
557
|
return r;
|
|
546
558
|
}, policy, { signal: halt });
|
|
@@ -716,7 +728,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
716
728
|
const leaseName = broker !== undefined ? `${SANDBOX_LEASE_NAME_PREFIX}${randomUUID()}` : undefined;
|
|
717
729
|
const placeholder = broker !== undefined ? buildSandboxLeaseFile(broker.origins, placeholderLeaseEntries(broker.origins)) : undefined;
|
|
718
730
|
if (placeholder !== undefined && !placeholder.ok) {
|
|
719
|
-
return this.
|
|
731
|
+
return this.seat.answer(connectRefusal("credential_broker_refused", `the member-credential broker cannot be set up for this pod: ${placeholder.problem}`));
|
|
720
732
|
}
|
|
721
733
|
const layered = { ...(cache ? workspaceCacheEnv(cache.mount) : {}), ...(this.cfg.podEnv ?? {}) };
|
|
722
734
|
const envEntries = [
|
|
@@ -770,7 +782,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
770
782
|
applyPodSpecPatch(pod, this.cfg.podSpecPatch, { broker: true });
|
|
771
783
|
}
|
|
772
784
|
catch (e) {
|
|
773
|
-
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)`));
|
|
774
786
|
}
|
|
775
787
|
}
|
|
776
788
|
else if (this.cfg.podSpecPatch)
|
|
@@ -783,7 +795,8 @@ export class RemoteK8sExecutionEnv {
|
|
|
783
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) };
|
|
784
796
|
}
|
|
785
797
|
if (created.status === 401 || created.status === 403) {
|
|
786
|
-
|
|
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) };
|
|
787
800
|
}
|
|
788
801
|
if (created.status >= 300) {
|
|
789
802
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `pod create failed (${created.status}): ${JSON.stringify(created.json).slice(0, 300)}`) };
|
|
@@ -876,8 +889,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
876
889
|
if (verdict !== undefined) {
|
|
877
890
|
await this.deletePod(name).catch(() => { });
|
|
878
891
|
this.podName = undefined;
|
|
879
|
-
|
|
880
|
-
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) };
|
|
881
893
|
}
|
|
882
894
|
}
|
|
883
895
|
if (cache?.fresh)
|
|
@@ -1129,7 +1141,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1129
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 — ` +
|
|
1130
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, ` +
|
|
1131
1143
|
"or set WORKSPACE_PERSIST=off for a deployment that keeps no workspace between runs";
|
|
1132
|
-
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}`));
|
|
1133
1145
|
}
|
|
1134
1146
|
async readLatest(space, signal) {
|
|
1135
1147
|
const r = await this.readPointer(space, "latest", signal);
|
|
@@ -1391,7 +1403,7 @@ export class RemoteK8sExecutionEnv {
|
|
|
1391
1403
|
case "space": {
|
|
1392
1404
|
const tries = attempt === 1 ? "1 attempt" : `${attempt} attempts`;
|
|
1393
1405
|
this.spaceLine("workspace_restore_failed", target.space, { snapshotId, class: failure.cls, attempts: attempt, boundMs: this.cfg.dataTimeoutMs, error: failure.detail });
|
|
1394
|
-
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), ` +
|
|
1395
1407
|
"so this run does not start on an empty /workspace. Ways out: raise WORKSPACE_SNAPSHOT_TIMEOUT_MS if the archive is large, " +
|
|
1396
1408
|
"or set WORKSPACE_PERSIST=off for one run (it starts from an empty /workspace and deletes no snapshot)", failure.cause));
|
|
1397
1409
|
}
|
|
@@ -2263,6 +2275,14 @@ export class RemoteK8sExecutionEnv {
|
|
|
2263
2275
|
run: (cmd, abortSignal) => this.fsExec(cmd, abortSignal),
|
|
2264
2276
|
resolve: (p) => this.resolve(p),
|
|
2265
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
|
+
},
|
|
2266
2286
|
}));
|
|
2267
2287
|
}
|
|
2268
2288
|
async fileInfo(p, abortSignal) {
|
|
@@ -2277,6 +2297,9 @@ export class RemoteK8sExecutionEnv {
|
|
|
2277
2297
|
async canonicalChain(p, abortSignal) {
|
|
2278
2298
|
return this.posixFs.canonicalChain(p, abortSignal);
|
|
2279
2299
|
}
|
|
2300
|
+
async openBound(p, abortSignal) {
|
|
2301
|
+
return this.posixFs.openBound(p, abortSignal);
|
|
2302
|
+
}
|
|
2280
2303
|
async canonicalPath(p, abortSignal) {
|
|
2281
2304
|
return this.posixFs.canonicalPath(p, abortSignal);
|
|
2282
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?: {
|