@sema-agent/server 7.125.1 → 7.127.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 +229 -2
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +346 -145
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/chart/templates/networkpolicy-sandbox.yaml +57 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +32 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +3 -2
- package/deploy/sema-up/chart/templates/server.yaml +2 -1
- package/deploy/sema-up/chart/values.yaml +22 -0
- package/deploy/sema-up/cluster-up.sh +10 -10
- package/deploy/sema-up/kube-up.sh +20 -13
- package/deploy/sema-up/lib/dataroot.sh +1 -1
- package/deploy/sema-up/lib/envf.sh +5 -5
- package/deploy/sema-up/lib/flagshape.sh +5 -5
- package/deploy/sema-up/lib/lanes.py +1 -1
- package/deploy/sema-up/lib/node-addrs.sh +42 -0
- package/deploy/sema-up/lib/parse-machines.py +2 -2
- package/deploy/sema-up/lib/shell-access.sh +1 -1
- package/deploy/sema-up/preflight.sh +4 -4
- package/deploy/sema-up/sema-up.sh +33 -33
- package/deploy/sema-up/smoke.sh +3 -3
- package/deploy/sema-up/stack/compose.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +26 -3
- package/dist/approval-card.d.ts +20 -4
- package/dist/approval-content-kind.d.ts +38 -22
- package/dist/approval-content-kind.js +20 -4
- package/dist/audit.js +3 -0
- package/dist/bench/s1/arms.d.ts +9 -37
- package/dist/bench/s1/arms.js +4 -61
- package/dist/bench/s1/live-deps.d.ts +4 -10
- package/dist/bench/s1/live-deps.js +1 -222
- package/dist/bench/s1/oracle.d.ts +1 -1
- package/dist/bench/s1/reviewer.d.ts +1 -1
- package/dist/bench/s1/row.d.ts +5 -7
- package/dist/bench/s1/row.js +0 -2
- package/dist/bench/s1/run-firm.d.ts +2 -2
- package/dist/bench/s1/run-firm.js +1 -4
- package/dist/bench/s1/runner-ctx.d.ts +17 -29
- package/dist/bench/s1/runner-ctx.js +3 -7
- package/dist/bench/s1/tasks.d.ts +9 -46
- package/dist/bench/s1/tasks.js +4 -63
- package/dist/boot/execution-env.d.ts +38 -26
- package/dist/boot/execution-env.js +87 -41
- package/dist/boot/leader.d.ts +2 -1
- package/dist/boot/memory-boundary.d.ts +1 -1
- package/dist/boot/memory-consolidation.d.ts +37 -3
- package/dist/boot/memory-consolidation.js +18 -2
- package/dist/boot/parked-revive-gate.d.ts +0 -3
- package/dist/boot/parked-revive-gate.js +2 -2
- package/dist/boot/resolve-spec.d.ts +0 -2
- package/dist/boot/resolve-spec.js +12 -17
- package/dist/boot/retention-lane.d.ts +12 -1
- package/dist/boot/retention-lane.js +2 -0
- package/dist/boot/runner-deps.d.ts +7 -6
- package/dist/boot/runner-deps.js +5 -16
- package/dist/boot/session-faces.d.ts +7 -2
- package/dist/boot/session-faces.js +37 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.js +7 -4
- package/dist/boot/stage-06-runners.d.ts +2 -3
- package/dist/boot/stage-06-runners.js +13 -9
- package/dist/boot/stage-07-capability-layer.d.ts +2 -3
- package/dist/boot/stage-07-capability-layer.js +3 -4
- package/dist/boot/stage-08-reapers.d.ts +2 -3
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -3
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +2 -3
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/boot/stores.d.ts +1 -0
- package/dist/boot/stores.js +4 -3
- package/dist/boot/web-admin.d.ts +5 -0
- package/dist/boot/web-admin.js +28 -14
- package/dist/brain.d.ts +2 -2
- package/dist/brain.js +1 -1
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/hands-lane.d.ts +9 -6
- package/dist/capabilities/hands-lane.js +2 -2
- package/dist/capabilities/memory-notice.d.ts +27 -22
- package/dist/capabilities/memory-notice.js +15 -17
- package/dist/capabilities/scenarios.d.ts +9 -14
- package/dist/capabilities/scenarios.js +5 -50
- package/dist/capability-facts.d.ts +22 -11
- package/dist/capability-facts.js +13 -6
- package/dist/config-catalog.js +14 -5
- package/dist/config-provider.d.ts +5 -1
- package/dist/config-provider.js +23 -4
- package/dist/config-types.d.ts +19 -2
- package/dist/config.js +41 -6
- package/dist/deployment-governance.d.ts +19 -21
- package/dist/deployment-governance.js +7 -8
- package/dist/env-name-allowlist-knobs.d.ts +5 -5
- package/dist/env-name-allowlist-knobs.js +1 -0
- package/dist/execution-lane-caps.d.ts +12 -0
- package/dist/execution-lane-caps.js +18 -4
- package/dist/fleet/subagent-tail-bus.js +15 -5
- package/dist/hooks/hook-runner.d.ts +84 -30
- package/dist/hooks/hook-runner.js +74 -41
- package/dist/http/admission.js +17 -10
- package/dist/http/durable-run-leg.d.ts +5 -4
- package/dist/http/durable-run-leg.js +2 -4
- package/dist/http/idempotency.d.ts +28 -6
- package/dist/http/idempotency.js +10 -2
- package/dist/http/principal-gate.d.ts +14 -0
- package/dist/http/principal-gate.js +7 -0
- package/dist/http/request-guards.d.ts +2 -1
- package/dist/http/request-guards.js +14 -14
- package/dist/http/resume-legs.d.ts +2 -2
- package/dist/http/resume-legs.js +20 -10
- package/dist/http/route-ctx.d.ts +15 -7
- package/dist/http/routes/a2a-serve.js +11 -7
- package/dist/http/routes/approvals-assistant.js +18 -3
- package/dist/http/routes/background.d.ts +1 -1
- package/dist/http/routes/background.js +1 -1
- package/dist/http/routes/capabilities.js +10 -2
- package/dist/http/routes/mcp-reconnect.js +1 -1
- package/dist/http/routes/runs.d.ts +4 -3
- package/dist/http/routes/runs.js +35 -14
- package/dist/http/routes/session-sync.js +14 -11
- package/dist/http/routes/sessions.js +9 -9
- package/dist/http/routes/side-query.js +6 -19
- package/dist/http/routes/tasks.js +29 -19
- package/dist/http/routes/workflows.js +8 -0
- package/dist/http/server.d.ts +4 -0
- package/dist/http/server.js +3 -5
- package/dist/http/submit-replay.d.ts +15 -8
- package/dist/http/submit-replay.js +6 -0
- package/dist/http/wire-types.d.ts +7 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal-run-env.d.ts +30 -0
- package/dist/internal-run-env.js +11 -0
- package/dist/leader/wire.js +3 -2
- package/dist/lsp/manager.js +1 -1
- package/dist/memory-scope.d.ts +19 -7
- package/dist/memory-scope.js +25 -8
- package/dist/observability/fail-open.d.ts +31 -3
- package/dist/observability/fail-open.js +31 -3
- package/dist/observability/metrics.d.ts +0 -1
- package/dist/observability/metrics.js +1 -4
- package/dist/observability/run-terminal.js +72 -4
- package/dist/permission-rule-vocab.d.ts +3 -2
- package/dist/permission-rule-vocab.js +1 -1
- package/dist/plan-cache-probe.d.ts +3 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/caching-session-store.d.ts +42 -22
- package/dist/plugins/caching-session-store.js +19 -9
- package/dist/plugins/checkpoint-store-sql.d.ts +42 -28
- package/dist/plugins/checkpoint-store-sql.js +57 -11
- package/dist/plugins/file-run-store.d.ts +25 -21
- package/dist/plugins/file-run-store.js +70 -34
- package/dist/plugins/k8s-exec-protocol.js +3 -0
- package/dist/plugins/local-checkpoint-store.d.ts +8 -5
- package/dist/plugins/local-checkpoint-store.js +5 -15
- package/dist/plugins/local-session-store.d.ts +66 -7
- package/dist/plugins/local-session-store.js +168 -101
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +1 -1
- package/dist/plugins/memory-run-store.d.ts +19 -3
- package/dist/plugins/memory-run-store.js +38 -3
- package/dist/plugins/one-time-migrations.d.ts +165 -39
- package/dist/plugins/one-time-migrations.js +51 -144
- package/dist/plugins/pg-pool.js +11 -1
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +9 -5
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +1 -0
- package/dist/plugins/remote-env-e2b.d.ts +4 -0
- package/dist/plugins/remote-env-e2b.js +2 -1
- package/dist/plugins/remote-env-host.d.ts +23 -1
- package/dist/plugins/remote-env-host.js +20 -2
- package/dist/plugins/remote-env-k8s.d.ts +264 -41
- package/dist/plugins/remote-env-k8s.js +551 -82
- package/dist/plugins/remote-env-local-docker.d.ts +4 -0
- package/dist/plugins/remote-env-local-docker.js +2 -1
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/retention-store-sql.d.ts +4 -1
- package/dist/plugins/retention-store-sql.js +11 -9
- package/dist/plugins/run-store-sql.d.ts +44 -6
- package/dist/plugins/run-store-sql.js +92 -27
- package/dist/plugins/session-capture-record-store-sql.d.ts +2 -3
- package/dist/plugins/session-store.d.ts +4 -3
- package/dist/plugins/session-store.js +1 -1
- package/dist/plugins/sql-driver.d.ts +2 -1
- package/dist/plugins/store-backend.d.ts +12 -5
- package/dist/plugins/store-backend.js +13 -4
- package/dist/plugins/store-contracts.d.ts +20 -3
- package/dist/plugins/store-open-wait.js +3 -0
- package/dist/plugins/tidb-pool.js +15 -3
- package/dist/plugins/tidb-session-storage.js +3 -2
- package/dist/plugins/tidb-session-store.d.ts +2 -0
- package/dist/plugins/tidb-session-store.js +6 -2
- package/dist/request-key-closure.d.ts +32 -7
- package/dist/request-key-closure.js +25 -9
- package/dist/router/route-orchestration.d.ts +3 -11
- package/dist/router/route-orchestration.js +0 -4
- package/dist/rule-entry-list.js +0 -4
- package/dist/rules-consent.js +3 -0
- package/dist/run-acceptance.d.ts +51 -0
- package/dist/run-acceptance.js +27 -0
- package/dist/run-local.d.ts +5 -4
- package/dist/run-local.js +23 -17
- package/dist/runs.d.ts +7 -11
- package/dist/runs.js +6 -16
- package/dist/sandbox-egress-policy.d.ts +61 -0
- package/dist/sandbox-egress-policy.js +235 -0
- package/dist/security.d.ts +7 -0
- package/dist/security.js +5 -0
- package/dist/server-secret-env.d.ts +10 -4
- package/dist/server-secret-env.js +4 -1
- package/dist/session-error-wire.d.ts +2 -2
- package/dist/session-incarnation.d.ts +46 -2
- package/dist/session-incarnation.js +14 -3
- package/dist/session-sync-kernel.d.ts +10 -3
- package/dist/session-titler.d.ts +5 -3
- package/dist/spec-fields.d.ts +24 -8
- package/dist/spec-fields.js +21 -2
- package/dist/task-workflow.d.ts +6 -4
- package/dist/task-workflow.js +3 -2
- package/dist/tool-approval.d.ts +78 -6
- package/dist/tool-approval.js +7 -3
- package/dist/trace/core-keyset-guard.d.ts +6 -5
- package/dist/trace/engine-notice-wire.d.ts +26 -1
- package/dist/trace/engine-notice-wire.js +7 -2
- package/dist/trace/ledger-events.d.ts +9 -3
- package/dist/trace/ledger-sink.d.ts +7 -0
- package/dist/trace/ledger-sink.js +14 -4
- package/dist/trace/project.d.ts +57 -11
- package/dist/trace/project.js +54 -8
- package/dist/trace/self-minted-wire.d.ts +94 -2
- package/dist/trace/self-minted-wire.js +28 -0
- package/dist/trace/tool-output-media.d.ts +26 -0
- package/dist/trace/tool-output-media.js +57 -0
- package/dist/trace/wire-projection-faces.d.ts +7 -1
- package/dist/trace/wire-projection-faces.js +2 -1
- package/dist/web-admin-client/apply-effective.d.ts +13 -7
- package/dist/web-admin-client/apply-effective.js +11 -26
- package/dist/web-admin-client/apply-ledger.d.ts +2 -2
- package/dist/web-admin-client/candidate-gate.d.ts +39 -4
- package/dist/web-admin-client/candidate-gate.js +9 -2
- package/dist/web-admin-client/facade.d.ts +2 -2
- package/dist/web-admin-client/http-client.d.ts +3 -1
- package/dist/web-admin-client/http-client.js +4 -3
- package/dist/web-admin-client/read-warnings.d.ts +33 -5
- package/dist/web-admin-client/read-warnings.js +41 -6
- package/dist/web-admin-client/types.d.ts +11 -5
- package/dist/workspace-cache.d.ts +45 -0
- package/dist/workspace-cache.js +30 -0
- package/dist/workspace-space-commit.d.ts +116 -0
- package/dist/workspace-space-commit.js +117 -0
- package/dist/workspace-space.d.ts +75 -9
- package/dist/workspace-space.js +52 -1
- package/package.json +4 -4
- package/dist/boot/session-incarnation-seat.d.ts +0 -41
- package/dist/boot/session-incarnation-seat.js +0 -69
- package/dist/capabilities/code-review-council.d.ts +0 -49
- package/dist/capabilities/code-review-council.js +0 -111
- package/dist/capabilities/collab-wire.d.ts +0 -36
- package/dist/capabilities/collab-wire.js +0 -2
- package/dist/capabilities/collab-workflows.d.ts +0 -47
- package/dist/capabilities/collab-workflows.js +0 -90
- package/dist/capabilities/prompts/code-review.d.ts +0 -25
- package/dist/capabilities/prompts/code-review.js +0 -39
- package/dist/capabilities/prompts/team.d.ts +0 -10
- package/dist/capabilities/prompts/team.js +0 -6
- package/dist/capabilities/team.d.ts +0 -113
- package/dist/capabilities/team.js +0 -70
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SpaceReclaimer, type WorkspaceSpacePolicy, type CacheReclaimReason } from "../workspace-space.js";
|
|
2
|
+
import { type SpaceCommitFence } from "../workspace-space-commit.js";
|
|
3
|
+
import { type WorkspaceCacheCapability } from "../workspace-cache.js";
|
|
4
|
+
import { type SandboxEgressGap, type SandboxIpv6 } from "../sandbox-egress-policy.js";
|
|
2
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 BackgroundShellCapability, type BackgroundShellId, type BackgroundShellError, type BackgroundSpawnOptions, type BackgroundPoll } from "@sema-agent/core";
|
|
3
6
|
import type { PodSpecPatch } from "./k8s-exec-protocol.js";
|
|
4
7
|
type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
|
|
@@ -37,10 +40,20 @@ export interface K8sEnvConfig {
|
|
|
37
40
|
rpcTimeoutMs?: number;
|
|
38
41
|
/** Zero-progress liveness bound for commands (no output AND not finished ⇒ hang). */
|
|
39
42
|
livenessMs?: number;
|
|
40
|
-
/**
|
|
43
|
+
/** The ONE data bound (`WORKSPACE_SNAPSHOT_TIMEOUT_MS`; default = `WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT`, owned by
|
|
44
|
+
* `workspace-space.ts`): the snapshot upload's and restore's wall-clock, the deadline this replica's commits write into
|
|
45
|
+
* their space-commit fence row (a reader judges another run's row by the deadline IN that row, never by this field), and
|
|
46
|
+
* a large file transfer's wall-clock. Every use reads this field — never a second number. */
|
|
41
47
|
dataTimeoutMs?: number;
|
|
48
|
+
/** The snapshot's exclusion table (`WORKSPACE_SNAPSHOT_EXCLUDE`, items already in the closed form — `workspace-space.ts`):
|
|
49
|
+
* the ONE tar upload ({@link RemoteK8sExecutionEnv.uploadSnapshot}, both lanes) passes `--exclude=./<item>` per item.
|
|
50
|
+
* Absent / empty ⇒ the upload command is what it always was. */
|
|
51
|
+
snapshotExclude?: readonly string[];
|
|
42
52
|
cpu?: string;
|
|
43
53
|
memory?: string;
|
|
54
|
+
/** The sandbox pod's IPv6 posture (`K8S_SANDBOX_IPV6`; closed set + default owned by `sandbox-egress-policy.ts`): `disabled` ⇒ the
|
|
55
|
+
* pod-level sysctls of {@link sandboxPodSysctls}; `enabled` (default) ⇒ no `securityContext` key, the pod byte-identical to before. */
|
|
56
|
+
ipv6?: SandboxIpv6;
|
|
44
57
|
retry?: {
|
|
45
58
|
maxAttempts?: number;
|
|
46
59
|
backoffMs?: number;
|
|
@@ -58,8 +71,8 @@ export interface K8sEnvConfig {
|
|
|
58
71
|
* trifecta. The presigned URL is used only inside an adapter-issued exec at suspend/resume time, so it never
|
|
59
72
|
* reaches an agent turn.
|
|
60
73
|
*
|
|
61
|
-
* WORKSPACE SPACE (`workspacePersist`, default `off`
|
|
62
|
-
* RUNS, in its own sub-prefix `<prefix>/<
|
|
74
|
+
* WORKSPACE SPACE (`workspacePersist`, default `space`; `off` = a fresh workspace every run): the same store also carries the workspace ACROSS
|
|
75
|
+
* RUNS, in its own sub-prefix `<prefix>/<incarnation>/space/` — `space/latest` (body = the newest snapshotId) names the
|
|
63
76
|
* snapshot `connect()` restores; `destroy()` snapshots into `space/`, moves `latest`, keeps the newest `workspaceSnapshotKeep`
|
|
64
77
|
* (the same one upload / one download as suspend / resume, see `SnapshotTarget`). The pointer read / write, the listing and the deletes are
|
|
65
78
|
* WORKER-side requests to `endpoint` (the snapshot-BLOB offload dials the same `MINIO_ENDPOINT` from the worker).
|
|
@@ -74,7 +87,7 @@ export interface K8sEnvConfig {
|
|
|
74
87
|
presignTtlSec?: number;
|
|
75
88
|
/** The workspace-space policy (the capability fact `workspaceSpace`'s witness, handed in by the factory; the adapter
|
|
76
89
|
* never resolves defaults itself). Absent or `persist: "off"` ⇒ no persistence across runs. */
|
|
77
|
-
space?:
|
|
90
|
+
space?: WorkspaceSpacePolicy;
|
|
78
91
|
};
|
|
79
92
|
/**
|
|
80
93
|
* The PARK lane's partition (`<prefix>/<sessionId>/<snapshotId>.tar.gz`), set by {@link k8sExecutionEnvFactory} from
|
|
@@ -84,18 +97,22 @@ export interface K8sEnvConfig {
|
|
|
84
97
|
* it never decides who reads. Segment shape is validated in exactly one place ({@link RemoteK8sExecutionEnv.presignSnapshot}).
|
|
85
98
|
*/
|
|
86
99
|
sessionId?: string;
|
|
100
|
+
/** The SPACE lane's occupant — handed in per run by the factory binding (`boot/execution-env.ts`); see {@link SpaceOccupant}. */
|
|
101
|
+
spaceOccupant?: SpaceOccupant;
|
|
87
102
|
/**
|
|
88
|
-
* The
|
|
89
|
-
*
|
|
90
|
-
* (`
|
|
91
|
-
*
|
|
92
|
-
* (
|
|
93
|
-
*
|
|
94
|
-
* line
|
|
103
|
+
* The build-cache volume per workspace space (`WORKSPACE_CACHE=volume`; the capability fact `workspaceCache`'s witness, handed
|
|
104
|
+
* in by the factory — absent for a delegated child, whose env opens no space). Keyed by the SAME space key as the space lane
|
|
105
|
+
* (`spaceOccupant.incarnation`): connect finds / creates the space's claim (`workspaceCacheClaimOf`) and mounts it at `mount`
|
|
106
|
+
* — outside the workspace mount, so no snapshot ever covers it — and layers the cache env closed table under `podEnv`
|
|
107
|
+
* (an explicit operator value of the same name wins). A volume that cannot be had (create refused, not bound within
|
|
108
|
+
* `dataTimeoutMs`, no such session ⇒ no space key) ⇒ the pod starts WITHOUT it and without the cache env: `recordFailOpen` + one
|
|
109
|
+
* `workspace_cache_unavailable` line. An occupant read that FAULTED is not a miss: the run is refused, as on the space lane
|
|
110
|
+
* ({@link SpaceOccupant}). Absent ⇒ no claim request, the pod as before.
|
|
95
111
|
*/
|
|
96
|
-
|
|
112
|
+
cache?: WorkspaceCacheCapability;
|
|
97
113
|
/** Operator log seat (the e2b adapter's shape): `workspace_snapshot_failed` / `workspace_snapshot_prune_failed` /
|
|
98
|
-
* `workspace_space_unavailable` / `workspace_commit_abandoned` / `
|
|
114
|
+
* `workspace_space_unavailable` / `workspace_space_unreadable_occupant` / `workspace_commit_abandoned` / `workspace_latest_dangling` /
|
|
115
|
+
* `workspace_restore_failed` / `workspace_cache_fresh` / `workspace_cache_unavailable`. */
|
|
99
116
|
logger?: {
|
|
100
117
|
warn(event: string, fields?: Record<string, unknown>): void;
|
|
101
118
|
};
|
|
@@ -115,8 +132,10 @@ export interface K8sEnvConfig {
|
|
|
115
132
|
podSpecPatch?: PodSpecPatch;
|
|
116
133
|
/** RFC B5 (sema-internal server/docs/design/ENV-SELECT-AND-REGION-SOURCE.md 抽象点②): operator-trusted env injected into the
|
|
117
134
|
* sandbox container (e.g. `SEMA_PKG_SOURCE=cn|global` — the runtime package-source selector the image's
|
|
118
|
-
* pkg-source.sh hook materializes at start). Deployment
|
|
119
|
-
*
|
|
135
|
+
* pkg-source.sh hook materializes at start). Deployment config, NEVER task-controlled. This is the pod's ONE container-env
|
|
136
|
+
* seat: the region package-source set AND the deployer-named forwarding (`K8S_SANDBOX_ENV` keys, original values) arrive
|
|
137
|
+
* here already merged by the boot assembly (explicit names win). Values go into `containers[0].env[]` and nowhere else —
|
|
138
|
+
* never a command string, the workspace or a snapshot. Absent = the pod is byte-identical to today. */
|
|
120
139
|
podEnv?: Record<string, string>;
|
|
121
140
|
}
|
|
122
141
|
export { type PodSpecPatch, applyPodSpecPatch } from "./k8s-exec-protocol.js";
|
|
@@ -150,11 +169,52 @@ export interface K8sEnvDeps {
|
|
|
150
169
|
/** Worker-side object-store requests (the space pointer, listing, deletes). Default: global `fetch`. */
|
|
151
170
|
fetch?: typeof fetch;
|
|
152
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* One run's space occupant — the space key of both lanes keyed by it (the space lane, the cache volume) — a closed set of three
|
|
174
|
+
* readings of the session row's cell, read by id from the session store (`SessionStore.incarnationOf`) when the run's env is built:
|
|
175
|
+
* - known: `incarnation` — the partition (`<prefix>/<incarnation>/space/…`): the session OCCUPANT's incarnation token
|
|
176
|
+
* (`session-incarnation.ts`, the one owner of "is this the same occupant"). Why not the session id: the space lane is read by
|
|
177
|
+
* whoever runs next in the session, so its partition decides WHO reads — a session id deleted and registered again (by anyone)
|
|
178
|
+
* is a new occupant and starts from an empty workspace. Never logged whole: operator lines carry `incarnationLogPrefix`;
|
|
179
|
+
* `fence` — this env's seat on the space's commit fence (the run store's `workspace_space_commit` row,
|
|
180
|
+
* `workspace-space-commit.ts`): `connect()` waits on it, the run-end commit holds it;
|
|
181
|
+
* - absent: `unread: "incarnation_unknown"` — there is NO space for this run: the store answered that it has no such session
|
|
182
|
+
* (deleted / never registered), or this replica holds no commit fence to key one by (see `occupantOf`) ⇒ a lane keyed by it
|
|
183
|
+
* does not open (no restore, no save, no volume) and says so in one line;
|
|
184
|
+
* - faulted: `fault` — the read did not answer (it threw, or answered something that is not a token): the code of the store's
|
|
185
|
+
* error (never its text), `incarnation_read_failed` without one. A space we could not read is not "no space" ⇒ a lane keyed by
|
|
186
|
+
* it refuses this env (`RemoteK8sExecutionEnv.provision`, the one pod seat): the run never starts on a workspace it believes is its own.
|
|
187
|
+
* Never a fallback to the session id.
|
|
188
|
+
*/
|
|
189
|
+
export type SpaceOccupant = {
|
|
190
|
+
readonly incarnation: string;
|
|
191
|
+
readonly fence: SpaceCommitFence;
|
|
192
|
+
} | {
|
|
193
|
+
readonly unread: "incarnation_unknown";
|
|
194
|
+
} | {
|
|
195
|
+
readonly fault: string;
|
|
196
|
+
};
|
|
197
|
+
/** One failed stage of {@link RemoteK8sExecutionEnv.reclaimSpaceSnapshots}: whose partition (the space's `space/` or the park lane's), listing it, deleting from it,
|
|
198
|
+
* or the reclaim call's deadline passing before or while on it (`budget`: the rest was not attempted). */
|
|
199
|
+
export interface ReclaimFailure {
|
|
200
|
+
readonly lane: "space" | "park";
|
|
201
|
+
readonly stage: "list" | "delete" | "budget";
|
|
202
|
+
readonly detail: string;
|
|
203
|
+
}
|
|
204
|
+
/** The failed cache-volume half of a reclaim ({@link RemoteK8sExecutionEnv.reclaimSpaceCache}); `reason` is the closed word set of
|
|
205
|
+
* `workspace_cache_reclaim_failed` from the reclaim seat: the delete-by-label failed or answered non-2xx (`delete_failed`), or the reclaim call's
|
|
206
|
+
* deadline passed before it was sent or while it was in flight (`budget` — the same word the object half's `stage` uses for the same cause). */
|
|
207
|
+
export interface CacheReclaimFailure {
|
|
208
|
+
readonly reason: Exclude<CacheReclaimReason, "occupant_unreadable">;
|
|
209
|
+
readonly detail: string;
|
|
210
|
+
}
|
|
153
211
|
export { execSocketTlsOptions } from "./k8s-exec-protocol.js";
|
|
154
212
|
/** `Error`-or-event → message. Bun's ws shim emits browser-style ErrorEvents (NOT instanceof Error) whose
|
|
155
213
|
* String() is "[object ErrorEvent]" — that masked the real TLS failure for a whole drill. */
|
|
156
214
|
export declare function errorMessageOf(e: unknown): string;
|
|
157
215
|
export { exitCodeFromStatus } from "./k8s-exec-protocol.js";
|
|
216
|
+
/** The sandbox namespace when `K8S_NAMESPACE` is unset (the one spelling: the adapter and the boot's egress line read it). */
|
|
217
|
+
export declare const K8S_SANDBOX_NAMESPACE_DEFAULT = "sandboxes";
|
|
158
218
|
export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, BackgroundShellCapability {
|
|
159
219
|
/**
|
|
160
220
|
* 🔴 **S-354 / core 7.20.0([ref])`canonicalPathAuthoritative: true`** —— 本适配器的 `canonicalPath`
|
|
@@ -183,14 +243,30 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
183
243
|
private inFlightExecs;
|
|
184
244
|
/** Memoized in-flight connect (lazy-connect; prevents concurrent double pod-create — the E2B race lesson). */
|
|
185
245
|
private connectPromise?;
|
|
246
|
+
/** The space restore's refusal: `latest` names an archive that is there and did not come back. A fact of this env until `destroy` —
|
|
247
|
+
* written once, by {@link restoreSnapshot} at the restore's end; read by {@link connect} alone (the lazy and the explicit connect alike),
|
|
248
|
+
* which answers it to every later connect: the same refusal, zero pods, zero store requests, no second line. */
|
|
249
|
+
private restoreRefusal?;
|
|
186
250
|
/** core `ExecutionEnv.homeDir`([ref]):本沙箱/目标机上执行用户的 home。见配置同名字段。 */
|
|
187
251
|
readonly homeDir?: string | undefined;
|
|
252
|
+
/** core 7.49.0 #1554:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在 pod 里(镜像与 pod spec 的 env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
|
|
253
|
+
* 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
|
|
254
|
+
readonly cdPath: "unknown";
|
|
188
255
|
private readonly cfg;
|
|
189
256
|
private readonly deps;
|
|
257
|
+
/** The known occupant's incarnation (`undefined` = this run has no known occupant: a delegated child, an unread session, a direct construction) — the
|
|
258
|
+
* ownership mark of every snapshot id this env mints, both lanes ({@link mintSnapshotId}); the space lane's partition is the same reading. */
|
|
259
|
+
private readonly occupantIncarnation;
|
|
190
260
|
/** This env's space lane (resolved once from the policy + the occupant's incarnation; see {@link SpaceLane}). */
|
|
191
261
|
private readonly spaceLane;
|
|
192
262
|
/** `workspace_space_unavailable` is said once per env. */
|
|
193
263
|
private spaceUnavailableSaid;
|
|
264
|
+
/** `workspace_space_unreadable_occupant` is said once per env (a retried lazy connect is refused again, silently). */
|
|
265
|
+
private unreadableOccupantSaid;
|
|
266
|
+
/** This env's cache volume lane (resolved once from the cache policy + the space key; see {@link CacheLane}). */
|
|
267
|
+
private readonly cacheLane;
|
|
268
|
+
/** A cache volume that could not be had is counted and said once per env (a retried connect does not count again). */
|
|
269
|
+
private cacheUnavailableSaid;
|
|
194
270
|
private readonly logger?;
|
|
195
271
|
private readonly onWorkspaceSnapshot?;
|
|
196
272
|
constructor(config: K8sEnvConfig, deps?: K8sEnvDeps);
|
|
@@ -208,29 +284,97 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
208
284
|
private openExec;
|
|
209
285
|
/**
|
|
210
286
|
* Connect = the workspace SPACE's restore point. Space lane open ⇒ first wait out a commit still in flight
|
|
211
|
-
* ({@link awaitSpaceCommit}), then read `<prefix>/<incarnation>/space/latest`: present ⇒
|
|
212
|
-
* restore it through {@link restoreSnapshot} (the one tar download, shared with `resumeVM`;
|
|
213
|
-
*
|
|
214
|
-
* `connect_failed` — never a silent fresh workspace (a run that believes it resumed while it did not is
|
|
215
|
-
*
|
|
216
|
-
*
|
|
287
|
+
* ({@link awaitSpaceCommit}: the run store's commit row), then read `<prefix>/<incarnation>/space/latest`: present ⇒
|
|
288
|
+
* restore it through {@link restoreSnapshot} (the one tar download, shared with `resumeVM`; an archive that is gone ⇒
|
|
289
|
+
* loud `workspace_latest_dangling`, the pointer deleted, an empty workspace); absent (404) ⇒ a fresh pod; a read that
|
|
290
|
+
* FAILS ⇒ a loud `connect_failed` — never a silent fresh workspace (a run that believes it resumed while it did not is
|
|
291
|
+
* the worst form). Once the pod holds the workspace, this env arms its fence seat (its destroy will commit).
|
|
292
|
+
* Persistence off / no store ⇒ a fresh pod, exactly as before. Persistence on but no such session (the occupant absent) ⇒ a
|
|
293
|
+
* fresh pod and one `workspace_space_unavailable` line (no restore by any other key). The occupant read FAULTED ⇒ the same
|
|
294
|
+
* loud `connect_failed` as a failed `latest` read, no pod ({@link provision} refuses it — every entry that would create one).
|
|
295
|
+
* One restore per env: a restore that ended refused ({@link restoreRefusal}) is answered as it stands to every later connect — the
|
|
296
|
+
* previous commit is not waited on again, `latest` is not read again, no pod is created.
|
|
217
297
|
*/
|
|
218
298
|
connect(config?: RemoteConnectConfig): Promise<ConnectResult>;
|
|
219
|
-
/**
|
|
299
|
+
/** This env holds a workspace its `destroy()` will commit ⇒ arm its seat on the space's commit fence (from here the
|
|
300
|
+
* run's terminal write fences the space; `workspace-space-commit.ts`). Each write of this run's row sets its deadline to
|
|
301
|
+
* now + the upload's own wall-clock bound (`dataTimeoutMs`). No-op unless the space lane is open. */
|
|
302
|
+
private armSpace;
|
|
303
|
+
/** The env no longer holds a workspace to commit (a commit ended, a park released the pod, or destroy found no pod) ⇒
|
|
304
|
+
* end its fence seat: the row this run wrote, if any, is deleted. A delete that fails leaves the row until its deadline —
|
|
305
|
+
* the next run waits at most that long and says `workspace_commit_abandoned` (the loud form of a stuck row). */
|
|
306
|
+
private endSpace;
|
|
307
|
+
/**
|
|
308
|
+
* One call to the space's commit fence (the run store), bounded like every other request this adapter makes
|
|
309
|
+
* (`rpcTimeoutMs`) and abortable: a store that never answers never hangs connect or destroy. The store call itself is
|
|
310
|
+
* not cancellable — a late answer is dropped; what it may have written dies at its own deadline (the fence's one rule).
|
|
311
|
+
* Rejects with {@link FenceCallAborted} on abort.
|
|
312
|
+
*/
|
|
313
|
+
private fenceCall;
|
|
314
|
+
/** The space lane is on but this run's session has no occupant (no such session — {@link SpaceOccupant}) — said once per env
|
|
315
|
+
* (at connect, or at a destroy with a pod). `reason` = the reading's word. */
|
|
220
316
|
private sayUnavailable;
|
|
317
|
+
/**
|
|
318
|
+
* A lane that is on is keyed by an occupant read that FAULTED (`unreadable`) ⇒ this env creates no pod: the refusal, in the one
|
|
319
|
+
* space-read failure form ({@link spaceReadFailed} — a failed `latest` / commit-row read says the same), naming the knob of every
|
|
320
|
+
* lane that refuses; one `workspace_space_unreadable_occupant{sessionId, code}` line per env. `code` = the store error's code,
|
|
321
|
+
* never its text. Not a knob: there is no honest way to run a space we could not read — a retry of the run reads it again.
|
|
322
|
+
*/
|
|
323
|
+
private refuseUnreadableOccupant;
|
|
221
324
|
/** Every space-lane operator line: the space is named by its incarnation's log prefix only, and free text (a curl
|
|
222
325
|
* stderr, a fetch error) passes {@link redactIncarnations} — the full token never reaches a log body. */
|
|
223
326
|
private spaceLine;
|
|
224
|
-
/** A fresh pod (create → Running → workspace root), bounded retry
|
|
327
|
+
/** A fresh pod (create → Running → workspace root), bounded retry — THE seat every pod of this env is created through
|
|
328
|
+
* ({@link connect} and, via {@link restoreSnapshot}, {@link resumeVM}). A lane keyed by an occupant read that faulted ⇒ refused
|
|
329
|
+
* here, before any request ({@link refuseUnreadableOccupant}): never retried (the reading is this env's, fixed at construction). */
|
|
225
330
|
private provision;
|
|
226
331
|
/**
|
|
227
332
|
* Lazy-connect on first use — 🔴 core NEVER calls `connect()` (verified: the Runner only calls `resumeVM` on
|
|
228
333
|
* the resume path), so an adapter that demands an explicit connect can never work through the production
|
|
229
334
|
* hand-tool path (lazy-connect belongs in the adapter). Memoized so concurrent first ops share ONE
|
|
230
|
-
* pod create;
|
|
335
|
+
* pod create; `destroyed` is terminal. A failed connect clears the memo — how long a failure stands is {@link connect}'s, not
|
|
336
|
+
* this memo's: a failure of that attempt (a pod that could not be created, a space read that failed) ⇒ the next op connects
|
|
337
|
+
* afresh; the space restore's refusal is the env's ({@link restoreRefusal}) ⇒ the next op's connect answers that same refusal at once.
|
|
231
338
|
*/
|
|
232
339
|
private ensureConnected;
|
|
340
|
+
/**
|
|
341
|
+
* One pod creation attempt. With the cache volume open: first the space's claim ({@link attachCache} — found, or created),
|
|
342
|
+
* then the pod carrying it; a pod that only waits on a claim that does not bind within the data bound is deleted and ONE pod
|
|
343
|
+
* without the volume is created in its place (the run starts; the miss is counted). Every other failure is the attempt's.
|
|
344
|
+
*/
|
|
233
345
|
private connectOnce;
|
|
346
|
+
/** The cache volume for this pod creation, or `undefined` (off, or not to be had — counted and said there). */
|
|
347
|
+
private attachCache;
|
|
348
|
+
/** Find the space's claim, or create it (labelled with the space digest; size = the policy's; the cluster's default
|
|
349
|
+
* StorageClass). `fresh` = this call created it. A concurrent creator (409) is a find. Any other answer is a miss. */
|
|
350
|
+
private ensureCacheClaim;
|
|
351
|
+
/** Is the claim bound? (A read that fails counts as not bound — the bind deadline still decides.) */
|
|
352
|
+
private cacheClaimBound;
|
|
353
|
+
/**
|
|
354
|
+
* Reclaim one ended space's cache volume: delete the claims labelled `sema.space=<its digest>` (one deletecollection —
|
|
355
|
+
* a claim still mounted by a terminating pod is kept by the cluster's pvc-protection until the pod is gone). None ⇒ zero
|
|
356
|
+
* action. The request runs under the reclaim call's `deadline` (built once per call by {@link createSpaceReclaimer}): already passed ⇒
|
|
357
|
+
* nothing is sent; passing while the request is in flight ⇒ the request is cut (its own `rpcTimeoutMs` stays as this control-plane request's socket-idle bound; the object half's `storeFetch` bound is a whole-request wall clock — the deadline is what bounds both). Answers the outcome; never throws. Used by {@link createSpaceReclaimer} only (a control-plane env, no pod).
|
|
358
|
+
*/
|
|
359
|
+
reclaimSpaceCache(spaceKey: string, deadline: AbortSignal): Promise<{
|
|
360
|
+
ok: true;
|
|
361
|
+
deleted: number;
|
|
362
|
+
} | ({
|
|
363
|
+
ok: false;
|
|
364
|
+
} & CacheReclaimFailure)>;
|
|
365
|
+
/**
|
|
366
|
+
* The egress self-check's two reads (`GET` nodes, `GET` the sandbox namespace's NetworkPolicies) judged by the one rule
|
|
367
|
+
* ({@link sandboxEgressGaps}) against the labels this env mints on its pods (base identity + the pod-spec patch's additions, the
|
|
368
|
+
* same merge `createPod` applies). A read that is refused / fails / times out is a `unreadable` gap, never a guess. Never throws.
|
|
369
|
+
*/
|
|
370
|
+
egressPolicyGaps(): Promise<SandboxEgressGap[]>;
|
|
371
|
+
/** The one spelling of "the reclaim call's deadline passed" — both halves of the reclaim (volume and objects) say it with this. */
|
|
372
|
+
private reclaimDeadlinePassed;
|
|
373
|
+
/** The cache volume could not be had: counted (`recordFailOpen`) and one operator line — once per env. */
|
|
374
|
+
private cacheUnavailable;
|
|
375
|
+
/** Create one pod (with the cache volume when given) and wait for it to run. A {@link CacheMiss} = a pod carrying the
|
|
376
|
+
* volume did not become ready (its claim unbound past the bind deadline, or bound and still not ready) — already deleted. */
|
|
377
|
+
private createPod;
|
|
234
378
|
/**
|
|
235
379
|
* Build a validated, presigned URL for a snapshot object of either lane (see {@link SnapshotTarget}) — the ONE place a
|
|
236
380
|
* snapshot object key is spelled, and the one place each lane's partition is chosen (park = the session id, space = the
|
|
@@ -243,6 +387,30 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
243
387
|
* snapshotKey()+s3Presign() pair so the two call sites can't diverge.
|
|
244
388
|
*/
|
|
245
389
|
private presignSnapshot;
|
|
390
|
+
/** A lane's partition prefix inside the bucket — the ONE place it is spelled (`presignSnapshot` signs under it; the reclaim reads the listing against it).
|
|
391
|
+
* The partition segment is validated by `presignSnapshot` before a URL is signed. */
|
|
392
|
+
private snapshotBase;
|
|
393
|
+
/**
|
|
394
|
+
* Reclaim an ENDED space's snapshot objects (the reclaim seat's half of {@link createSpaceReclaimer}): list the whole `space/` partition
|
|
395
|
+
* of `spaceKey` (archives, `latest`, anything else this adapter ever wrote there) and, when this env is keyed by a session
|
|
396
|
+
* (`cfg.sessionId`), the park lane's `<prefix>/<sessionId>/*.tar.gz` whose names carry the ended occupant's ownership mark (see the two
|
|
397
|
+
* paragraphs below: unmarked / `none` objects stay) — the session is gone, so nothing can ever read either again.
|
|
398
|
+
* Each object is deleted over the same presigned DELETE the prune uses. Never throws: answers the failed stages (an empty answer = all
|
|
399
|
+
* gone, or nothing was there). An object whose name this adapter could not have written, or that sits deeper than the lane's own
|
|
400
|
+
* partition, is left alone. A truncated listing (more than 1000 objects) deletes what it saw and says so.
|
|
401
|
+
* Every LIST and DELETE of both partitions runs under the reclaim call's `deadline` — ONE absolute deadline per call of {@link createSpaceReclaimer}, built at its
|
|
402
|
+
* entry from `dataTimeoutMs` (the snapshot time bound; no knob of its own) and shared by every space of the call and by the cache-volume half; this method builds
|
|
403
|
+
* none of its own. Passed before this space is reached ⇒ nothing is sent; passing on the way ⇒ the request in flight is cut and nothing further is sent. Either
|
|
404
|
+
* way the answer carries one `stage: "budget"` failure for the lane it stopped on: what was not yet deleted stays for the bucket's lifecycle rule. (Each request
|
|
405
|
+
* also keeps its own `rpcTimeoutMs`; the deadline is what bounds their sum.)
|
|
406
|
+
* The park partition is keyed by the SESSION ID, which another tenant may register again once the old row is gone — and a reclaim can run
|
|
407
|
+
* late (a retention sweep walks many sessions). So the park lane deletes only objects whose NAME carries the ended occupant's ownership mark
|
|
408
|
+
* ({@link snapshotNameOwnedBy}; the mark is minted into every snapshot id by {@link mintSnapshotId}, `uploadSnapshot`): a later occupant's
|
|
409
|
+
* objects carry its own mark, and a name with no mark (written before marks existed, or by a run with no occupant) is nobody's to delete here —
|
|
410
|
+
* the bucket's lifecycle rule is its fallback. The judgment reads NO clock (not the object store's stamp, not this replica's). The `space/`
|
|
411
|
+
* partition needs no such guard (its partition is the incarnation, which a new occupant does not share).
|
|
412
|
+
*/
|
|
413
|
+
reclaimSpaceSnapshots(spaceKey: string, deadline: AbortSignal): Promise<ReclaimFailure[]>;
|
|
246
414
|
/** Worker-side object-store request, bounded by the control-plane RPC wall-clock — or a tighter caller deadline — and
|
|
247
415
|
* the caller's abort. */
|
|
248
416
|
private storeFetch;
|
|
@@ -256,12 +424,20 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
256
424
|
* caller deadline tighter than the RPC bound. */
|
|
257
425
|
private writePointer;
|
|
258
426
|
/**
|
|
259
|
-
* Wait out the previous run's space commit
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
427
|
+
* Wait out the previous run's space commit — its row in the run store's `workspace_space_commit` (the ONE seat of
|
|
428
|
+
* "a commit of this space is in flight", `workspace-space-commit.ts`; written by that run's terminal write before the
|
|
429
|
+
* session's slot was released, so a next run can never start before it). The row carries its WRITER's deadline; this
|
|
430
|
+
* reader brings no number of its own: alive (`spaceCommitAlive`) ⇒ re-read every {@link SPACE_COMMIT_POLL_MS} until the
|
|
431
|
+
* row is gone. Read dead = a run whose worker died between its terminal write and its destroy (state `pending`), inside
|
|
432
|
+
* the upload, or while moving `latest` — or a writer whose renewal lands at this very moment. So the read alone
|
|
433
|
+
* decides nothing: the reader ABANDONS the row ({@link SpaceCommitFence.abandon} — one locked conditional delete, judged
|
|
434
|
+
* on the store's clock under the row's lock). Deleted ⇒ one `workspace_commit_abandoned{space, remainingMs, state}` line
|
|
435
|
+
* (`remainingMs` ≤ 0: how far past the writer's deadline, as read) and `latest` (still the previous COMPLETE snapshot)
|
|
436
|
+
* is restored; not deleted (renewed / taken / gone) ⇒ re-read at once. A row read or an abandon that fails is a loud
|
|
437
|
+
* `connect_failed` (never a guess that no commit is in flight).
|
|
438
|
+
* How long connect may wait here has no bound of its own (no knob, and NOT this replica's `dataTimeoutMs`): it is the
|
|
439
|
+
* deadline the writer keeps in its row — each of that run's at most three writes (terminal, commit start, publish) sets
|
|
440
|
+
* it to now + the writer's bound — plus the run's abort signal, which ends the wait at once.
|
|
265
441
|
*/
|
|
266
442
|
private awaitSpaceCommit;
|
|
267
443
|
/**
|
|
@@ -292,7 +468,9 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
292
468
|
}>;
|
|
293
469
|
/**
|
|
294
470
|
* The ONE tar upload (both lanes): tar the workspace to a pod-local file, presigned PUT into the lane's namespace,
|
|
295
|
-
* release the pod. `suspendVM` = lane `park`; the run-end space snapshot = lane `space` (its open space).
|
|
471
|
+
* release the pod. `suspendVM` = lane `park`; the run-end space snapshot = lane `space` (its open space). The exclusion
|
|
472
|
+
* table (`snapshotExclude`) is applied HERE and nowhere else — each item one `--exclude=./<item>` argument through the one
|
|
473
|
+
* `shellQuote`, anchored at the workspace root (`-C <mount> .` names every member `./…`); no table ⇒ the command as it always was.
|
|
296
474
|
*/
|
|
297
475
|
private uploadSnapshot;
|
|
298
476
|
/**
|
|
@@ -309,12 +487,34 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
309
487
|
resumeVM(snapshotId: SnapshotId, options?: VmLifecycleOptions): Promise<ConnectResult>;
|
|
310
488
|
/**
|
|
311
489
|
* The ONE tar download (both lanes): provision a FRESH pod, download + untar the lane's archive into the workspace.
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
490
|
+
* A missing archive (404) starts from the clean workspace on both lanes — what differs is what it means:
|
|
491
|
+
* - park: a plan-only suspend has no object — nothing to say;
|
|
492
|
+
* - space: `latest` dangles (its pointer is only written after a successful upload, so the archive was removed later —
|
|
493
|
+
* by the bucket's lifecycle rule or a hand): the data is gone by an operator rule ⇒ loud and continuable —
|
|
494
|
+
* {@link dropDanglingLatest} (one `workspace_latest_dangling` line, the pointer deleted); the run's own run-end
|
|
495
|
+
* snapshot then becomes the new `latest`.
|
|
496
|
+
* An attempt that does not complete releases the pod it provisioned: no tool and no run-end snapshot may ever see a torn
|
|
497
|
+
* workspace. A transient class ({@link restoreFailureIsTransient}) is tried again on a fresh pod after the `retry.backoffMs`
|
|
498
|
+
* pause, up to the lane's {@link RESTORE_MAX_ATTEMPTS}; a non-transient one ends the restore at once. The lane then says what
|
|
499
|
+
* the end means:
|
|
500
|
+
* - park: a plain `connect_failed` — its caller retries; nothing said, nothing held;
|
|
501
|
+
* - space: the archive `latest` names is THERE and did not come back ({@link RestoreFailureClass}): the run is refused all the
|
|
502
|
+
* same — never an empty workspace (the user's changes are still in the bucket) — with exactly one `workspace_restore_failed`
|
|
503
|
+
* line (its `attempts`; an attempt that failed before one that succeeded is no line) and a refusal naming the two ways out,
|
|
504
|
+
* held for the env ({@link restoreRefusal}).
|
|
505
|
+
* 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
|
|
506
|
+
* next connect tries again. An abort during the pause ends the restore `aborted` (not the archive's either).
|
|
316
507
|
*/
|
|
317
508
|
private restoreSnapshot;
|
|
509
|
+
/** The pause before a restore's next attempt (`retry.backoffMs`, the adapter's one retry pause). `false` ⇒ aborted (now or during it). */
|
|
510
|
+
private restorePause;
|
|
511
|
+
/**
|
|
512
|
+
* `latest` names an archive that is not there (S-1438): one `workspace_latest_dangling{space, key, deleted}` line and the
|
|
513
|
+
* pointer is deleted (the next run must not trip on it again); the run continues on the empty workspace. A pointer
|
|
514
|
+
* delete that fails rides on the same line (`deleted: false` + the error) — the next run then says it once more.
|
|
515
|
+
* This env is the space's only writer here (the commit fence admits one per space), so the delete is a plain one.
|
|
516
|
+
*/
|
|
517
|
+
private dropDanglingLatest;
|
|
318
518
|
/**
|
|
319
519
|
* Honest post-resume consistency ([ref] §5 council #8): the workspace FILES are restored byte-for-byte
|
|
320
520
|
* from the snapshot, so read-before-edit hashing already lines up — there's nothing to re-fetch. In-VM
|
|
@@ -334,15 +534,18 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
334
534
|
* Delete the sandbox pod. Idempotent, never throws. Space lane open and a pod exists ⇒ FIRST the run-end space
|
|
335
535
|
* snapshot ({@link persistWorkspace}); its failure is loud and never blocks the delete (the pod lifetime is the
|
|
336
536
|
* leak guard). A run that never connected has no workspace to save — `latest` stays where it was. Space lane
|
|
337
|
-
* unavailable (
|
|
537
|
+
* unavailable (no such session) ⇒ nothing is saved (the once-per-env line says so); unreadable ⇒ there is no pod
|
|
538
|
+
* ({@link provision} refused every creation).
|
|
338
539
|
*/
|
|
339
540
|
destroy(): Promise<void>;
|
|
340
541
|
cleanup(): Promise<void>;
|
|
341
542
|
/**
|
|
342
|
-
* Run-end space snapshot
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
543
|
+
* Run-end space snapshot, under the space's commit fence (the run store's row, `workspace-space-commit.ts`): the fence
|
|
544
|
+
* first (this run's row moves to its upload state; lost ⇒ no upload, loud) → the one tar upload ({@link uploadSnapshot},
|
|
545
|
+
* lane `space` — it also releases the pod) → the fence again (this run's row moves to its publishing state; lost ⇒
|
|
546
|
+
* `latest` is not published, loud) → move `latest` → keep N → the row is deleted (always, saved or failed).
|
|
547
|
+
* A lost fence, an upload or a pointer failure ⇒ one `workspace_snapshot_failed` line + the `failed` outcome; `latest`
|
|
548
|
+
* keeps naming the previous snapshot, so the next run restores THAT — never an empty workspace dressed up as a restore.
|
|
346
549
|
*/
|
|
347
550
|
private persistWorkspace;
|
|
348
551
|
private execUrl;
|
|
@@ -457,6 +660,26 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
|
|
|
457
660
|
* 车PL:buffered `exec` 上守卫触发 ⇒ `spawn_error`)。 */
|
|
458
661
|
private workingDirOf;
|
|
459
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* The ONE reclaim seat for an ended space ({@link SpaceReclaimer}) — called where a space ENDS (the session-delete coordinator and the
|
|
665
|
+
* managed-retention sweep). For each ended space it reclaims everything the space left behind, over this deployment's k8s config (it
|
|
666
|
+
* never creates a pod): its build-cache volume (the claims labelled by the space key; only when the cache volume is configured),
|
|
667
|
+
* its snapshot objects — the `space/` partition (archives + `latest`) and the park lane's objects in `<prefix>/<sessionId>/` that carry the ended occupant's mark (only when a
|
|
668
|
+
* snapshot store is configured). Each part is independent: one part failing never skips another. A failed part ⇒ one operator line
|
|
669
|
+
* (`workspace_cache_reclaim_failed{reason}` for the volume, `workspace_space_reclaim_failed{stage}` for the objects) + `recordFailOpen`; never throws.
|
|
670
|
+
* ONE call = ONE absolute deadline (the snapshot bound, `dataTimeoutMs`; no knob of its own), built at the entry and nowhere else: every request of the call — each
|
|
671
|
+
* space's cache-volume delete and every LIST / DELETE of its objects — runs under it. A session delete calls with its one space; the managed-retention sweep calls
|
|
672
|
+
* once per domain per round with the spaces that round ended there. When the deadline passes the request in flight is cut and every space not yet done is still
|
|
673
|
+
* walked but sends nothing: its volume says `reason: "budget"`, its objects `stage: "budget"` (the volume stays for deletion by label, the objects for the
|
|
674
|
+
* bucket's lifecycle rule). A caller that must not wait for the seat (the session-delete answer) starts it and does not await it.
|
|
675
|
+
*/
|
|
676
|
+
export declare function createSpaceReclaimer(config: K8sEnvConfig, deps?: K8sEnvDeps): SpaceReclaimer;
|
|
677
|
+
/**
|
|
678
|
+
* The k8s lane's boot-time egress self-check (once per boot, never holds the boot): the gaps between the sandbox pods' egress
|
|
679
|
+
* policy and what the cluster API shows (`sandbox-egress-policy.ts` — the closed set and the rule). A reading, not the boundary:
|
|
680
|
+
* nodes that join after boot are read at the next boot. Never throws (a read failure is an `unreadable` gap).
|
|
681
|
+
*/
|
|
682
|
+
export declare function checkSandboxEgressPolicy(config: K8sEnvConfig, deps?: K8sEnvDeps): Promise<SandboxEgressGap[]>;
|
|
460
683
|
/**
|
|
461
684
|
* `ExecutionEnvFactory` for the k8s pod-sandbox backend — one fresh (unconnected) env per task; the pod is
|
|
462
685
|
* created lazily on connect and deleted by `destroy()` (Runner-owned lifetime; `activeDeadlineSeconds` is the
|