@fitlab-ai/agent-infra 0.12.0 → 0.12.2
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/README.md +0 -2
- package/README.zh-CN.md +0 -2
- package/dist/bin/internal-cli.js +2 -17
- package/dist/lib/agent-clients/adapter.js +0 -13
- package/dist/lib/agent-clients/adapters/antigravity-cli.js +0 -5
- package/dist/lib/agent-clients/adapters/claude-code-credentials.js +0 -17
- package/dist/lib/agent-clients/adapters/claude-code.js +0 -9
- package/dist/lib/agent-clients/adapters/codex.js +0 -52
- package/dist/lib/agent-clients/adapters/opencode.js +0 -5
- package/dist/lib/agent-clients/adapters/traecli.js +0 -5
- package/dist/lib/agent-clients/config.js +19 -54
- package/dist/lib/agent-clients/registry.js +1 -12
- package/dist/lib/agent-clients/schema.js +0 -18
- package/dist/lib/agent-clients/tokens.js +1 -2
- package/dist/lib/agent-clients/types.js +0 -1
- package/dist/lib/decide.js +2 -0
- package/dist/lib/git/worktree-identity.js +1 -9
- package/dist/lib/internal/agent-client.js +2 -62
- package/dist/lib/internal/cli-route-inventory.js +1 -5
- package/dist/lib/internal/manual-validation.js +29 -27
- package/dist/lib/internal/platform-comment.js +56 -7
- package/dist/lib/internal/task-create.js +1 -1
- package/dist/lib/internal/task-event.js +1 -8
- package/dist/lib/internal/task-finalization.js +0 -1
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +4 -20
- package/dist/lib/internal/task-review.js +4 -4
- package/dist/lib/platform/comment-safety.js +1 -7
- package/dist/lib/platform/in-label-sync.js +1 -14
- package/dist/lib/platform/issue-comments.js +373 -282
- package/dist/lib/platform/issue-summary-content.js +112 -0
- package/dist/lib/platform/issues.js +1 -1
- package/dist/lib/platform/operation-coordinator.js +4 -2
- package/dist/lib/platform/pr-change-report.js +176 -30
- package/dist/lib/platform/pr-checks.js +2 -8
- package/dist/lib/platform/pr-summary-content.js +54 -0
- package/dist/lib/platform/pr-summary.js +318 -284
- package/dist/lib/platform/provider-validation.js +0 -20
- package/dist/lib/platform/pull-requests.js +73 -84
- package/dist/lib/platform/releases.js +1 -43
- package/dist/lib/platform/summary-content-pipeline.js +18 -0
- package/dist/lib/platform/verification-required.js +0 -2
- package/dist/lib/platform/verification-sync.js +1 -79
- package/dist/lib/process-data/normalize.js +1 -185
- package/dist/lib/process-data/privacy.js +1 -16
- package/dist/lib/process-data/sources.js +2 -279
- package/dist/lib/render.js +0 -12
- package/dist/lib/run/index.js +15 -10
- package/dist/lib/run/skills.js +0 -3
- package/dist/lib/sandbox/commands/create.js +40 -53
- package/dist/lib/sandbox/commands/list-running.js +0 -18
- package/dist/lib/sandbox/commands/start.js +7 -8
- package/dist/lib/sandbox/constants.js +0 -6
- package/dist/lib/sandbox/control/client.js +0 -1
- package/dist/lib/sandbox/control/container-identity.js +1 -16
- package/dist/lib/sandbox/control/executor.js +0 -4
- package/dist/lib/sandbox/control/identity-sentinel.js +0 -7
- package/dist/lib/sandbox/control/lifecycle.js +21 -37
- package/dist/lib/sandbox/control/native-file-lock.js +0 -3
- package/dist/lib/sandbox/control/server.js +18 -38
- package/dist/lib/sandbox/control/state.js +1 -9
- package/dist/lib/sandbox/dockerfile.js +0 -6
- package/dist/lib/sandbox/engine.js +0 -14
- package/dist/lib/sandbox/engines/docker-exact-discovery.js +1 -4
- package/dist/lib/sandbox/engines/wsl2-paths.js +0 -3
- package/dist/lib/sandbox/recovery.js +24 -26
- package/dist/lib/sandbox/removal.js +67 -518
- package/dist/lib/sandbox/task-resolver.js +0 -41
- package/dist/lib/sandbox/tools.js +1 -4
- package/dist/lib/sandbox/workspace-view.js +1 -2
- package/dist/lib/server/display.js +0 -6
- package/dist/lib/task/activity-intent.js +45 -40
- package/dist/lib/task/activity-log.js +9 -16
- package/dist/lib/task/artifact-command.js +1 -1
- package/dist/lib/task/artifact-lifecycle.js +61 -63
- package/dist/lib/task/artifact-name.js +0 -6
- package/dist/lib/task/artifact-operations.js +3 -3
- package/dist/lib/task/capabilities.js +4 -5
- package/dist/lib/task/commands/log.js +0 -6
- package/dist/lib/task/commands/status.js +17 -60
- package/dist/lib/task/commit-operation.js +28 -39
- package/dist/lib/task/completion-facts.js +56 -43
- package/dist/lib/task/control-authority.js +23 -256
- package/dist/lib/task/control-recovery.js +3 -5
- package/dist/lib/task/create.js +2 -4
- package/dist/lib/task/decision-intents.js +8 -9
- package/dist/lib/task/delivery-target.js +1 -40
- package/dist/lib/task/events.js +84 -107
- package/dist/lib/task/finalization.js +60 -15
- package/dist/lib/task/ledger-intents.js +1 -1
- package/dist/lib/task/lifecycle-execution.js +38 -33
- package/dist/lib/task/lifecycle.js +49 -41
- package/dist/lib/task/local-artifact-finalization.js +1 -4
- package/dist/lib/task/manual-validation-evidence.js +1 -37
- package/dist/lib/task/platform-operation-recovery.js +151 -108
- package/dist/lib/task/pr-delivery-fact.js +2 -5
- package/dist/lib/task/qualification-confirmation.js +5 -7
- package/dist/lib/task/resolve-ref.js +1 -25
- package/dist/lib/task/review-command.js +2 -2
- package/dist/lib/task/review-finalization.js +1 -21
- package/dist/lib/task/review-fingerprint.js +0 -4
- package/dist/lib/task/secure-fs.js +0 -56
- package/dist/lib/task/short-id.js +1 -39
- package/dist/lib/task/skill-scope.js +0 -1
- package/dist/lib/task/verification-engine.js +58 -282
- package/dist/lib/task/verification.js +6 -3
- package/dist/lib/task/write.js +11 -1
- package/lib/agent-clients/adapter.ts +0 -76
- package/lib/agent-clients/adapters/antigravity-cli.ts +0 -5
- package/lib/agent-clients/adapters/claude-code-credentials.ts +0 -19
- package/lib/agent-clients/adapters/claude-code.ts +0 -9
- package/lib/agent-clients/adapters/codex.ts +0 -56
- package/lib/agent-clients/adapters/opencode.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +0 -5
- package/lib/agent-clients/config.ts +20 -61
- package/lib/agent-clients/registry.ts +1 -34
- package/lib/agent-clients/schema.ts +0 -18
- package/lib/agent-clients/tokens.ts +1 -2
- package/lib/agent-clients/types.ts +0 -15
- package/lib/decide.ts +1 -0
- package/lib/git/worktree-identity.ts +0 -10
- package/lib/internal/agent-client.ts +2 -70
- package/lib/internal/cli-route-inventory.ts +1 -4
- package/lib/internal/manual-validation.ts +28 -27
- package/lib/internal/platform-comment.ts +46 -4
- package/lib/internal/task-create.ts +0 -1
- package/lib/internal/task-event.ts +1 -5
- package/lib/internal/task-finalization.ts +0 -1
- package/lib/internal/task-lifecycle.ts +3 -5
- package/lib/internal/task-operation-registry.ts +3 -19
- package/lib/internal/task-review.ts +4 -4
- package/lib/platform/comment-safety.ts +0 -10
- package/lib/platform/github-provider.ts +0 -2
- package/lib/platform/in-label-sync.ts +0 -16
- package/lib/platform/issue-comments.ts +478 -301
- package/lib/platform/issue-summary-content.ts +132 -0
- package/lib/platform/issues.ts +0 -2
- package/lib/platform/operation-coordinator.ts +4 -2
- package/lib/platform/pr-change-report.ts +165 -25
- package/lib/platform/pr-checks.ts +1 -10
- package/lib/platform/pr-review.ts +1 -1
- package/lib/platform/pr-summary-content.ts +60 -0
- package/lib/platform/pr-summary.ts +364 -257
- package/lib/platform/provider-bridge.ts +0 -1
- package/lib/platform/provider-loader.ts +1 -5
- package/lib/platform/provider-validation.ts +1 -49
- package/lib/platform/pull-requests.ts +64 -99
- package/lib/platform/releases.ts +1 -40
- package/lib/platform/summary-content-pipeline.ts +23 -0
- package/lib/platform/verification-required.ts +0 -2
- package/lib/platform/verification-sync.ts +0 -94
- package/lib/process-data/normalize.ts +1 -186
- package/lib/process-data/privacy.ts +2 -19
- package/lib/process-data/sources.ts +3 -283
- package/lib/render.ts +0 -15
- package/lib/run/index.ts +13 -7
- package/lib/run/skills.ts +0 -4
- package/lib/sandbox/commands/create.ts +37 -74
- package/lib/sandbox/commands/list-running.ts +0 -19
- package/lib/sandbox/commands/start.ts +7 -10
- package/lib/sandbox/constants.ts +0 -8
- package/lib/sandbox/control/client.ts +0 -1
- package/lib/sandbox/control/container-identity.ts +1 -16
- package/lib/sandbox/control/executor.ts +1 -5
- package/lib/sandbox/control/identity-sentinel.ts +0 -8
- package/lib/sandbox/control/lifecycle.ts +18 -51
- package/lib/sandbox/control/native-file-lock.ts +0 -8
- package/lib/sandbox/control/protocol.ts +0 -2
- package/lib/sandbox/control/server.ts +13 -39
- package/lib/sandbox/control/state.ts +1 -10
- package/lib/sandbox/dockerfile.ts +0 -11
- package/lib/sandbox/engine.ts +0 -17
- package/lib/sandbox/engines/docker-exact-discovery.ts +1 -5
- package/lib/sandbox/engines/wsl2-paths.ts +0 -4
- package/lib/sandbox/recovery.ts +24 -26
- package/lib/sandbox/removal.ts +58 -687
- package/lib/sandbox/shell.ts +1 -1
- package/lib/sandbox/task-resolver.ts +0 -48
- package/lib/sandbox/tools.ts +1 -5
- package/lib/sandbox/workspace-view.ts +2 -4
- package/lib/server/display.ts +0 -14
- package/lib/server/process-control.ts +1 -1
- package/lib/task/activity-intent.ts +36 -44
- package/lib/task/activity-log.ts +9 -22
- package/lib/task/artifact-command.ts +1 -1
- package/lib/task/artifact-lifecycle.ts +58 -67
- package/lib/task/artifact-name.ts +0 -7
- package/lib/task/artifact-operations.ts +3 -3
- package/lib/task/capabilities.ts +5 -6
- package/lib/task/commands/log.ts +0 -7
- package/lib/task/commands/status.ts +14 -79
- package/lib/task/commit-operation.ts +29 -51
- package/lib/task/completion-facts.ts +45 -42
- package/lib/task/control-authority.ts +27 -317
- package/lib/task/control-recovery.ts +2 -5
- package/lib/task/create.ts +2 -5
- package/lib/task/decision-intents.ts +5 -5
- package/lib/task/delivery-target.ts +0 -40
- package/lib/task/events.ts +89 -123
- package/lib/task/finalization.ts +52 -15
- package/lib/task/ledger-intents.ts +1 -1
- package/lib/task/lifecycle-execution.ts +37 -89
- package/lib/task/lifecycle.ts +48 -44
- package/lib/task/local-artifact-finalization.ts +0 -7
- package/lib/task/manual-validation-evidence.ts +1 -47
- package/lib/task/platform-operation-recovery.ts +117 -104
- package/lib/task/pr-delivery-fact.ts +2 -11
- package/lib/task/qualification-confirmation.ts +6 -6
- package/lib/task/resolve-ref.ts +0 -23
- package/lib/task/review-command.ts +2 -2
- package/lib/task/review-finalization.ts +1 -27
- package/lib/task/review-fingerprint.ts +0 -8
- package/lib/task/secure-fs.ts +0 -42
- package/lib/task/short-id.ts +0 -43
- package/lib/task/skill-scope.ts +0 -1
- package/lib/task/verification-engine.ts +56 -287
- package/lib/task/verification.ts +5 -5
- package/lib/task/write.ts +12 -1
- package/package.json +2 -2
- package/templates/.agents/README.en.md +1 -3
- package/templates/.agents/README.zh-CN.md +0 -2
- package/templates/.agents/rules/README.en.md +0 -1
- package/templates/.agents/rules/README.zh-CN.md +0 -1
- package/templates/.agents/rules/commit-and-pr.en.md +1 -1
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +0 -10
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +0 -10
- package/templates/.agents/rules/pr-sync.en.md +4 -2
- package/templates/.agents/rules/pr-sync.zh-CN.md +6 -9
- package/templates/.agents/rules/task-management.en.md +3 -3
- package/templates/.agents/rules/task-management.zh-CN.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/commit/SKILL.en.md +2 -2
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +6 -6
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +6 -6
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -25
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -26
- package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -2
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +0 -18
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +0 -18
- package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -5
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-pr/SKILL.en.md +4 -4
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -75
- package/templates/.agents/skills/watch-pr/SKILL.en.md +1 -1
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +3 -3
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +3 -3
- package/templates/.agents/templates/task.en.md +1 -0
- package/templates/.agents/templates/task.zh-CN.md +1 -0
- package/templates/.claude/settings.json +0 -24
- package/templates/.codex/hooks.json +0 -50
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +0 -582
- package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +0 -114
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +0 -237
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +0 -443
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +0 -236
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +0 -292
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +0 -267
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +0 -466
- package/dist/lib/internal/task-orchestration.js +0 -71
- package/dist/lib/platform/verification.js +0 -6
- package/dist/lib/run/prompt.js +0 -2
- package/dist/lib/sandbox/engines/authority.js +0 -289
- package/dist/lib/task/delegation-receipts.js +0 -410
- package/dist/lib/task/orchestration.js +0 -1233
- package/dist/lib/task/verification-checks.js +0 -9
- package/dist/lib/task/workspace-snapshot.js +0 -121
- package/dist/lifecycle-build-manifest.json +0 -6
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +0 -688
- package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +0 -147
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +0 -349
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +0 -506
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +0 -385
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -41
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +0 -418
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +0 -354
- package/lib/agent-clients/adapters/codex-orchestration.ts +0 -630
- package/lib/internal/task-orchestration.ts +0 -74
- package/lib/platform/verification.ts +0 -11
- package/lib/run/prompt.ts +0 -1
- package/lib/sandbox/engines/authority.ts +0 -365
- package/lib/task/delegation-receipts.ts +0 -547
- package/lib/task/orchestration.ts +0 -1651
- package/lib/task/verification-checks.ts +0 -10
- package/lib/task/workspace-snapshot.ts +0 -144
- package/templates/.agents/hooks/lifecycle-delegation.js +0 -140
- package/templates/.agents/rules/lifecycle-orchestration.en.md +0 -44
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +0 -44
- package/templates/.agents/skills/run-task/SKILL.en.md +0 -29
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +0 -29
- package/templates/.agents/skills/run-task/config/verify.en.json +0 -10
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +0 -10
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +0 -24
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +0 -24
- package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +0 -6
- package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +0 -6
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +0 -6
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +0 -6
- package/templates/.claude/commands/run-task.en.md +0 -8
- package/templates/.claude/commands/run-task.zh-CN.md +0 -8
- package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +0 -3
- package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +0 -3
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +0 -3
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +0 -3
- package/templates/.opencode/commands/run-task.en.md +0 -11
- package/templates/.opencode/commands/run-task.zh-CN.md +0 -11
|
@@ -57,12 +57,7 @@ import {
|
|
|
57
57
|
runSandboxHooks,
|
|
58
58
|
runBoundedSandboxHookCommand
|
|
59
59
|
} from '../agent-client-reconciler.ts';
|
|
60
|
-
import {
|
|
61
|
-
resolveTools,
|
|
62
|
-
tmpfsSeedStagingPath,
|
|
63
|
-
tmpfsSeedTargetPath,
|
|
64
|
-
toolConfigDirCandidates
|
|
65
|
-
} from '../tools.ts';
|
|
60
|
+
import { tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.ts';
|
|
66
61
|
import type { SandboxTool, TmpfsSeedEntry } from '../tools.ts';
|
|
67
62
|
import {
|
|
68
63
|
assertFreshSandboxReady,
|
|
@@ -96,12 +91,6 @@ import {
|
|
|
96
91
|
} from '../workspace-view.ts';
|
|
97
92
|
import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.ts';
|
|
98
93
|
import { validateSelinuxDisableEnv } from '../engines/selinux.ts';
|
|
99
|
-
import {
|
|
100
|
-
captureSandboxAuthority,
|
|
101
|
-
commandForSandboxAuthority,
|
|
102
|
-
verifySandboxAuthority,
|
|
103
|
-
type SandboxAuthorityEvidenceV1
|
|
104
|
-
} from '../engines/authority.ts';
|
|
105
94
|
import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.ts';
|
|
106
95
|
import { ensureSandboxDiscoveryReadmes } from '../readme-scaffold.ts';
|
|
107
96
|
import { removeDirRecursive } from '../../remove-dir.ts';
|
|
@@ -155,7 +144,6 @@ const MANAGED_GITHUB_GIT_CONFIG = [
|
|
|
155
144
|
];
|
|
156
145
|
|
|
157
146
|
type SandboxCreateConfig = ReturnType<typeof loadConfig>;
|
|
158
|
-
type PreparedDockerfile = ReturnType<typeof prepareDockerfile>;
|
|
159
147
|
type ResolvedTool = { tool: SandboxTool; dir: string };
|
|
160
148
|
type TmpfsSeedPlanEntry = TmpfsSeedEntry & {
|
|
161
149
|
volumeArgs: string[];
|
|
@@ -175,7 +163,6 @@ type EngineRunSafeFn = EngineRunFn;
|
|
|
175
163
|
type EngineRunVerboseFn = (engine: string, cmd: string, args: string[], opts?: { cwd?: string; env?: NodeJS.ProcessEnv }) => void;
|
|
176
164
|
type DirectRunFn = (cmd: string, args: string[], opts?: { cwd?: string }) => string;
|
|
177
165
|
type DirectRunSafeFn = DirectRunFn;
|
|
178
|
-
type DirectRunVerboseFn = (cmd: string, args: string[], opts?: { cwd?: string }) => void;
|
|
179
166
|
type HostShellConfig = {
|
|
180
167
|
hostDir: string;
|
|
181
168
|
mounts: Array<{ hostPath: string; containerPath: string }>;
|
|
@@ -933,24 +920,6 @@ function runEngineTaskCommand(engine: string, cmd: string, args: string[], opts:
|
|
|
933
920
|
return runTaskCommand(command.cmd, command.args, opts);
|
|
934
921
|
}
|
|
935
922
|
|
|
936
|
-
function runSandboxAuthorityTaskCommand(
|
|
937
|
-
authority: SandboxAuthorityEvidenceV1,
|
|
938
|
-
cmd: string,
|
|
939
|
-
args: string[],
|
|
940
|
-
opts: { cwd?: string } = {}
|
|
941
|
-
): string {
|
|
942
|
-
const command = commandForSandboxAuthority(authority, cmd, args);
|
|
943
|
-
return runTaskCommand(command.cmd, command.args, opts);
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
function runSandboxAuthoritySafeCommand(
|
|
947
|
-
authority: SandboxAuthorityEvidenceV1,
|
|
948
|
-
args: string[]
|
|
949
|
-
): string {
|
|
950
|
-
const command = commandForSandboxAuthority(authority, 'docker', args);
|
|
951
|
-
return runSafe(command.cmd, command.args);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
923
|
// `docker run` args for mounting a tool's containerMount as an in-container
|
|
955
924
|
// tmpfs. containerMount is an in-container path, so it is NOT engine-converted.
|
|
956
925
|
export function buildTmpfsRunArgs(containerMount: string, tmpfs: { size?: string; exec?: boolean }): string[] {
|
|
@@ -1317,18 +1286,29 @@ export async function create(
|
|
|
1317
1286
|
const previousManifest = fs.existsSync(controlPaths.manifestPath)
|
|
1318
1287
|
? readSandboxControlManifest(controlPaths.manifestPath)
|
|
1319
1288
|
: null;
|
|
1320
|
-
if (hadExistingControlRoot) {
|
|
1321
|
-
await quiesceSandboxControlRoot(controlPaths.root);
|
|
1322
|
-
}
|
|
1323
|
-
const previousCutoverSnapshot = hadExistingControlRoot
|
|
1324
|
-
? captureSandboxControlCutoverSnapshot(controlPaths.root)
|
|
1325
|
-
: null;
|
|
1326
1289
|
const existing = runSafeEngine(engine, 'docker', ['ps', '-a', '--format', '{{.Names}}']).split('\n').filter(Boolean);
|
|
1327
1290
|
const matchedContainers = containerNameCandidates(effectiveConfig, branch)
|
|
1328
1291
|
.filter((name) => existing.includes(name));
|
|
1329
1292
|
|
|
1293
|
+
const oldContainer = previousManifest ? await inspectSandboxControlContainer(previousManifest) : null;
|
|
1294
|
+
if (oldContainer?.state === 'unknown') {
|
|
1295
|
+
throw new Error(`SANDBOX_CONTROL_CONTAINER_UNKNOWN: ${oldContainer.reason}`);
|
|
1296
|
+
}
|
|
1297
|
+
if (previousManifest && (previousManifest.branch !== branch
|
|
1298
|
+
|| previousManifest.mode !== target.workspace.mode
|
|
1299
|
+
|| previousManifest.taskId !== (target.workspace.mode === 'task-bound' ? target.workspace.taskId : null)
|
|
1300
|
+
|| fs.realpathSync.native(previousManifest.worktreeRoot) !== fs.realpathSync.native(worktree))) {
|
|
1301
|
+
throw new Error('SANDBOX_WORKSPACE_IDENTITY_CONFLICT: recorded sandbox binding does not match the requested worktree');
|
|
1302
|
+
}
|
|
1303
|
+
const removalIds: string[] = [];
|
|
1330
1304
|
if (matchedContainers.length > 0) {
|
|
1331
1305
|
for (const name of matchedContainers) {
|
|
1306
|
+
const id = runSafeEngine(engine, 'docker', ['inspect', '--format', '{{.Id}}', name]).trim();
|
|
1307
|
+
if (!/^[a-f0-9]{64}$/u.test(id)
|
|
1308
|
+
|| (previousManifest && id !== previousManifest.containerIdentity.id)) {
|
|
1309
|
+
throw new Error('SANDBOX_CONTROL_CONTAINER_IDENTITY_MISMATCH');
|
|
1310
|
+
}
|
|
1311
|
+
removalIds.push(id);
|
|
1332
1312
|
const rawLabels = runSafeEngine(engine, 'docker', [
|
|
1333
1313
|
'inspect', '--format', '{{json .Config.Labels}}', name
|
|
1334
1314
|
]).trim();
|
|
@@ -1359,19 +1339,9 @@ export async function create(
|
|
|
1359
1339
|
);
|
|
1360
1340
|
}
|
|
1361
1341
|
}
|
|
1362
|
-
message('Removing old container instance...');
|
|
1363
|
-
for (const name of matchedContainers) {
|
|
1364
|
-
runSafeEngine(engine, 'docker', ['stop', name]);
|
|
1365
|
-
runSafeEngine(engine, 'docker', ['rm', name]);
|
|
1366
|
-
}
|
|
1367
1342
|
}
|
|
1368
|
-
if (
|
|
1369
|
-
|
|
1370
|
-
if (oldContainer.state !== 'absent') {
|
|
1371
|
-
throw new Error(oldContainer.state === 'unknown'
|
|
1372
|
-
? `SANDBOX_CONTROL_CONTAINER_UNKNOWN: ${oldContainer.reason}`
|
|
1373
|
-
: 'SANDBOX_CONTROL_CONTAINER_STILL_EXISTS');
|
|
1374
|
-
}
|
|
1343
|
+
if (oldContainer?.state === 'found' && !removalIds.includes(oldContainer.id)) {
|
|
1344
|
+
throw new Error('SANDBOX_CONTROL_CONTAINER_IDENTITY_MISMATCH');
|
|
1375
1345
|
}
|
|
1376
1346
|
const aliasesFile = ensureSandboxAliasesFile(
|
|
1377
1347
|
effectiveConfig.home,
|
|
@@ -1455,8 +1425,17 @@ export async function create(
|
|
|
1455
1425
|
? prepareSandboxCompletedTaskMountSource(effectiveConfig.repoRoot, target.workspace.taskId)
|
|
1456
1426
|
: null;
|
|
1457
1427
|
prepareSandboxWorkspaceMountTargets(effectiveConfig.repoRoot);
|
|
1428
|
+
if (hadExistingControlRoot) await quiesceSandboxControlRoot(controlPaths.root);
|
|
1429
|
+
const previousCutoverSnapshot = hadExistingControlRoot
|
|
1430
|
+
? captureSandboxControlCutoverSnapshot(controlPaths.root)
|
|
1431
|
+
: null;
|
|
1432
|
+
if (previousCutoverSnapshot) assertSandboxControlCutoverSnapshot(previousCutoverSnapshot);
|
|
1433
|
+
if (removalIds.length > 0) message('Removing old container instance...');
|
|
1434
|
+
for (const id of removalIds) {
|
|
1435
|
+
runEngineTaskCommand(engine, 'docker', ['stop', id]);
|
|
1436
|
+
runEngineTaskCommand(engine, 'docker', ['rm', id]);
|
|
1437
|
+
}
|
|
1458
1438
|
if (previousCutoverSnapshot) {
|
|
1459
|
-
assertSandboxControlCutoverSnapshot(previousCutoverSnapshot);
|
|
1460
1439
|
if (previousManifest) {
|
|
1461
1440
|
const finalOldContainer = await inspectSandboxControlContainer(previousManifest);
|
|
1462
1441
|
if (finalOldContainer.state !== 'absent') {
|
|
@@ -1559,11 +1538,8 @@ export async function create(
|
|
|
1559
1538
|
const tzFlags = hostTz ? ['-e', `TZ=${hostTz}`] : [];
|
|
1560
1539
|
|
|
1561
1540
|
let createdContainerRef: string | null = null;
|
|
1562
|
-
let creationAuthority: SandboxAuthorityEvidenceV1 | null = null;
|
|
1563
1541
|
try {
|
|
1564
|
-
|
|
1565
|
-
commandForSandboxAuthority(creationAuthority, 'docker');
|
|
1566
|
-
const dockerRunId = runSandboxAuthorityTaskCommand(creationAuthority, 'docker', [
|
|
1542
|
+
const dockerRunId = runEngineTaskCommand(engine, 'docker', [
|
|
1567
1543
|
'run',
|
|
1568
1544
|
'-d',
|
|
1569
1545
|
'--init',
|
|
@@ -1622,11 +1598,11 @@ export async function create(
|
|
|
1622
1598
|
effectiveConfig.imageName
|
|
1623
1599
|
]);
|
|
1624
1600
|
createdContainerRef = dockerRunId.trim();
|
|
1625
|
-
const containerId =
|
|
1601
|
+
const containerId = runEngineTaskCommand(engine, 'docker', [
|
|
1626
1602
|
'inspect', '--format', '{{.Id}}', dockerRunId.trim()
|
|
1627
1603
|
]).trim();
|
|
1628
|
-
if (
|
|
1629
|
-
const rawContainerLabels =
|
|
1604
|
+
if (!/^[a-f0-9]{64}$/u.test(containerId)) throw new Error('SANDBOX_CONTROL_CONTAINER_ID_INVALID');
|
|
1605
|
+
const rawContainerLabels = runEngineTaskCommand(engine, 'docker', [
|
|
1630
1606
|
'inspect', '--format', '{{json .Config.Labels}}', containerId
|
|
1631
1607
|
]).trim();
|
|
1632
1608
|
let inspectedLabels: unknown;
|
|
@@ -1651,18 +1627,10 @@ export async function create(
|
|
|
1651
1627
|
for (const [key, expected] of Object.entries(expectedLabels)) {
|
|
1652
1628
|
if (labels[key] !== expected) throw new Error('SANDBOX_CONTROL_CONTAINER_IDENTITY_MISMATCH');
|
|
1653
1629
|
}
|
|
1654
|
-
const authorityEvidence = captureSandboxAuthority(engine, {
|
|
1655
|
-
route: creationAuthority,
|
|
1656
|
-
lockDomain: creationAuthority.lockDomain
|
|
1657
|
-
});
|
|
1658
|
-
if (verifySandboxAuthority(creationAuthority, authorityEvidence).state !== 'verified') {
|
|
1659
|
-
throw new Error('SANDBOX_AUTHORITY_DRIFT_DURING_CREATE');
|
|
1660
|
-
}
|
|
1661
1630
|
finalizeSandboxControlManifest(control, {
|
|
1662
1631
|
engine,
|
|
1663
1632
|
id: containerId,
|
|
1664
|
-
labels: expectedLabels
|
|
1665
|
-
authorityEvidence
|
|
1633
|
+
labels: expectedLabels
|
|
1666
1634
|
});
|
|
1667
1635
|
replacementLease.clearQuiescing();
|
|
1668
1636
|
await startSandboxControlBroker(effectiveConfig.repoRoot, control.manifestPath);
|
|
@@ -1673,13 +1641,8 @@ export async function create(
|
|
|
1673
1641
|
createdTmpfsSeedPlan = tmpfsSeedPlan;
|
|
1674
1642
|
} catch (error) {
|
|
1675
1643
|
if (createdContainerRef) {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
runSandboxAuthoritySafeCommand(creationAuthority, ['rm', createdContainerRef]);
|
|
1679
|
-
} else {
|
|
1680
|
-
runSafeEngine(engine, 'docker', ['stop', createdContainerRef]);
|
|
1681
|
-
runSafeEngine(engine, 'docker', ['rm', createdContainerRef]);
|
|
1682
|
-
}
|
|
1644
|
+
runSafeEngine(engine, 'docker', ['stop', createdContainerRef]);
|
|
1645
|
+
runSafeEngine(engine, 'docker', ['rm', createdContainerRef]);
|
|
1683
1646
|
}
|
|
1684
1647
|
if (!hadExistingControlRoot) removeDirRecursive(control.root);
|
|
1685
1648
|
if (replacementCutover) {
|
|
@@ -2,8 +2,6 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { runSafeEngine, runVerboseEngine } from '../shell.ts';
|
|
5
|
-
import { resolveTaskBranch } from '../task-resolver.ts';
|
|
6
|
-
import { isRemovedHashShortIdInput } from '../../task/short-id.ts';
|
|
7
5
|
import { parseSandboxWorkspaceIdentity } from '../workspace-identity.ts';
|
|
8
6
|
import { resolveProjectTaskWorkspaceRoot } from '../../task/workspace-root.ts';
|
|
9
7
|
|
|
@@ -191,23 +189,6 @@ export function resolveTaskShortRef(
|
|
|
191
189
|
);
|
|
192
190
|
}
|
|
193
191
|
|
|
194
|
-
/**
|
|
195
|
-
* Resolve a sandbox command argument (`<branch | TASK-id | N>`) to a
|
|
196
|
-
* branch name, mirroring `ai sandbox exec` so that `start` and `exec` share one
|
|
197
|
-
* input contract. Short refs go through the registry-only resolver (which throws
|
|
198
|
-
* an actionable error on a miss); everything else flows through resolveTaskBranch
|
|
199
|
-
* (plain branch names pass through unchanged, TASK-ids resolve via task.md).
|
|
200
|
-
* Callers still run assertValidBranchName on the result.
|
|
201
|
-
*/
|
|
202
|
-
export function resolveBranchArg(arg: string, ctx: { repoRoot: string }): string {
|
|
203
|
-
if (isRemovedHashShortIdInput(arg)) {
|
|
204
|
-
throw new Error(`Invalid task short id '${arg}': task short ids must use bare digits`);
|
|
205
|
-
}
|
|
206
|
-
return isTaskShortRef(arg)
|
|
207
|
-
? resolveTaskShortRef(arg, ctx)
|
|
208
|
-
: resolveTaskBranch(arg, ctx.repoRoot);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
192
|
/**
|
|
212
193
|
* Pick the sandbox container row whose name matches one of the candidate
|
|
213
194
|
* container names (covers both the '..' and legacy '-' branch sanitizations).
|
|
@@ -15,11 +15,10 @@ import { resolveSandboxTarget } from '../workspace-identity.ts';
|
|
|
15
15
|
|
|
16
16
|
const USAGE = `Usage: ai sandbox start [--recreate] <branch | TASK-id | N>
|
|
17
17
|
|
|
18
|
-
Start an existing sandbox
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
state. --recreate replaces the existing container even when it is healthy.`;
|
|
18
|
+
Start or check an existing sandbox without replacing it. A missing container
|
|
19
|
+
leaves its task and control data intact. --recreate explicitly restores a missing
|
|
20
|
+
container from its existing worktree or replaces an existing container, preserving
|
|
21
|
+
the worktree and host-managed data.`;
|
|
23
22
|
|
|
24
23
|
export function parseStartArgs(args: string[]): { target: string; recreate: boolean; help: boolean } {
|
|
25
24
|
let recreate = false;
|
|
@@ -70,10 +69,8 @@ export async function start(args: string[]): Promise<void> {
|
|
|
70
69
|
containerNameCandidates(config, branch)
|
|
71
70
|
);
|
|
72
71
|
|
|
73
|
-
if (!found) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`No sandbox container for branch '${branch}'. Run 'ai sandbox create ${branch}' to create one.`
|
|
76
|
-
);
|
|
72
|
+
if (!found && !parsed.recreate) {
|
|
73
|
+
throw new Error(`No sandbox container for branch '${branch}'. Existing task and control data were preserved; use --recreate to explicitly restore the container.`);
|
|
77
74
|
}
|
|
78
75
|
|
|
79
76
|
const ready = await ensureSandboxReady({
|
|
@@ -81,7 +78,7 @@ export async function start(args: string[]): Promise<void> {
|
|
|
81
78
|
engine,
|
|
82
79
|
branch,
|
|
83
80
|
workspace: target.workspace,
|
|
84
|
-
row: found,
|
|
81
|
+
row: found ?? null,
|
|
85
82
|
allowRecreate: parsed.recreate,
|
|
86
83
|
forceRecreate: parsed.recreate,
|
|
87
84
|
recreate: async () => {
|
package/lib/sandbox/constants.ts
CHANGED
|
@@ -67,18 +67,10 @@ export function containerNameCandidates(config: Pick<SandboxPathConfig, 'contain
|
|
|
67
67
|
return safeNameCandidates(branch).map((name) => `${config.containerPrefix}-${name}`);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
export function worktreeDir(config: Pick<SandboxPathConfig, 'worktreeBase'>, branch: string): string {
|
|
71
|
-
return hostJoin(config.worktreeBase, sanitizeBranchName(branch));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
70
|
export function worktreeDirCandidates(config: Pick<SandboxPathConfig, 'worktreeBase'>, branch: string): string[] {
|
|
75
71
|
return safeNameCandidates(branch).map((name) => hostJoin(config.worktreeBase, name));
|
|
76
72
|
}
|
|
77
73
|
|
|
78
|
-
export function shareDir(config: Pick<SandboxPathConfig, 'shareBase'>): string {
|
|
79
|
-
return config.shareBase;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
74
|
export function shareCommonDir(config: Pick<SandboxPathConfig, 'shareBase'>): string {
|
|
83
75
|
return hostJoin(config.shareBase, 'common');
|
|
84
76
|
}
|
|
@@ -188,7 +188,6 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
188
188
|
const statusDir = params.statusDir ?? process.env.AGENT_INFRA_CONTROL_STATUS_DIR ?? '/run/agent-infra/control-status';
|
|
189
189
|
const taskViewEffect = taskViewEffectForRequest(request);
|
|
190
190
|
const hostGeneration = preflight(statusDir, taskViewEffect);
|
|
191
|
-
const identityPath = path.join(statusDir, 'identity.json');
|
|
192
191
|
if (fs.existsSync(statusDir)) {
|
|
193
192
|
const identity = readSandboxControlIdentitySentinel(statusDir);
|
|
194
193
|
const requestTask = canonicalRequestTask(request);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { SandboxControlManifest } from './protocol.ts';
|
|
2
2
|
import { runProbe } from '../shell.ts';
|
|
3
|
-
import { captureSandboxAuthority, isSandboxAuthorityEvidence, verifySandboxAuthority } from '../engines/authority.ts';
|
|
4
3
|
import { discoverExactContainer } from '../engines/docker-exact-discovery.ts';
|
|
5
4
|
|
|
6
5
|
export type ContainerObservation =
|
|
@@ -46,24 +45,10 @@ export async function inspectSandboxControlContainer(
|
|
|
46
45
|
manifest: SandboxControlManifest,
|
|
47
46
|
options: { probe?: typeof runProbe; timeoutMs?: number } = {}
|
|
48
47
|
): Promise<ContainerObservation> {
|
|
49
|
-
if (!isSandboxAuthorityEvidence(manifest.authorityEvidence)) {
|
|
50
|
-
return { state: 'unknown', reason: 'SANDBOX_AUTHORITY_EVIDENCE_MISSING' };
|
|
51
|
-
}
|
|
52
48
|
try {
|
|
53
|
-
const authority = captureSandboxAuthority(manifest.engine, {
|
|
54
|
-
probe: options.probe,
|
|
55
|
-
timeoutMs: options.timeoutMs,
|
|
56
|
-
lockDomain: manifest.authorityEvidence.lockDomain,
|
|
57
|
-
route: manifest.authorityEvidence
|
|
58
|
-
});
|
|
59
|
-
const authorityState = verifySandboxAuthority(manifest.authorityEvidence, authority);
|
|
60
|
-
if (authorityState.state !== 'verified') {
|
|
61
|
-
return { state: 'unknown', reason: authorityState.reason ?? 'SANDBOX_AUTHORITY_UNAVAILABLE' };
|
|
62
|
-
}
|
|
63
49
|
const discovered = discoverExactContainer(manifest.engine, manifest.containerIdentity.id, {
|
|
64
50
|
probe: options.probe,
|
|
65
|
-
timeoutMs: options.timeoutMs
|
|
66
|
-
authority: manifest.authorityEvidence
|
|
51
|
+
timeoutMs: options.timeoutMs
|
|
67
52
|
});
|
|
68
53
|
if (discovered.state === 'absent') return { state: 'absent', id: discovered.id };
|
|
69
54
|
if (discovered.state === 'conflict') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
@@ -424,7 +424,3 @@ function readJsonExecution(filePath: string): SandboxControlExecution {
|
|
|
424
424
|
if (!value || value.requestId.length === 0) throw new Error('SANDBOX_CONTROL_EXECUTION_INVALID');
|
|
425
425
|
return value;
|
|
426
426
|
}
|
|
427
|
-
|
|
428
|
-
export function disconnectExecutor(child: ChildProcess): void {
|
|
429
|
-
if (child.connected) child.disconnect();
|
|
430
|
-
}
|
|
@@ -127,11 +127,3 @@ export function validateSandboxControlIdentity(params: Readonly<{
|
|
|
127
127
|
}
|
|
128
128
|
return { state: 'valid', sentinel };
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
export function assertSandboxControlIdentity(params: Parameters<typeof validateSandboxControlIdentity>[0]): SandboxControlIdentitySentinel {
|
|
132
|
-
const result = validateSandboxControlIdentity(params);
|
|
133
|
-
if (result.state !== 'valid' || !result.sentinel) {
|
|
134
|
-
throw new Error(`SANDBOX_CONTROL_IDENTITY_${result.state.replaceAll('-', '_').toUpperCase()}`);
|
|
135
|
-
}
|
|
136
|
-
return result.sentinel;
|
|
137
|
-
}
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
resolveSandboxLockNamespace,
|
|
23
23
|
type SandboxResourceLock
|
|
24
24
|
} from './native-file-lock.ts';
|
|
25
|
-
import { isSandboxAuthorityEvidence } from '../engines/authority.ts';
|
|
26
25
|
import { validateSandboxControlIdentity } from './identity-sentinel.ts';
|
|
27
26
|
import {
|
|
28
27
|
parseSandboxControlStatus,
|
|
@@ -97,7 +96,6 @@ export type SandboxRemovalJournal = Readonly<{
|
|
|
97
96
|
engine: string;
|
|
98
97
|
containerId: string;
|
|
99
98
|
generation: string;
|
|
100
|
-
authorityFingerprint: string;
|
|
101
99
|
carrierIdentityDigest: string;
|
|
102
100
|
lockDomain: string;
|
|
103
101
|
owner: ProcessIdentity & Readonly<{ leaseNonce: string }>;
|
|
@@ -144,10 +142,14 @@ function removalJournalDomain(lockDomain: string, create = true): string {
|
|
|
144
142
|
}
|
|
145
143
|
|
|
146
144
|
function removalJournalPath(manifest: SandboxControlManifest): string {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
const digest = removalCarrierDigest(manifest.engine, manifest.containerIdentity.id, manifest.generation);
|
|
146
|
+
// Existing interrupted removals retain their original native lock namespace.
|
|
147
|
+
const existing = listSandboxRemovalJournals({ project: manifest.project, branch: manifest.branch })
|
|
148
|
+
.filter((record) => record.carrierIdentityDigest === digest);
|
|
149
|
+
if (existing.length > 1) throw new Error('SANDBOX_CONTROL_REMOVAL_JOURNAL_IDENTITY_MISMATCH');
|
|
150
|
+
if (existing[0]) return removalJournalPathForRecord(existing[0]);
|
|
151
|
+
const namespace = resolveSandboxLockNamespace('sandbox-removal-journal');
|
|
152
|
+
return path.join(removalJournalDomain(namespace.lockDomain), `${digest}.json`);
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
function removalJournalPathForRecord(record: SandboxRemovalJournal): string {
|
|
@@ -195,8 +197,10 @@ function parseRemovalJournal(raw: string): SandboxRemovalJournal {
|
|
|
195
197
|
} catch {
|
|
196
198
|
throw new Error('SANDBOX_CONTROL_REMOVAL_JOURNAL_INVALID');
|
|
197
199
|
}
|
|
200
|
+
// TODO(compat): Remove obsolete-field normalization after pre-simplification removal journals drain.
|
|
201
|
+
if (isRecord(value)) delete value.authorityFingerprint;
|
|
198
202
|
if (!isRecord(value) || !hasExactKeys(value, [
|
|
199
|
-
'
|
|
203
|
+
'carrierIdentityDigest', 'containerId', 'engine', 'expectedOldJournalRevision',
|
|
200
204
|
'generation', 'handoffId', 'lockDomain', 'newOperation', 'oldOperation', 'operation', 'owner', 'phase',
|
|
201
205
|
'recordedAt', 'revision', 'target', 'transitionId', 'version'
|
|
202
206
|
]) || !isRecord(value.owner) || !hasExactKeys(value.owner, ['leaseNonce', 'pid', 'startTime'])
|
|
@@ -211,7 +215,6 @@ function parseRemovalJournal(raw: string): SandboxRemovalJournal {
|
|
|
211
215
|
|| typeof value.engine !== 'string' || value.engine.length === 0
|
|
212
216
|
|| typeof value.containerId !== 'string' || value.containerId.length === 0
|
|
213
217
|
|| typeof value.generation !== 'string' || value.generation.length === 0
|
|
214
|
-
|| typeof value.authorityFingerprint !== 'string' || !/^[a-f0-9]{64}$/u.test(value.authorityFingerprint)
|
|
215
218
|
|| typeof value.carrierIdentityDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(value.carrierIdentityDigest)
|
|
216
219
|
|| typeof value.lockDomain !== 'string' || !/^[a-f0-9]{64}$/u.test(value.lockDomain)
|
|
217
220
|
|| !Number.isSafeInteger(value.owner.pid) || (value.owner.pid as number) <= 0
|
|
@@ -254,7 +257,6 @@ function parseRemovalJournal(raw: string): SandboxRemovalJournal {
|
|
|
254
257
|
engine: value.engine as string,
|
|
255
258
|
containerId: value.containerId as string,
|
|
256
259
|
generation: value.generation as string,
|
|
257
|
-
authorityFingerprint: value.authorityFingerprint as string,
|
|
258
260
|
carrierIdentityDigest: value.carrierIdentityDigest as string,
|
|
259
261
|
lockDomain: value.lockDomain as string,
|
|
260
262
|
owner: {
|
|
@@ -470,9 +472,7 @@ function startRemovalJournal(
|
|
|
470
472
|
const target = removalJournalPath(manifest);
|
|
471
473
|
const existing = readRemovalJournalAt(target);
|
|
472
474
|
if (existing && (existing.engine !== manifest.engine || existing.containerId !== manifest.containerIdentity.id
|
|
473
|
-
|| existing.generation !== manifest.generation
|
|
474
|
-
|| existing.authorityFingerprint !== manifest.authorityEvidence.authorityFingerprint
|
|
475
|
-
|| existing.lockDomain !== manifest.authorityEvidence.lockDomain)) {
|
|
475
|
+
|| existing.generation !== manifest.generation)) {
|
|
476
476
|
throw new Error('SANDBOX_CONTROL_REMOVAL_JOURNAL_IDENTITY_MISMATCH');
|
|
477
477
|
}
|
|
478
478
|
const targetCommit = options.target ?? existing?.target ?? defaultRemovalTarget(manifest);
|
|
@@ -496,9 +496,8 @@ function startRemovalJournal(
|
|
|
496
496
|
engine: manifest.engine,
|
|
497
497
|
containerId: manifest.containerIdentity.id,
|
|
498
498
|
generation: manifest.generation,
|
|
499
|
-
authorityFingerprint: manifest.authorityEvidence.authorityFingerprint,
|
|
500
499
|
carrierIdentityDigest: removalCarrierDigest(manifest.engine, manifest.containerIdentity.id, manifest.generation),
|
|
501
|
-
lockDomain:
|
|
500
|
+
lockDomain: existing?.lockDomain ?? resolveSandboxLockNamespace('sandbox-removal-journal').lockDomain,
|
|
502
501
|
owner: { pid: process.pid, startTime, leaseNonce: randomUUID() },
|
|
503
502
|
target: targetCommit
|
|
504
503
|
};
|
|
@@ -574,21 +573,6 @@ export function advanceSandboxRemovalJournalPhase(
|
|
|
574
573
|
}
|
|
575
574
|
}
|
|
576
575
|
|
|
577
|
-
export function advanceSandboxRemovalJournalToPhase(
|
|
578
|
-
journal: SandboxRemovalJournal,
|
|
579
|
-
phase: SandboxRemovalJournalPhase,
|
|
580
|
-
resourceLock: SandboxResourceLock
|
|
581
|
-
): SandboxRemovalJournal {
|
|
582
|
-
let current = journal;
|
|
583
|
-
while (phaseIndex(current.phase) < phaseIndex(phase)) {
|
|
584
|
-
const next = current.phase === 'target-committed' && phase === 'container-absent'
|
|
585
|
-
? phase : SANDBOX_REMOVAL_JOURNAL_PHASES[phaseIndex(current.phase) + 1];
|
|
586
|
-
if (!next) throw new Error('SANDBOX_CONTROL_REMOVAL_PHASE_TRANSITION_INVALID');
|
|
587
|
-
current = advanceSandboxRemovalJournalPhase(current, next, { resourceLock });
|
|
588
|
-
}
|
|
589
|
-
return current;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
576
|
async function awaitWithDeadline<T>(
|
|
593
577
|
operation: () => Promise<T>,
|
|
594
578
|
deadlineAt: number,
|
|
@@ -682,7 +666,6 @@ function recordSandboxRemovalPending(
|
|
|
682
666
|
phase,
|
|
683
667
|
containerId: manifest.containerIdentity.id,
|
|
684
668
|
generation: manifest.generation,
|
|
685
|
-
authorityFingerprint: manifest.authorityEvidence.authorityFingerprint,
|
|
686
669
|
recordedAt: Date.now()
|
|
687
670
|
})}\n`, { mode: 0o600, flag: 'wx' });
|
|
688
671
|
fs.renameSync(temporary, pendingPath);
|
|
@@ -1104,11 +1087,13 @@ function readSandboxControlManifestValue(manifestPath: string): SandboxControlMa
|
|
|
1104
1087
|
} catch {
|
|
1105
1088
|
throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
1106
1089
|
}
|
|
1090
|
+
// TODO(compat): Remove obsolete-field normalization after existing sandbox manifests are rewritten by their lifecycle.
|
|
1091
|
+
delete manifest.authorityEvidence;
|
|
1107
1092
|
const candidate = manifest as Partial<SandboxControlManifest>;
|
|
1108
1093
|
const expectedKeys = [
|
|
1109
1094
|
'branch', 'channelDir', 'container', 'containerIdentity', 'engine', 'generation',
|
|
1110
1095
|
'mode', 'processingDir', 'project', 'publicStatusDir', 'repoRoot', 'runtimeDir',
|
|
1111
|
-
'taskId', 'token', 'worktreeRoot', '
|
|
1096
|
+
'taskId', 'token', 'worktreeRoot', 'controlRootId',
|
|
1112
1097
|
];
|
|
1113
1098
|
const actualKeys = Object.keys(manifest).sort().join(',');
|
|
1114
1099
|
if (actualKeys !== expectedKeys.sort().join(',')) {
|
|
@@ -1134,8 +1119,7 @@ function readSandboxControlManifestValue(manifestPath: string): SandboxControlMa
|
|
|
1134
1119
|
|| typeof candidate.generation !== 'string'
|
|
1135
1120
|
|| typeof candidate.controlRootId !== 'string' || !/^[a-f0-9]{64,128}$/u.test(candidate.controlRootId)
|
|
1136
1121
|
|| (candidate.mode === 'task-bound' && (typeof candidate.taskId !== 'string' || candidate.taskId.length === 0))
|
|
1137
|
-
|| (candidate.mode === 'branch-only' && candidate.taskId !== null)
|
|
1138
|
-
|| !isSandboxAuthorityEvidence(candidate.authorityEvidence)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
1122
|
+
|| (candidate.mode === 'branch-only' && candidate.taskId !== null)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
1139
1123
|
const root = path.dirname(path.resolve(manifestPath));
|
|
1140
1124
|
const canonicalRoot = fs.realpathSync.native(root);
|
|
1141
1125
|
const resolveControlChildPath = (candidatePath: string, name: string): string | null => {
|
|
@@ -1174,7 +1158,6 @@ function readSandboxControlManifestValue(manifestPath: string): SandboxControlMa
|
|
|
1174
1158
|
id: containerIdentity.id as string,
|
|
1175
1159
|
labels: { ...(containerIdentity.labels as Record<string, string>) }
|
|
1176
1160
|
},
|
|
1177
|
-
authorityEvidence: candidate.authorityEvidence,
|
|
1178
1161
|
branch: candidate.branch,
|
|
1179
1162
|
mode: candidate.mode,
|
|
1180
1163
|
taskId: candidate.taskId,
|
|
@@ -1489,7 +1472,7 @@ async function removeSandboxControlRootAttempt(
|
|
|
1489
1472
|
const manifest = readSandboxControlManifest(manifestPath);
|
|
1490
1473
|
const resourceLock = options.resourceLock ?? acquireSandboxResourceLock(
|
|
1491
1474
|
`${manifest.engine}:${manifest.containerIdentity.id}`,
|
|
1492
|
-
{ lockDomain: manifest
|
|
1475
|
+
{ lockDomain: readSandboxRemovalJournal(manifest)?.lockDomain }
|
|
1493
1476
|
);
|
|
1494
1477
|
try {
|
|
1495
1478
|
if (fs.readFileSync(manifestPath, 'utf8') !== manifestRawBeforeLock) {
|
|
@@ -1603,7 +1586,6 @@ async function removeSandboxControlRootAttempt(
|
|
|
1603
1586
|
if (currentManifestRaw !== manifestRawBeforeLock
|
|
1604
1587
|
|| currentManifest.engine !== manifest.engine
|
|
1605
1588
|
|| currentManifest.containerIdentity.id !== manifest.containerIdentity.id
|
|
1606
|
-
|| currentManifest.authorityEvidence.lockDomain !== manifest.authorityEvidence.lockDomain
|
|
1607
1589
|
|| currentManifest.token !== manifest.token || currentManifest.generation !== manifest.generation
|
|
1608
1590
|
|| !isSandboxControlRootQuiescing(resolvedRoot)) {
|
|
1609
1591
|
throw new Error('SANDBOX_CONTROL_MANIFEST_CHANGED');
|
|
@@ -1683,18 +1665,3 @@ async function removeSandboxControlRootAttempt(
|
|
|
1683
1665
|
if (!options.resourceLock) resourceLock.release();
|
|
1684
1666
|
}
|
|
1685
1667
|
}
|
|
1686
|
-
|
|
1687
|
-
export type GarbageCollectSandboxControlOptions = Omit<RemoveSandboxControlOptions, 'removeContainer' | 'requireAbsent'>;
|
|
1688
|
-
|
|
1689
|
-
export async function garbageCollectSandboxControlRoot(
|
|
1690
|
-
root: string,
|
|
1691
|
-
options: GarbageCollectSandboxControlOptions = {}
|
|
1692
|
-
): Promise<void> {
|
|
1693
|
-
await removeSandboxControlRoot(path.resolve(root), {
|
|
1694
|
-
...options,
|
|
1695
|
-
requireAbsent: true,
|
|
1696
|
-
removeContainer: async () => {
|
|
1697
|
-
throw new Error('SANDBOX_CONTROL_CONTAINER_REAPPEARED');
|
|
1698
|
-
}
|
|
1699
|
-
});
|
|
1700
|
-
}
|
|
@@ -86,14 +86,6 @@ export function resolveSandboxLockNamespace(
|
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export function stableSandboxLockPath(
|
|
90
|
-
carrierIdentity: string,
|
|
91
|
-
lockDomain?: string,
|
|
92
|
-
options: Readonly<{ home?: string }> = {}
|
|
93
|
-
): string {
|
|
94
|
-
return resolveSandboxLockNamespace(carrierIdentity, { lockDomain, home: options.home }).lockPath;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
89
|
export function acquireSandboxResourceLock(
|
|
98
90
|
carrierIdentity: string,
|
|
99
91
|
options: Readonly<{ lockDomain?: string; home?: string }> = {}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { validateTaskCreateCandidate, type TaskCreateCandidateV1 } from '../../task/create.ts';
|
|
3
3
|
import type { ProcessIdentity } from '../../server/process-state.ts';
|
|
4
|
-
import type { SandboxAuthorityEvidenceV1 } from '../engines/authority.ts';
|
|
5
4
|
import type { SandboxTaskView } from './task-view.ts';
|
|
6
5
|
|
|
7
6
|
export const SANDBOX_CONTROL_MAX_BYTES = 64 * 1024;
|
|
@@ -36,7 +35,6 @@ export type SandboxControlContainerIdentity = Readonly<{
|
|
|
36
35
|
export type SandboxControlManifestBase = Readonly<{
|
|
37
36
|
engine: string; repoRoot: string; worktreeRoot: string; project: string; container: string;
|
|
38
37
|
containerIdentity: SandboxControlContainerIdentity;
|
|
39
|
-
authorityEvidence: SandboxAuthorityEvidenceV1;
|
|
40
38
|
branch: string; mode: 'task-bound' | 'branch-only'; taskId: string | null; token: string;
|
|
41
39
|
generation: string; controlRootId: string; channelDir: string; publicStatusDir: string; processingDir: string;
|
|
42
40
|
}>;
|