@fitlab-ai/agent-infra 0.9.9 → 0.9.11
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 +13 -3
- package/README.zh-CN.md +13 -3
- package/bin/cli.ts +17 -4
- package/dist/bin/cli.js +19 -4
- package/dist/bin/internal-cli.js +55 -4
- package/dist/lib/agent-client.js +0 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +17 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +11 -31
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +15 -33
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
- package/dist/lib/agent-clients/adapters/index.js +3 -1
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +65 -0
- package/dist/lib/agent-clients/config.js +14 -99
- package/dist/lib/agent-clients/reconcile.js +1 -11
- package/dist/lib/agent-clients/schema.js +5 -4
- package/dist/lib/agent-clients/tokens.js +7 -17
- package/dist/lib/agent-clients/types.js +2 -1
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +9 -0
- package/dist/lib/init.js +2 -2
- package/dist/lib/internal/codex-lifecycle.js +2 -2
- package/dist/lib/internal/codex-sandbox-controller.js +4 -3
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/sandbox-control.js +2 -2
- package/dist/lib/internal/task-finalization.js +19 -9
- package/dist/lib/internal/task-ledger.js +8 -2
- package/dist/lib/internal/task-lifecycle.js +17 -93
- package/dist/lib/internal/task-orchestration.js +30 -216
- package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +77 -43
- package/dist/lib/merge.js +205 -103
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- package/dist/lib/run/tui.js +5 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
- package/dist/lib/sandbox/commands/create.js +3 -3
- package/dist/lib/sandbox/commands/enter.js +18 -4
- package/dist/lib/sandbox/config.js +3 -12
- package/dist/lib/sandbox/control/controller-registration.js +4 -2
- package/dist/lib/sandbox/control/executor.js +135 -40
- package/dist/lib/sandbox/control/lifecycle.js +29 -20
- package/dist/lib/sandbox/control/server.js +18 -16
- package/dist/lib/sandbox/mounts.js +3 -2
- package/dist/lib/sandbox/recovery.js +123 -22
- package/dist/lib/sandbox/workspace-identity.js +14 -0
- package/dist/lib/sandbox/workspace-view.js +8 -9
- package/dist/lib/self-update.js +278 -0
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +4 -2
- package/dist/lib/task/codex-orchestration.js +32 -35
- package/dist/lib/task/command-options.js +1 -3
- package/dist/lib/task/commands/cat.js +0 -5
- package/dist/lib/task/commands/decisions.js +76 -92
- package/dist/lib/task/commands/files.js +6 -6
- package/dist/lib/task/commands/grep.js +25 -61
- package/dist/lib/task/commands/issue-body.js +6 -6
- package/dist/lib/task/commands/log.js +25 -10
- package/dist/lib/task/commands/show.js +5 -6
- package/dist/lib/task/commands/status.js +8 -8
- package/dist/lib/task/commit-operation.js +21 -25
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +6 -1
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +263 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delegation-receipts.js +8 -7
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/finalization.js +72 -46
- package/dist/lib/task/index.js +19 -29
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +16 -0
- package/dist/lib/task/orchestration.js +51 -14
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +19 -6
- package/dist/lib/task/verification-engine.js +38 -18
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +6 -37
- package/dist/lib/version.js +5 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +0 -1
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +25 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +16 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +16 -35
- package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
- package/lib/agent-clients/adapters/index.ts +3 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +66 -0
- package/lib/agent-clients/config.ts +14 -144
- package/lib/agent-clients/reconcile.ts +1 -12
- package/lib/agent-clients/schema.ts +5 -4
- package/lib/agent-clients/tokens.ts +7 -20
- package/lib/agent-clients/types.ts +2 -1
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +9 -0
- package/lib/init.ts +2 -2
- package/lib/internal/codex-lifecycle.ts +2 -2
- package/lib/internal/codex-sandbox-controller.ts +4 -3
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/sandbox-control.ts +2 -2
- package/lib/internal/task-finalization.ts +27 -13
- package/lib/internal/task-ledger.ts +8 -2
- package/lib/internal/task-lifecycle.ts +33 -76
- package/lib/internal/task-orchestration.ts +33 -190
- package/lib/{task/commands/validate.ts → internal/task-validate.ts} +77 -41
- package/lib/merge.ts +236 -120
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- package/lib/run/tui.ts +5 -3
- package/lib/sandbox/agent-client-reconciler.ts +5 -15
- package/lib/sandbox/commands/create.ts +4 -4
- package/lib/sandbox/commands/enter.ts +24 -4
- package/lib/sandbox/config.ts +2 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/controller-registration.ts +8 -3
- package/lib/sandbox/control/executor.ts +156 -53
- package/lib/sandbox/control/lifecycle.ts +34 -28
- package/lib/sandbox/control/protocol.ts +1 -3
- package/lib/sandbox/control/server.ts +15 -12
- package/lib/sandbox/control/state.ts +7 -7
- package/lib/sandbox/mounts.ts +4 -3
- package/lib/sandbox/recovery.ts +162 -34
- package/lib/sandbox/tools.ts +1 -1
- package/lib/sandbox/workspace-identity.ts +28 -0
- package/lib/sandbox/workspace-view.ts +14 -10
- package/lib/self-update.ts +341 -0
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +5 -3
- package/lib/task/codex-orchestration.ts +40 -45
- package/lib/task/command-options.ts +1 -2
- package/lib/task/commands/cat.ts +0 -4
- package/lib/task/commands/decisions.ts +80 -75
- package/lib/task/commands/files.ts +6 -6
- package/lib/task/commands/grep.ts +23 -51
- package/lib/task/commands/issue-body.ts +6 -6
- package/lib/task/commands/log.ts +22 -10
- package/lib/task/commands/show.ts +5 -6
- package/lib/task/commands/status.ts +8 -8
- package/lib/task/commit-operation.ts +25 -30
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +6 -1
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +340 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delegation-receipts.ts +8 -6
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +84 -42
- package/lib/task/index.ts +19 -28
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +20 -0
- package/lib/task/orchestration.ts +42 -16
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +33 -7
- package/lib/task/verification-engine.ts +33 -21
- package/lib/task/verification-review.ts +1 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +6 -40
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/QUICKSTART.en.md +2 -3
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -3
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +1 -0
- package/templates/.agents/rules/README.zh-CN.md +1 -0
- package/templates/.agents/rules/compatibility-policy.en.md +40 -0
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +40 -0
- package/templates/.agents/rules/create-issue.github.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
- package/templates/.agents/rules/human-decision-context.en.md +9 -7
- package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
- package/templates/.agents/rules/issue-fields.github.en.md +1 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +1 -1
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
- package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +3 -3
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
- package/templates/.agents/rules/pr-sync.en.md +2 -2
- package/templates/.agents/rules/pr-sync.github.en.md +3 -2
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +2 -1
- package/templates/.agents/rules/release-commands.github.en.md +6 -0
- package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
- package/templates/.agents/rules/review-handshake.en.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/review-method.en.md +1 -0
- package/templates/.agents/rules/review-method.zh-CN.md +1 -0
- package/templates/.agents/rules/task-management.en.md +1 -2
- package/templates/.agents/rules/task-management.zh-CN.md +1 -2
- package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- 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/check-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/commit/SKILL.en.md +27 -7
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +15 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +15 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +46 -0
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +46 -0
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
- package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
- package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -4
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +9 -6
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +9 -6
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +7 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +6 -2
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +159 -104
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -1
- package/templates/.claude/commands/commit.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/run-task.en.md +1 -1
- package/templates/.claude/commands/run-task.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.traecli/commands/update-agent-infra.en.md +7 -0
- package/templates/.traecli/commands/update-agent-infra.zh-CN.md +7 -0
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
|
@@ -9,6 +9,7 @@ import { createSandboxCapabilityPlan } from './agent-client-reconciler.js';
|
|
|
9
9
|
import { toEnginePath } from './engines/wsl2-paths.js';
|
|
10
10
|
import { sandboxCoreBindMounts } from './mounts.js';
|
|
11
11
|
import { assertSandboxTaskSource, sandboxControlPaths, sandboxWorkspaceViewPaths } from './workspace-view.js';
|
|
12
|
+
import { resolveTaskWorkspace } from './task-resolver.js';
|
|
12
13
|
import { parseSandboxWorkspaceIdentity, sameSandboxWorkspaceIdentity } from './workspace-identity.js';
|
|
13
14
|
import { inspectWorktree } from './worktree-safety.js';
|
|
14
15
|
import { commandForEngine, runEngine, runOkEngine, runProbe, runVerboseEngine } from './shell.js';
|
|
@@ -407,6 +408,66 @@ function sourceCandidates(engine, hostPath) {
|
|
|
407
408
|
}
|
|
408
409
|
return [...new Set(candidates.map((candidate) => normalizeMountSource(engine, candidate)))];
|
|
409
410
|
}
|
|
411
|
+
function recoveryTaskSources(repoRoot, taskId) {
|
|
412
|
+
const activePath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
|
|
413
|
+
const completedPath = path.join(repoRoot, '.agents', 'workspace', 'completed', taskId);
|
|
414
|
+
if (fs.existsSync(activePath) && !fs.existsSync(completedPath)) {
|
|
415
|
+
const source = assertSandboxTaskSource(repoRoot, taskId);
|
|
416
|
+
return { mountPaths: [source], accessiblePaths: [source] };
|
|
417
|
+
}
|
|
418
|
+
const task = resolveTaskWorkspace(taskId, repoRoot);
|
|
419
|
+
if (task.state === 'active') {
|
|
420
|
+
const source = assertSandboxTaskSource(repoRoot, taskId);
|
|
421
|
+
return { mountPaths: [source], accessiblePaths: [source] };
|
|
422
|
+
}
|
|
423
|
+
if (task.state !== 'completed') {
|
|
424
|
+
throw new Error(`SANDBOX_TASK_SOURCE_STATE_UNSUPPORTED: task ${taskId} is ${task.state}`);
|
|
425
|
+
}
|
|
426
|
+
const completedRoot = fs.realpathSync.native(path.join(repoRoot, '.agents', 'workspace', 'completed'));
|
|
427
|
+
const completedSource = fs.realpathSync.native(path.dirname(task.taskMd));
|
|
428
|
+
if (path.dirname(completedSource) !== completedRoot) {
|
|
429
|
+
throw new Error(`SANDBOX_TASK_SOURCE_INVALID: ${completedSource} is not under completed workspace`);
|
|
430
|
+
}
|
|
431
|
+
const historicalActivePath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
|
|
432
|
+
const mountPaths = [completedSource, historicalActivePath];
|
|
433
|
+
if (fs.existsSync(historicalActivePath)) {
|
|
434
|
+
try {
|
|
435
|
+
mountPaths[1] = fs.realpathSync.native(historicalActivePath);
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
// Keep the exact historical path as a candidate; the current completed
|
|
439
|
+
// source remains the only source that must still be accessible.
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return { mountPaths, accessiblePaths: [completedSource] };
|
|
443
|
+
}
|
|
444
|
+
function startCompletedSandboxContainer(params) {
|
|
445
|
+
const taskSources = recoveryTaskSources(params.config.repoRoot, params.taskId);
|
|
446
|
+
const completedSource = taskSources.accessiblePaths[0];
|
|
447
|
+
const activeRoot = path.join(params.config.repoRoot, '.agents', 'workspace', 'active');
|
|
448
|
+
const historicalSource = path.join(activeRoot, params.taskId);
|
|
449
|
+
fs.mkdirSync(activeRoot, { recursive: true });
|
|
450
|
+
try {
|
|
451
|
+
fs.lstatSync(historicalSource);
|
|
452
|
+
throw new Error(`SANDBOX_COMPLETED_SOURCE_CONFLICT: historical task source already exists at ${historicalSource}`);
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT'))
|
|
456
|
+
throw error;
|
|
457
|
+
}
|
|
458
|
+
fs.symlinkSync(completedSource, historicalSource, process.platform === 'win32' ? 'junction' : 'dir');
|
|
459
|
+
try {
|
|
460
|
+
params.start(params.engine, params.container);
|
|
461
|
+
}
|
|
462
|
+
finally {
|
|
463
|
+
const temporarySource = fs.lstatSync(historicalSource);
|
|
464
|
+
const resolvedSource = fs.realpathSync.native(historicalSource);
|
|
465
|
+
if (!temporarySource.isSymbolicLink() || resolvedSource !== completedSource) {
|
|
466
|
+
throw new Error(`SANDBOX_COMPLETED_SOURCE_CHANGED: refusing to remove unexpected path at ${historicalSource}`);
|
|
467
|
+
}
|
|
468
|
+
fs.unlinkSync(historicalSource);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
410
471
|
function sameHostSource(left, right) {
|
|
411
472
|
try {
|
|
412
473
|
const leftStat = fs.statSync(left, { bigint: true });
|
|
@@ -440,21 +501,29 @@ function expectedMounts(params) {
|
|
|
440
501
|
container: params.container,
|
|
441
502
|
identity: params.workspace
|
|
442
503
|
});
|
|
504
|
+
const taskSources = params.workspace.mode === 'task-bound'
|
|
505
|
+
? recoveryTaskSources(config.repoRoot, params.workspace.taskId)
|
|
506
|
+
: null;
|
|
507
|
+
const taskId = params.workspace.mode === 'task-bound' ? params.workspace.taskId : null;
|
|
443
508
|
const core = sandboxCoreBindMounts(config, branch, {
|
|
444
509
|
workspaceViewRoot: view.root,
|
|
445
510
|
controlDir: control.channelDir,
|
|
446
511
|
controlStatusDir: control.statusDir,
|
|
447
|
-
...(
|
|
448
|
-
? {
|
|
512
|
+
...(taskSources === null
|
|
513
|
+
? {}
|
|
514
|
+
: {
|
|
449
515
|
runtimeDir: control.runtimeDir,
|
|
450
|
-
|
|
451
|
-
taskId:
|
|
452
|
-
}
|
|
453
|
-
: {})
|
|
516
|
+
taskSources: taskSources.mountPaths,
|
|
517
|
+
taskId: taskId
|
|
518
|
+
})
|
|
454
519
|
}).map((mount) => ({
|
|
455
520
|
path: mount.containerPath,
|
|
456
521
|
expectedType: 'bind',
|
|
457
522
|
hostPaths: mount.hostPaths,
|
|
523
|
+
...(taskSources !== null && taskId !== null
|
|
524
|
+
&& mount.containerPath === `/workspace/.agents/workspace/active/${taskId}`
|
|
525
|
+
? { sourceAccessiblePaths: taskSources.accessiblePaths }
|
|
526
|
+
: {}),
|
|
458
527
|
expectedRW: !mount.readOnly
|
|
459
528
|
}));
|
|
460
529
|
const live = tools.flatMap((tool) => (tool.hostLiveMounts ?? []).flatMap(({ hostPath, containerSubpath }) => {
|
|
@@ -469,9 +538,7 @@ function expectedMounts(params) {
|
|
|
469
538
|
}];
|
|
470
539
|
}));
|
|
471
540
|
const persistentTools = tools
|
|
472
|
-
.filter((tool) => !tool.tmpfs
|
|
473
|
-
&& (config.agentClientSource === 'canonical'
|
|
474
|
-
|| actualMounts.has(tool.containerMount)))
|
|
541
|
+
.filter((tool) => !tool.tmpfs)
|
|
475
542
|
.map((tool) => ({
|
|
476
543
|
path: tool.containerMount,
|
|
477
544
|
expectedType: 'bind',
|
|
@@ -577,13 +644,10 @@ export function collectSandboxRecoverySnapshot(params) {
|
|
|
577
644
|
taskId: sandboxTaskIdLabel(params.config)
|
|
578
645
|
});
|
|
579
646
|
const selectedToolIds = new Set(tools.map((tool) => tool.id));
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
.map((tool) => tool.containerMount)
|
|
585
|
-
.filter((mountPath) => mountsByDestination.has(mountPath))
|
|
586
|
-
: [];
|
|
647
|
+
const unexpectedCapabilityMounts = capabilityPlan.cleanupInventory
|
|
648
|
+
.filter((tool) => isAgentClientId(tool.id) && !selectedToolIds.has(tool.id))
|
|
649
|
+
.map((tool) => tool.containerMount)
|
|
650
|
+
.filter((mountPath) => mountsByDestination.has(mountPath));
|
|
587
651
|
const mountSnapshots = expectedMounts({
|
|
588
652
|
config: params.config,
|
|
589
653
|
branch: params.branch,
|
|
@@ -616,7 +680,7 @@ export function collectSandboxRecoverySnapshot(params) {
|
|
|
616
680
|
sourceAccessible: expected.expectedType === 'tmpfs'
|
|
617
681
|
? true
|
|
618
682
|
: matchedHostPath !== undefined
|
|
619
|
-
&& hostSourceAccessible(
|
|
683
|
+
&& (expected.sourceAccessiblePaths ?? [matchedHostPath]).some((hostPath) => hostSourceAccessible(hostPath, expected.expectedRW))
|
|
620
684
|
};
|
|
621
685
|
});
|
|
622
686
|
return {
|
|
@@ -636,9 +700,7 @@ export function collectSandboxRecoverySnapshot(params) {
|
|
|
636
700
|
identityOk: typeof inspection.Id === 'string' && inspection.Id.length > 0
|
|
637
701
|
&& branchLabel === params.branch
|
|
638
702
|
&& sameSandboxWorkspaceIdentity(containerWorkspace, workspace),
|
|
639
|
-
runtimeCapabilityOk:
|
|
640
|
-
? labels[sandboxRuntimeCapabilityLabel(params.config)] === capabilityPlan.runtimeSignature
|
|
641
|
-
: undefined,
|
|
703
|
+
runtimeCapabilityOk: labels[sandboxRuntimeCapabilityLabel(params.config)] === capabilityPlan.runtimeSignature,
|
|
642
704
|
unexpectedCapabilityMounts,
|
|
643
705
|
mounts: mountSnapshots,
|
|
644
706
|
tmpfs,
|
|
@@ -813,11 +875,22 @@ export async function ensureSandboxReady(params) {
|
|
|
813
875
|
workspace: params.workspace ?? { mode: 'branch-only' }
|
|
814
876
|
});
|
|
815
877
|
}
|
|
816
|
-
if (params.forceRecreate) {
|
|
878
|
+
if (params.forceRecreate || (params.reentry === 'completed' && params.allowRecreate)) {
|
|
817
879
|
throw new Error('Explicit container recreation requested.');
|
|
818
880
|
}
|
|
819
881
|
if (!params.row.running) {
|
|
820
|
-
|
|
882
|
+
if (params.reentry === 'completed' && params.workspace?.mode === 'task-bound') {
|
|
883
|
+
startCompletedSandboxContainer({
|
|
884
|
+
config: params.config,
|
|
885
|
+
engine: params.engine,
|
|
886
|
+
container: params.row.name,
|
|
887
|
+
taskId: params.workspace.taskId,
|
|
888
|
+
start: startFn
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
startFn(params.engine, params.row.name);
|
|
893
|
+
}
|
|
821
894
|
const initial = assess({
|
|
822
895
|
config: params.config,
|
|
823
896
|
engine: params.engine,
|
|
@@ -930,6 +1003,34 @@ export async function ensureSandboxReady(params) {
|
|
|
930
1003
|
catch (error) {
|
|
931
1004
|
failure = error instanceof Error ? error : new Error(String(error));
|
|
932
1005
|
}
|
|
1006
|
+
if (params.reentry === 'completed') {
|
|
1007
|
+
if (params.workspace?.mode !== 'task-bound') {
|
|
1008
|
+
throw new Error('SANDBOX_COMPLETED_REENTRY_INVALID: completed re-entry requires a task-bound workspace');
|
|
1009
|
+
}
|
|
1010
|
+
const taskId = params.workspace.taskId;
|
|
1011
|
+
const commandChain = [
|
|
1012
|
+
`docker exec -it ${params.row.name} bash /usr/local/bin/sandbox-tmux-entry`,
|
|
1013
|
+
`ai sandbox rm ${taskId}`,
|
|
1014
|
+
`ai sandbox create ${params.branch}`
|
|
1015
|
+
];
|
|
1016
|
+
const code = params.allowRecreate || params.forceRecreate
|
|
1017
|
+
? 'SANDBOX_COMPLETED_RECREATE_UNSUPPORTED'
|
|
1018
|
+
: 'SANDBOX_COMPLETED_REENTRY_FAILED';
|
|
1019
|
+
const lead = code === 'SANDBOX_COMPLETED_RECREATE_UNSUPPORTED'
|
|
1020
|
+
? 'Completed sandbox replacement is unsupported; the existing container was not replaced.'
|
|
1021
|
+
: 'Completed sandbox recovery failed; the existing container was not replaced.';
|
|
1022
|
+
throw new Error([
|
|
1023
|
+
`${code}: ${lead}`,
|
|
1024
|
+
`Task: ${taskId}; container: ${params.row.name}; branch: ${params.branch}.`,
|
|
1025
|
+
'To preserve the current container state, enter it manually with:',
|
|
1026
|
+
` ${commandChain[0]}`,
|
|
1027
|
+
'If you intentionally want a replacement, the following is the full interactive sandbox cleanup, not a container-only delete.',
|
|
1028
|
+
'It may remove the worktree, local branch, tool/shell state, and branch share:',
|
|
1029
|
+
` ${commandChain[1]}`,
|
|
1030
|
+
` ${commandChain[2]}`,
|
|
1031
|
+
`Details: ${failure.message}`
|
|
1032
|
+
].join('\n'));
|
|
1033
|
+
}
|
|
933
1034
|
const dataBoundary = 'The existing container writable layer, ordinary /tmp data, processes, tmux sessions, and RAM state may be lost by replacement; worktree and host-managed sandbox data are preserved.';
|
|
934
1035
|
const recoveryTarget = params.workspace?.mode === 'task-bound'
|
|
935
1036
|
? params.workspace.taskId
|
|
@@ -79,6 +79,20 @@ export function resolveSandboxTarget(requestedRef, repoRoot) {
|
|
|
79
79
|
}
|
|
80
80
|
return { requestedRef, branch: requestedRef, workspace: { mode: 'branch-only' } };
|
|
81
81
|
}
|
|
82
|
+
export function resolveSandboxReentryContext(params) {
|
|
83
|
+
if (params.target.workspace.mode !== 'branch-only'
|
|
84
|
+
|| params.containerWorkspace.mode !== 'task-bound') {
|
|
85
|
+
return { workspace: params.target.workspace, reentry: 'standard' };
|
|
86
|
+
}
|
|
87
|
+
const task = resolveTaskWorkspace(params.containerWorkspace.taskId, params.repoRoot);
|
|
88
|
+
if (task.state !== 'completed' || task.branch !== params.target.branch) {
|
|
89
|
+
return { workspace: params.target.workspace, reentry: 'standard' };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
workspace: { mode: 'task-bound', taskId: task.taskId },
|
|
93
|
+
reentry: 'completed'
|
|
94
|
+
};
|
|
95
|
+
}
|
|
82
96
|
export function resolveSandboxCleanupTarget(requestedRef, repoRoot, options = {}) {
|
|
83
97
|
if (isRemovedHashShortIdInput(requestedRef)) {
|
|
84
98
|
throw new Error(`Invalid task short id '${requestedRef}': task short ids must use bare digits`);
|
|
@@ -171,14 +171,12 @@ export function materializeSandboxControl(params) {
|
|
|
171
171
|
const token = randomBytes(32).toString('hex');
|
|
172
172
|
const generation = randomBytes(16).toString('hex');
|
|
173
173
|
const repoRoot = fs.realpathSync.native(params.repoRoot);
|
|
174
|
-
const
|
|
175
|
-
version: 5,
|
|
174
|
+
const manifestDraft = {
|
|
176
175
|
engine: params.engine ?? 'docker',
|
|
177
176
|
repoRoot,
|
|
178
177
|
worktreeRoot: fs.realpathSync.native(params.worktreeRoot),
|
|
179
178
|
project: params.project,
|
|
180
179
|
container: params.container,
|
|
181
|
-
containerIdentity: { id: '', labels: {} },
|
|
182
180
|
branch: params.branch,
|
|
183
181
|
mode: params.identity.mode,
|
|
184
182
|
taskId: params.identity.mode === 'task-bound' ? params.identity.taskId : null,
|
|
@@ -189,16 +187,16 @@ export function materializeSandboxControl(params) {
|
|
|
189
187
|
processingDir,
|
|
190
188
|
runtimeDir
|
|
191
189
|
};
|
|
192
|
-
|
|
193
|
-
return { root, channelDir, statusDir, runtimeDir, manifestPath, token, generation };
|
|
190
|
+
return { root, channelDir, statusDir, runtimeDir, manifestPath, manifestDraft, token, generation };
|
|
194
191
|
}
|
|
195
192
|
export function finalizeSandboxControlManifest(setup, identity) {
|
|
196
193
|
if (!identity.engine || !identity.id)
|
|
197
194
|
throw new Error('SANDBOX_CONTROL_CONTAINER_ID_INVALID');
|
|
198
|
-
const manifest =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
195
|
+
const manifest = {
|
|
196
|
+
...setup.manifestDraft,
|
|
197
|
+
engine: identity.engine,
|
|
198
|
+
containerIdentity: { id: identity.id, labels: { ...identity.labels } }
|
|
199
|
+
};
|
|
202
200
|
const temporary = `${setup.manifestPath}.${process.pid}.${randomBytes(8).toString('hex')}.tmp`;
|
|
203
201
|
try {
|
|
204
202
|
fs.writeFileSync(temporary, `${JSON.stringify(manifest)}\n`, { mode: 0o600, flag: 'wx' });
|
|
@@ -207,5 +205,6 @@ export function finalizeSandboxControlManifest(setup, identity) {
|
|
|
207
205
|
finally {
|
|
208
206
|
fs.rmSync(temporary, { force: true });
|
|
209
207
|
}
|
|
208
|
+
return manifest;
|
|
210
209
|
}
|
|
211
210
|
//# sourceMappingURL=workspace-view.js.map
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { err, info, ok } from './log.js';
|
|
5
|
+
import { needsShell, resolveCommandFromAbsolutePath } from './run/host.js';
|
|
6
|
+
function canonicalPath(target) {
|
|
7
|
+
try {
|
|
8
|
+
return fs.realpathSync.native(target);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function comparable(target, platform) {
|
|
15
|
+
const resolved = path.resolve(target).replaceAll('\\', '/');
|
|
16
|
+
return platform === 'win32' ? resolved.toLowerCase() : resolved;
|
|
17
|
+
}
|
|
18
|
+
function samePath(left, right, platform) {
|
|
19
|
+
return comparable(left, platform) === comparable(right, platform);
|
|
20
|
+
}
|
|
21
|
+
function isWithin(root, target, platform) {
|
|
22
|
+
const normalizedRoot = comparable(root, platform).replace(/\/$/, '');
|
|
23
|
+
const normalizedTarget = comparable(target, platform).replace(/\/$/, '');
|
|
24
|
+
return normalizedTarget === normalizedRoot
|
|
25
|
+
|| normalizedTarget.startsWith(`${normalizedRoot}/`);
|
|
26
|
+
}
|
|
27
|
+
function packageInfoFromRoot(root) {
|
|
28
|
+
try {
|
|
29
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
30
|
+
return typeof packageJson.name === 'string'
|
|
31
|
+
? { root, name: packageJson.name }
|
|
32
|
+
: null;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function findPackageInfo(entry, platform) {
|
|
39
|
+
const canonicalEntry = canonicalPath(entry);
|
|
40
|
+
if (!canonicalEntry)
|
|
41
|
+
return null;
|
|
42
|
+
let current = path.dirname(canonicalEntry);
|
|
43
|
+
while (true) {
|
|
44
|
+
const canonicalRoot = canonicalPath(current);
|
|
45
|
+
const info = canonicalRoot ? packageInfoFromRoot(canonicalRoot) : null;
|
|
46
|
+
if (info && isWithin(info.root, canonicalEntry, platform))
|
|
47
|
+
return info;
|
|
48
|
+
const parent = path.dirname(current);
|
|
49
|
+
if (parent === current)
|
|
50
|
+
return null;
|
|
51
|
+
current = parent;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function packageInfoFromCommand(commandPath, platform) {
|
|
55
|
+
const direct = findPackageInfo(commandPath, platform);
|
|
56
|
+
if (direct)
|
|
57
|
+
return direct;
|
|
58
|
+
if (!/\.(?:cmd|bat)$/i.test(commandPath))
|
|
59
|
+
return null;
|
|
60
|
+
const globalPackageRoot = canonicalPath(path.join(path.dirname(commandPath), 'node_modules', '@fitlab-ai', 'agent-infra'));
|
|
61
|
+
return globalPackageRoot ? packageInfoFromRoot(globalPackageRoot) : null;
|
|
62
|
+
}
|
|
63
|
+
function visibleAliasError(packageRoot, platform, env) {
|
|
64
|
+
for (const alias of ['ai', 'agent-infra']) {
|
|
65
|
+
const absolute = resolveCommandFromAbsolutePath(alias, platform, env);
|
|
66
|
+
if (!absolute)
|
|
67
|
+
continue;
|
|
68
|
+
if (!canonicalPath(absolute))
|
|
69
|
+
continue;
|
|
70
|
+
const info = packageInfoFromCommand(absolute, platform);
|
|
71
|
+
if (info && !samePath(info.root, packageRoot, platform)) {
|
|
72
|
+
return `PATH shadowing detected: ${alias} resolves to ${info.root}, not ${packageRoot}.`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function resolveManager(name, expectedBasenames, platform, env) {
|
|
78
|
+
const absolute = resolveCommandFromAbsolutePath(name, platform, env);
|
|
79
|
+
if (!absolute) {
|
|
80
|
+
return { error: `${name} manager was not found on PATH.` };
|
|
81
|
+
}
|
|
82
|
+
let stat;
|
|
83
|
+
try {
|
|
84
|
+
stat = fs.statSync(absolute);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return { error: `Cannot inspect manager executable: ${absolute}.` };
|
|
88
|
+
}
|
|
89
|
+
if (!stat.isFile())
|
|
90
|
+
return { error: `Manager executable is not a regular file: ${absolute}.` };
|
|
91
|
+
if (!expectedBasenames.includes(path.basename(absolute).toLowerCase())) {
|
|
92
|
+
return { error: `Unexpected manager executable: ${absolute}.` };
|
|
93
|
+
}
|
|
94
|
+
const canonical = canonicalPath(absolute);
|
|
95
|
+
return canonical ? { path: canonical } : null;
|
|
96
|
+
}
|
|
97
|
+
function defaultRun(command, platform) {
|
|
98
|
+
const [file, ...args] = command;
|
|
99
|
+
if (!file)
|
|
100
|
+
return { status: null, error: new Error('run: missing command') };
|
|
101
|
+
const result = spawnSync(file, args, {
|
|
102
|
+
encoding: 'utf8',
|
|
103
|
+
shell: needsShell(file, platform),
|
|
104
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
status: result.status,
|
|
108
|
+
signal: result.signal,
|
|
109
|
+
stdout: result.stdout || '',
|
|
110
|
+
stderr: result.stderr || '',
|
|
111
|
+
error: result.error
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function runCommand(command, options, platform) {
|
|
115
|
+
return options.run ? options.run(command) : defaultRun(command, platform);
|
|
116
|
+
}
|
|
117
|
+
function probe(command, options, platform, label) {
|
|
118
|
+
const result = runCommand(command, options, platform);
|
|
119
|
+
if (result.error)
|
|
120
|
+
return { error: `${label} failed: ${result.error.message}` };
|
|
121
|
+
if (result.signal)
|
|
122
|
+
return { error: `${label} terminated by ${result.signal}.` };
|
|
123
|
+
if (result.status !== 0) {
|
|
124
|
+
const detail = result.stderr?.trim();
|
|
125
|
+
return { error: `${label} exited with ${result.status ?? 1}${detail ? `: ${detail}` : '.'}` };
|
|
126
|
+
}
|
|
127
|
+
const output = result.stdout?.trim() || '';
|
|
128
|
+
return output ? output : { error: `${label} returned no path.` };
|
|
129
|
+
}
|
|
130
|
+
function npmSource(packageInfo, options, platform, env) {
|
|
131
|
+
const manager = resolveManager('npm', platform === 'win32' ? ['npm.cmd', 'npm.bat'] : ['npm'], platform, env);
|
|
132
|
+
if (!manager)
|
|
133
|
+
return null;
|
|
134
|
+
if ('error' in manager)
|
|
135
|
+
return manager;
|
|
136
|
+
const prefixOutput = probe([manager.path, 'prefix', '--global'], options, platform, 'npm prefix --global');
|
|
137
|
+
if (typeof prefixOutput !== 'string')
|
|
138
|
+
return prefixOutput;
|
|
139
|
+
const rootOutput = probe([manager.path, 'root', '--global'], options, platform, 'npm root --global');
|
|
140
|
+
if (typeof rootOutput !== 'string')
|
|
141
|
+
return rootOutput;
|
|
142
|
+
const prefix = canonicalPath(prefixOutput);
|
|
143
|
+
const globalRoot = canonicalPath(rootOutput);
|
|
144
|
+
if (!prefix || !globalRoot)
|
|
145
|
+
return { error: 'npm global prefix or root is not an existing directory.' };
|
|
146
|
+
if (!isWithin(prefix, globalRoot, platform)) {
|
|
147
|
+
return { error: `npm global root ${globalRoot} is outside prefix ${prefix}.` };
|
|
148
|
+
}
|
|
149
|
+
const expectedPackageRoot = canonicalPath(path.join(globalRoot, '@fitlab-ai', 'agent-infra'));
|
|
150
|
+
if (!expectedPackageRoot || !samePath(expectedPackageRoot, packageInfo.root, platform)) {
|
|
151
|
+
return { error: `Current CLI package is not owned by npm global root ${globalRoot}.` };
|
|
152
|
+
}
|
|
153
|
+
if (packageInfo.name !== '@fitlab-ai/agent-infra') {
|
|
154
|
+
return { error: `Unexpected package name at ${packageInfo.root}: ${packageInfo.name}.` };
|
|
155
|
+
}
|
|
156
|
+
if (platform !== 'win32' && !isWithin(prefix, manager.path, platform)) {
|
|
157
|
+
return { error: `npm manager ${manager.path} is outside global prefix ${prefix}.` };
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
kind: 'npm',
|
|
161
|
+
managerPath: manager.path,
|
|
162
|
+
packageRoot: packageInfo.root,
|
|
163
|
+
updateArgs: ['update', '--global', '@fitlab-ai/agent-infra']
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function brewSource(packageInfo, options, platform, env) {
|
|
167
|
+
const manager = resolveManager('brew', ['brew', 'brew.exe', 'brew.cmd'], platform, env);
|
|
168
|
+
if (!manager)
|
|
169
|
+
return null;
|
|
170
|
+
if ('error' in manager)
|
|
171
|
+
return manager;
|
|
172
|
+
const homebrewOutput = probe([manager.path, '--prefix'], options, platform, 'brew --prefix');
|
|
173
|
+
if (typeof homebrewOutput !== 'string')
|
|
174
|
+
return homebrewOutput;
|
|
175
|
+
const formulaOutput = probe([manager.path, '--prefix', 'agent-infra'], options, platform, 'brew --prefix agent-infra');
|
|
176
|
+
if (typeof formulaOutput !== 'string')
|
|
177
|
+
return formulaOutput;
|
|
178
|
+
const homebrewRoot = canonicalPath(homebrewOutput);
|
|
179
|
+
const formulaRoot = canonicalPath(formulaOutput);
|
|
180
|
+
if (!homebrewRoot || !formulaRoot)
|
|
181
|
+
return { error: 'Homebrew prefix is not an existing directory.' };
|
|
182
|
+
if (!isWithin(homebrewRoot, manager.path, platform)) {
|
|
183
|
+
return { error: `brew manager ${manager.path} is outside Homebrew prefix ${homebrewRoot}.` };
|
|
184
|
+
}
|
|
185
|
+
if (!isWithin(homebrewRoot, formulaRoot, platform)) {
|
|
186
|
+
return { error: `agent-infra formula prefix ${formulaRoot} is outside Homebrew prefix ${homebrewRoot}.` };
|
|
187
|
+
}
|
|
188
|
+
if (!isWithin(formulaRoot, packageInfo.root, platform)) {
|
|
189
|
+
return { error: `Current CLI package is outside agent-infra formula prefix ${formulaRoot}.` };
|
|
190
|
+
}
|
|
191
|
+
if (packageInfo.name !== '@fitlab-ai/agent-infra') {
|
|
192
|
+
return { error: `Unexpected package name at ${packageInfo.root}: ${packageInfo.name}.` };
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
kind: 'brew',
|
|
196
|
+
managerPath: manager.path,
|
|
197
|
+
packageRoot: packageInfo.root,
|
|
198
|
+
updateArgs: ['upgrade', 'agent-infra']
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
export function detectUpdateSource(options = {}) {
|
|
202
|
+
const platform = options.platform ?? process.platform;
|
|
203
|
+
const env = options.env ?? process.env;
|
|
204
|
+
const entry = options.argv?.[1] ?? process.argv[1];
|
|
205
|
+
if (!entry)
|
|
206
|
+
return { source: null, error: 'Cannot determine the running CLI entry.' };
|
|
207
|
+
const packageInfo = findPackageInfo(entry, platform);
|
|
208
|
+
if (!packageInfo || packageInfo.name !== '@fitlab-ai/agent-infra') {
|
|
209
|
+
return { source: null, error: 'Running CLI is not a persistent @fitlab-ai/agent-infra package installation.' };
|
|
210
|
+
}
|
|
211
|
+
const aliasError = visibleAliasError(packageInfo.root, platform, env);
|
|
212
|
+
if (aliasError)
|
|
213
|
+
return { source: null, error: aliasError };
|
|
214
|
+
const candidates = [];
|
|
215
|
+
const errors = [];
|
|
216
|
+
for (const candidate of [
|
|
217
|
+
npmSource(packageInfo, options, platform, env),
|
|
218
|
+
brewSource(packageInfo, options, platform, env)
|
|
219
|
+
]) {
|
|
220
|
+
if (!candidate)
|
|
221
|
+
continue;
|
|
222
|
+
if ('error' in candidate)
|
|
223
|
+
errors.push(candidate.error);
|
|
224
|
+
else
|
|
225
|
+
candidates.push(candidate);
|
|
226
|
+
}
|
|
227
|
+
if (candidates.length === 1) {
|
|
228
|
+
const [source] = candidates;
|
|
229
|
+
if (source)
|
|
230
|
+
return { source };
|
|
231
|
+
}
|
|
232
|
+
if (candidates.length > 1)
|
|
233
|
+
return { source: null, error: 'Multiple package managers claim the running CLI; refusing to choose.' };
|
|
234
|
+
return {
|
|
235
|
+
source: null,
|
|
236
|
+
error: errors.length > 0
|
|
237
|
+
? errors.join(' ')
|
|
238
|
+
: 'No supported persistent npm or Homebrew installation owns the running CLI.'
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function managerStable(source, platform) {
|
|
242
|
+
const current = canonicalPath(source.managerPath);
|
|
243
|
+
return current !== null && samePath(current, source.managerPath, platform);
|
|
244
|
+
}
|
|
245
|
+
export async function cmdUpdate(options = {}) {
|
|
246
|
+
const platform = options.platform ?? process.platform;
|
|
247
|
+
const detected = detectUpdateSource(options);
|
|
248
|
+
if (!detected.source) {
|
|
249
|
+
err(`Cannot update agent-infra CLI: ${detected.error ?? 'unknown source'}`);
|
|
250
|
+
err('Install persistently with "npm install -g @fitlab-ai/agent-infra" or "brew install fitlab-ai/tap/agent-infra".');
|
|
251
|
+
return 1;
|
|
252
|
+
}
|
|
253
|
+
const source = detected.source;
|
|
254
|
+
if (!managerStable(source, platform)) {
|
|
255
|
+
err(`Cannot update agent-infra CLI: manager path changed: ${source.managerPath}.`);
|
|
256
|
+
return 1;
|
|
257
|
+
}
|
|
258
|
+
info(`Updating agent-infra CLI via ${source.kind}.`);
|
|
259
|
+
const result = runCommand([source.managerPath, ...source.updateArgs], options, platform);
|
|
260
|
+
if (result.error) {
|
|
261
|
+
err(`agent-infra CLI update failed: ${result.error.message}`);
|
|
262
|
+
return 1;
|
|
263
|
+
}
|
|
264
|
+
if (result.signal) {
|
|
265
|
+
err(`agent-infra CLI update terminated by ${result.signal}.`);
|
|
266
|
+
return 1;
|
|
267
|
+
}
|
|
268
|
+
if (result.status !== 0) {
|
|
269
|
+
err(`agent-infra CLI update failed with exit code ${result.status ?? 1}.`);
|
|
270
|
+
const detail = result.stderr?.trim();
|
|
271
|
+
if (detail)
|
|
272
|
+
err(detail);
|
|
273
|
+
return result.status && result.status > 0 ? result.status : 1;
|
|
274
|
+
}
|
|
275
|
+
ok('agent-infra CLI updated successfully!');
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=self-update.js.map
|
|
@@ -20,16 +20,16 @@ export function commandHelp() {
|
|
|
20
20
|
'/sandbox ls',
|
|
21
21
|
'/sandbox show {ref}',
|
|
22
22
|
'/sandbox vm status',
|
|
23
|
-
'/task decisions [--task {ref}] [--item {selector}]',
|
|
24
|
-
'/task log [--task {ref}]',
|
|
23
|
+
'/task decisions [--task {ref} | -t {ref}] [--item {selector} | -i {selector}]',
|
|
24
|
+
'/task log [--task {ref} | -t {ref}]',
|
|
25
25
|
'/task ls',
|
|
26
|
-
'/task show [--task {ref}]',
|
|
27
|
-
'/task status [--task {ref}]',
|
|
26
|
+
'/task show [--task {ref} | -t {ref}]',
|
|
27
|
+
'/task status [--task {ref} | -t {ref}]',
|
|
28
28
|
'Write:',
|
|
29
29
|
'/sandbox create {ref}',
|
|
30
30
|
'/sandbox start {ref}',
|
|
31
31
|
'Exec:',
|
|
32
|
-
'/decide [--task {ref}] --item {ordinal|ledger-id} [--needs-implementation true|false] {decision}',
|
|
32
|
+
'/decide [--task {ref} | -t {ref}] (--item {ordinal|ledger-id} | -i {ordinal|ledger-id}) [--needs-implementation true|false] {decision}',
|
|
33
33
|
'/run create-task {description}',
|
|
34
34
|
'/run {skill} {task-ref}'
|
|
35
35
|
].join('\n');
|
|
@@ -84,5 +84,8 @@ function pairEntries(entries) {
|
|
|
84
84
|
}
|
|
85
85
|
return rows;
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
function startedBackedRows(rows) {
|
|
88
|
+
return rows.filter((row) => row.started !== '');
|
|
89
|
+
}
|
|
90
|
+
export { activityAttempt, appendActivityEntry, commitAttemptStartedNote, locateActivityLog, pairEntries, parseActivityLog, parseCommitAttemptStarted, startedBackedRows };
|
|
88
91
|
//# sourceMappingURL=activity-log.js.map
|
|
@@ -305,7 +305,7 @@ function resolveCodeContext(inventory, options) {
|
|
|
305
305
|
}
|
|
306
306
|
const verdict = parseVerdict(reviewPlan.latest.path);
|
|
307
307
|
if (!verdict.ok)
|
|
308
|
-
return contextFailure(inventory, '
|
|
308
|
+
return contextFailure(inventory, 'ARTIFACT_VERDICT_INVALID', `${verdict.code}: ${verdict.message}`, reviewPlan.latest.name);
|
|
309
309
|
if (verdict.verdict !== 'Approved') {
|
|
310
310
|
return contextFailure(inventory, 'ARTIFACT_REFERENCE_INVALID', `latest ${reviewPlan.latest.name} is not approved`, reviewPlan.latest.name);
|
|
311
311
|
}
|
|
@@ -320,6 +320,8 @@ function resolveCodeContext(inventory, options) {
|
|
|
320
320
|
}
|
|
321
321
|
if (reviewPlan.latest && reviewPlan.reviewedInput?.name === plan.latest.name) {
|
|
322
322
|
const verdict = parseVerdict(reviewPlan.latest.path);
|
|
323
|
+
if (!verdict.ok)
|
|
324
|
+
return contextFailure(inventory, 'ARTIFACT_VERDICT_INVALID', `${verdict.code}: ${verdict.message}`, reviewPlan.latest.name);
|
|
323
325
|
const reviewedPlanSha256 = sha256File(reviewPlan.reviewedInput.path);
|
|
324
326
|
if (verdict.ok && verdict.verdict === 'Approved' && (codePlanInput.input.name !== plan.latest.name || codePlanInput.inputSha256 !== reviewedPlanSha256)) {
|
|
325
327
|
return withCodeMode(inventory, inputs, 'ready', 'init', codeMax, reviewMax, verdict.verdict, reviewPlan.latest.name, `Latest ${reviewPlan.latest.name} approves plan content not captured by the latest code input receipt. Entering replan-driven init.`);
|
|
@@ -334,7 +336,7 @@ function resolveCodeContext(inventory, options) {
|
|
|
334
336
|
return contextFailure(inventory, 'ARTIFACT_INPUT_MISSING', 'latest review-code artifact is required');
|
|
335
337
|
const verdict = parseVerdict(review.path);
|
|
336
338
|
if (!verdict.ok)
|
|
337
|
-
return contextFailure(inventory, '
|
|
339
|
+
return contextFailure(inventory, 'ARTIFACT_VERDICT_INVALID', `${verdict.code}: ${verdict.message}`, review.name);
|
|
338
340
|
if (verdict.verdict === 'Approved') {
|
|
339
341
|
const decision = resolveDecisionImplementationInput(inventory, review);
|
|
340
342
|
if ('error' in decision)
|