@fitlab-ai/agent-infra 0.9.2 → 0.9.4
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 +12 -9
- package/README.zh-CN.md +12 -9
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +134 -108
- package/dist/lib/agent-clients/adapter.js +137 -4
- package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
- package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
- package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
- package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +27 -4
- package/dist/lib/agent-clients/adapters/codex.js +9 -0
- package/dist/lib/agent-clients/adapters/index.js +2 -2
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
- package/dist/lib/agent-clients/adapters/opencode.js +24 -3
- package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/dist/lib/agent-clients/config.js +42 -4
- package/dist/lib/agent-clients/next-steps.js +11 -2
- package/dist/lib/agent-clients/reconcile.js +2 -1
- package/dist/lib/agent-clients/registry.js +16 -2
- package/dist/lib/agent-clients/schema.js +18 -0
- package/dist/lib/agent-clients/tokens.js +49 -0
- package/dist/lib/agent-clients/types.js +1 -1
- package/dist/lib/defaults.json +1 -1
- package/dist/lib/git/workflow.js +54 -1
- package/dist/lib/internal/agent-client.js +61 -4
- package/dist/lib/internal/git-workflow.js +6 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +29 -2
- package/dist/lib/internal/platform-issue.js +7 -0
- package/dist/lib/internal/platform-pr-review.js +132 -0
- package/dist/lib/internal/platform-pr.js +20 -2
- package/dist/lib/internal/platform-release-notes.js +52 -16
- package/dist/lib/internal/pr-review-grade.js +140 -0
- package/dist/lib/internal/release-workflow.js +13 -6
- package/dist/lib/internal/sandbox-control.js +24 -0
- package/dist/lib/internal/task-activity.js +105 -0
- package/dist/lib/internal/task-event.js +17 -1
- package/dist/lib/internal/task-ledger.js +12 -3
- package/dist/lib/internal/task-lifecycle.js +26 -1
- package/dist/lib/internal/task-orchestration.js +167 -18
- package/dist/lib/internal/task-review.js +8 -3
- package/dist/lib/platform/adapters.js +8 -1
- package/dist/lib/platform/completion-backfill.js +122 -0
- package/dist/lib/platform/github-client.js +19 -2
- package/dist/lib/platform/issue-comments.js +3 -2
- package/dist/lib/platform/merged-pr-equivalence.js +115 -5
- package/dist/lib/platform/pr-checks.js +65 -42
- package/dist/lib/platform/pr-review.js +166 -0
- package/dist/lib/platform/pull-requests.js +371 -4
- package/dist/lib/platform/release-notes.js +85 -3
- package/dist/lib/platform/releases.js +13 -5
- package/dist/lib/pr-review/evidence-grading.js +229 -0
- package/dist/lib/process-data/index.js +239 -0
- package/dist/lib/process-data/normalize.js +198 -0
- package/dist/lib/process-data/privacy.js +33 -0
- package/dist/lib/process-data/reconcile.js +95 -0
- package/dist/lib/process-data/repair.js +58 -0
- package/dist/lib/process-data/sources.js +345 -0
- package/dist/lib/process-data/store.js +282 -0
- package/dist/lib/process-data/types.js +2 -0
- package/dist/lib/run/index.js +4 -3
- package/dist/lib/run/tui.js +10 -13
- package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
- package/dist/lib/sandbox/capture.js +7 -2
- package/dist/lib/sandbox/commands/create.js +109 -248
- package/dist/lib/sandbox/commands/enter.js +15 -47
- package/dist/lib/sandbox/commands/list-running.js +16 -6
- package/dist/lib/sandbox/commands/ls.js +10 -6
- package/dist/lib/sandbox/commands/rebuild.js +4 -3
- package/dist/lib/sandbox/commands/refresh.js +4 -84
- package/dist/lib/sandbox/commands/rm.js +24 -3
- package/dist/lib/sandbox/commands/show.js +13 -4
- package/dist/lib/sandbox/commands/start.js +7 -4
- package/dist/lib/sandbox/config.js +2 -0
- package/dist/lib/sandbox/constants.js +6 -0
- package/dist/lib/sandbox/control/client.js +44 -0
- package/dist/lib/sandbox/control/protocol.js +35 -0
- package/dist/lib/sandbox/control/server.js +109 -0
- package/dist/lib/sandbox/dockerfile.js +36 -13
- package/dist/lib/sandbox/mounts.js +17 -4
- package/dist/lib/sandbox/recovery.js +128 -5
- package/dist/lib/sandbox/redaction.js +12 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/dist/lib/sandbox/workspace-identity.js +95 -0
- package/dist/lib/sandbox/workspace-view.js +103 -0
- package/dist/lib/task/activity-intent.js +307 -0
- package/dist/lib/task/artifact-lifecycle.js +2 -1
- package/dist/lib/task/commands/log.js +15 -14
- package/dist/lib/task/commit-finalization.js +267 -0
- package/dist/lib/task/commit-intent.js +165 -0
- package/dist/lib/task/decision-intents.js +31 -17
- package/dist/lib/task/delegation-receipts.js +25 -4
- package/dist/lib/task/events.js +67 -28
- package/dist/lib/task/finalization-artifacts.js +35 -0
- package/dist/lib/task/implementation-inputs.js +54 -7
- package/dist/lib/task/ledger-intents.js +35 -1
- package/dist/lib/task/lifecycle-execution.js +46 -0
- package/dist/lib/task/orchestration.js +753 -54
- package/dist/lib/task/resolve-ref.js +41 -1
- package/dist/lib/task/review-finalization.js +29 -9
- package/dist/lib/task/task-execution-lock.js +231 -0
- package/dist/lib/task/verification-checks.js +1 -1
- package/dist/lib/task/verification-engine.js +218 -2
- package/dist/lib/task/verification.js +4 -3
- package/dist/lib/task/workspace-snapshot.js +17 -5
- package/lib/agent-clients/adapter.ts +201 -4
- package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
- package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
- package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
- package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
- package/lib/agent-clients/adapters/claude-code.ts +31 -4
- package/lib/agent-clients/adapters/codex.ts +9 -0
- package/lib/agent-clients/adapters/index.ts +2 -2
- package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
- package/lib/agent-clients/adapters/opencode.ts +27 -3
- package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/lib/agent-clients/config.ts +49 -4
- package/lib/agent-clients/next-steps.ts +15 -2
- package/lib/agent-clients/reconcile.ts +2 -1
- package/lib/agent-clients/registry.ts +19 -1
- package/lib/agent-clients/schema.ts +18 -0
- package/lib/agent-clients/tokens.ts +53 -0
- package/lib/agent-clients/types.ts +15 -1
- package/lib/defaults.json +1 -1
- package/lib/git/workflow.ts +66 -2
- package/lib/internal/agent-client.ts +59 -4
- package/lib/internal/git-workflow.ts +5 -1
- package/lib/internal/platform-checks.ts +4 -4
- package/lib/internal/platform-comment.ts +17 -2
- package/lib/internal/platform-issue.ts +4 -0
- package/lib/internal/platform-pr-review.ts +104 -0
- package/lib/internal/platform-pr.ts +14 -1
- package/lib/internal/platform-release-notes.ts +59 -16
- package/lib/internal/pr-review-grade.ts +130 -0
- package/lib/internal/release-workflow.ts +13 -6
- package/lib/internal/sandbox-control.ts +24 -0
- package/lib/internal/task-activity.ts +91 -0
- package/lib/internal/task-event.ts +9 -1
- package/lib/internal/task-ledger.ts +12 -3
- package/lib/internal/task-lifecycle.ts +26 -1
- package/lib/internal/task-orchestration.ts +123 -14
- package/lib/internal/task-review.ts +8 -3
- package/lib/platform/adapters.ts +29 -2
- package/lib/platform/completion-backfill.ts +134 -0
- package/lib/platform/github-client.ts +20 -2
- package/lib/platform/issue-comments.ts +3 -2
- package/lib/platform/merged-pr-equivalence.ts +127 -5
- package/lib/platform/pr-checks.ts +73 -46
- package/lib/platform/pr-review.ts +201 -0
- package/lib/platform/pull-requests.ts +444 -3
- package/lib/platform/release-notes.ts +100 -4
- package/lib/platform/releases.ts +16 -4
- package/lib/pr-review/evidence-grading.ts +318 -0
- package/lib/process-data/index.ts +224 -0
- package/lib/process-data/normalize.ts +202 -0
- package/lib/process-data/privacy.ts +41 -0
- package/lib/process-data/reconcile.ts +110 -0
- package/lib/process-data/repair.ts +64 -0
- package/lib/process-data/sources.ts +354 -0
- package/lib/process-data/store.ts +315 -0
- package/lib/process-data/types.ts +135 -0
- package/lib/run/index.ts +5 -3
- package/lib/run/tui.ts +14 -12
- package/lib/sandbox/agent-client-reconciler.ts +44 -2
- package/lib/sandbox/capture.ts +9 -2
- package/lib/sandbox/commands/create.ts +126 -280
- package/lib/sandbox/commands/enter.ts +13 -59
- package/lib/sandbox/commands/list-running.ts +23 -6
- package/lib/sandbox/commands/ls.ts +22 -6
- package/lib/sandbox/commands/rebuild.ts +4 -3
- package/lib/sandbox/commands/refresh.ts +4 -126
- package/lib/sandbox/commands/rm.ts +24 -3
- package/lib/sandbox/commands/show.ts +30 -4
- package/lib/sandbox/commands/start.ts +6 -4
- package/lib/sandbox/config.ts +4 -0
- package/lib/sandbox/constants.ts +8 -0
- package/lib/sandbox/control/client.ts +55 -0
- package/lib/sandbox/control/protocol.ts +75 -0
- package/lib/sandbox/control/server.ts +117 -0
- package/lib/sandbox/dockerfile.ts +66 -13
- package/lib/sandbox/mounts.ts +24 -4
- package/lib/sandbox/recovery.ts +163 -6
- package/lib/sandbox/redaction.ts +14 -0
- package/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/lib/sandbox/tool-types.ts +16 -0
- package/lib/sandbox/workspace-identity.ts +123 -0
- package/lib/sandbox/workspace-view.ts +143 -0
- package/lib/server/server.schema.json +2 -2
- package/lib/task/activity-intent.ts +417 -0
- package/lib/task/artifact-lifecycle.ts +2 -1
- package/lib/task/commands/log.ts +15 -14
- package/lib/task/commit-finalization.ts +356 -0
- package/lib/task/commit-intent.ts +230 -0
- package/lib/task/decision-intents.ts +32 -12
- package/lib/task/delegation-receipts.ts +34 -5
- package/lib/task/events.ts +75 -30
- package/lib/task/finalization-artifacts.ts +58 -0
- package/lib/task/implementation-inputs.ts +62 -8
- package/lib/task/ledger-intents.ts +43 -3
- package/lib/task/lifecycle-execution.ts +106 -0
- package/lib/task/orchestration.ts +1019 -72
- package/lib/task/resolve-ref.ts +46 -1
- package/lib/task/review-finalization.ts +40 -9
- package/lib/task/task-execution-lock.ts +296 -0
- package/lib/task/verification-checks.ts +1 -1
- package/lib/task/verification-engine.ts +249 -2
- package/lib/task/verification.ts +5 -3
- package/lib/task/workspace-snapshot.ts +26 -5
- package/package.json +3 -3
- package/templates/.agents/QUICKSTART.en.md +25 -8
- package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
- package/templates/.agents/README.en.md +10 -7
- package/templates/.agents/README.zh-CN.md +10 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +4 -2
- package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
- package/templates/.agents/rules/human-decision-context.en.md +11 -2
- package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
- package/templates/.agents/rules/issue-fields.github.en.md +3 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-sync.github.en.md +7 -3
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
- package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
- package/templates/.agents/rules/next-step-output.en.md +3 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
- package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-sync.en.md +3 -1
- package/templates/.agents/rules/pr-sync.github.en.md +11 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
- package/templates/.agents/rules/release-commands.en.md +1 -1
- package/templates/.agents/rules/release-commands.github.en.md +12 -2
- package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
- package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +13 -0
- package/templates/.agents/rules/task-management.zh-CN.md +13 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
- package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +12 -9
- package/templates/.agents/skills/commit/SKILL.en.md +23 -11
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +29 -11
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- 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/commit/reference/task-status-update.en.md +4 -4
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +22 -7
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +22 -7
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -5
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/release/SKILL.en.md +16 -10
- package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
- package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-pr/SKILL.en.md +186 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +186 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +98 -107
- package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
- package/templates/.agents/workflows/code-review.en.yaml +1 -1
- package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +4 -4
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
- package/templates/.agents/workflows/refactoring.en.yaml +3 -3
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
- package/templates/.claude/commands/post-release.en.md +1 -0
- package/templates/.claude/commands/post-release.zh-CN.md +1 -0
- package/templates/.claude/commands/review-pr.en.md +8 -0
- package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.claude/rules/tool-preferences.en.md +20 -0
- package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
- package/templates/.opencode/commands/post-release.en.md +2 -0
- package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
- package/templates/.opencode/commands/review-pr.en.md +11 -0
- package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
- package/templates/.opencode/commands/watch-pr.en.md +1 -1
- package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
- package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
- package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
- package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
- package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/release.en.toml +0 -8
- package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/test.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
- package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
- package/templates/.gemini/settings.json +0 -3
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getProcessStartTime } from '../../server/process-state.ts';
|
|
5
|
+
import {
|
|
6
|
+
bindSandboxControlTask,
|
|
7
|
+
validateSandboxControlRequest,
|
|
8
|
+
type SandboxControlManifest,
|
|
9
|
+
type SandboxControlResponse
|
|
10
|
+
} from './protocol.ts';
|
|
11
|
+
|
|
12
|
+
function readManifest(manifestPath: string): SandboxControlManifest {
|
|
13
|
+
const stat = fs.lstatSync(manifestPath);
|
|
14
|
+
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
15
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as SandboxControlManifest;
|
|
16
|
+
if (
|
|
17
|
+
manifest.version !== 1
|
|
18
|
+
|| typeof manifest.repoRoot !== 'string'
|
|
19
|
+
|| typeof manifest.channelDir !== 'string'
|
|
20
|
+
|| typeof manifest.token !== 'string'
|
|
21
|
+
) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
22
|
+
if (path.resolve(manifest.channelDir) !== path.join(path.dirname(path.resolve(manifestPath)), 'channel')) {
|
|
23
|
+
throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
24
|
+
}
|
|
25
|
+
return manifest;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function assertRealDirectory(directory: string, parent?: string): void {
|
|
29
|
+
const stat = fs.lstatSync(directory);
|
|
30
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('SANDBOX_CONTROL_CHANNEL_INVALID');
|
|
31
|
+
if (parent && path.dirname(fs.realpathSync.native(directory)) !== fs.realpathSync.native(parent)) {
|
|
32
|
+
throw new Error('SANDBOX_CONTROL_CHANNEL_INVALID');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function writeResponse(directory: string, response: SandboxControlResponse): void {
|
|
37
|
+
const temporary = path.join(directory, `.${response.id}.tmp`);
|
|
38
|
+
fs.writeFileSync(temporary, `${JSON.stringify(response)}\n`, { mode: 0o600, flag: 'wx' });
|
|
39
|
+
fs.renameSync(temporary, path.join(directory, `${response.id}.json`));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function serveSandboxControl(manifestPath: string, signal: AbortSignal = new AbortController().signal): void {
|
|
43
|
+
const manifest = readManifest(manifestPath);
|
|
44
|
+
const brokerPath = path.join(path.dirname(manifestPath), 'broker.json');
|
|
45
|
+
const startTime = getProcessStartTime(process.pid);
|
|
46
|
+
if (!startTime) throw new Error('SANDBOX_CONTROL_BROKER_IDENTITY_UNAVAILABLE');
|
|
47
|
+
fs.writeFileSync(brokerPath, `${JSON.stringify({
|
|
48
|
+
version: 1,
|
|
49
|
+
pid: process.pid,
|
|
50
|
+
startTime,
|
|
51
|
+
token: manifest.token
|
|
52
|
+
})}\n`, { mode: 0o600, flag: 'wx' });
|
|
53
|
+
const requestsDir = path.join(manifest.channelDir, 'requests');
|
|
54
|
+
const responsesDir = path.join(manifest.channelDir, 'responses');
|
|
55
|
+
assertRealDirectory(manifest.channelDir);
|
|
56
|
+
fs.mkdirSync(requestsDir, { recursive: true, mode: 0o700 });
|
|
57
|
+
fs.mkdirSync(responsesDir, { recursive: true, mode: 0o700 });
|
|
58
|
+
assertRealDirectory(requestsDir, manifest.channelDir);
|
|
59
|
+
assertRealDirectory(responsesDir, manifest.channelDir);
|
|
60
|
+
const consumed = new Set<string>();
|
|
61
|
+
while (!signal.aborted) {
|
|
62
|
+
const current = readManifest(manifestPath);
|
|
63
|
+
if (current.token !== manifest.token) return;
|
|
64
|
+
assertRealDirectory(requestsDir, manifest.channelDir);
|
|
65
|
+
assertRealDirectory(responsesDir, manifest.channelDir);
|
|
66
|
+
for (const name of fs.readdirSync(requestsDir).sort()) {
|
|
67
|
+
if (!/^[a-f0-9-]{16,64}\.json$/.test(name)) continue;
|
|
68
|
+
const requestPath = path.join(requestsDir, name);
|
|
69
|
+
let id = name.slice(0, -5);
|
|
70
|
+
let response: SandboxControlResponse;
|
|
71
|
+
try {
|
|
72
|
+
const stat = fs.lstatSync(requestPath);
|
|
73
|
+
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error('SANDBOX_CONTROL_REQUEST_INVALID');
|
|
74
|
+
const request = validateSandboxControlRequest(
|
|
75
|
+
JSON.parse(fs.readFileSync(requestPath, 'utf8')),
|
|
76
|
+
manifest
|
|
77
|
+
);
|
|
78
|
+
id = request.id;
|
|
79
|
+
if (consumed.has(id)) throw new Error('SANDBOX_CONTROL_REQUEST_REPLAYED');
|
|
80
|
+
consumed.add(id);
|
|
81
|
+
const result = spawnSync(
|
|
82
|
+
process.execPath,
|
|
83
|
+
['--experimental-strip-types', '--no-warnings', process.argv[1]!, request.family, ...bindSandboxControlTask(request, manifest.taskId!)],
|
|
84
|
+
{
|
|
85
|
+
cwd: manifest.repoRoot,
|
|
86
|
+
encoding: 'utf8',
|
|
87
|
+
env: Object.fromEntries(
|
|
88
|
+
Object.entries(process.env).filter(([key]) => !key.startsWith('AGENT_INFRA_CONTROL_'))
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
response = {
|
|
93
|
+
version: 1,
|
|
94
|
+
id,
|
|
95
|
+
exitCode: result.status ?? 1,
|
|
96
|
+
stdout: result.stdout ?? '',
|
|
97
|
+
stderr: result.stderr ?? ''
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
response = {
|
|
101
|
+
version: 1,
|
|
102
|
+
id,
|
|
103
|
+
exitCode: 1,
|
|
104
|
+
stdout: '',
|
|
105
|
+
stderr: `${error instanceof Error ? error.message : String(error)}\n`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
fs.unlinkSync(requestPath);
|
|
110
|
+
writeResponse(responsesDir, response);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -9,6 +9,23 @@ const RUNTIMES_DIR = path.join(
|
|
|
9
9
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
10
10
|
'runtimes'
|
|
11
11
|
);
|
|
12
|
+
const AGENT_CLIENT_RUNTIMES_DIR = path.join(
|
|
13
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
14
|
+
'..',
|
|
15
|
+
'agent-clients',
|
|
16
|
+
'adapters',
|
|
17
|
+
'runtimes'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
type DockerfileImageContribution = Readonly<{
|
|
21
|
+
dockerfileFragments: readonly string[];
|
|
22
|
+
dotfilesExclusions: readonly string[];
|
|
23
|
+
}>;
|
|
24
|
+
|
|
25
|
+
const EMPTY_IMAGE_CONTRIBUTION: DockerfileImageContribution = Object.freeze({
|
|
26
|
+
dockerfileFragments: Object.freeze([]),
|
|
27
|
+
dotfilesExclusions: Object.freeze([])
|
|
28
|
+
});
|
|
12
29
|
|
|
13
30
|
type DockerfileConfig = {
|
|
14
31
|
repoRoot: string;
|
|
@@ -29,7 +46,29 @@ export function availableRuntimes(): string[] {
|
|
|
29
46
|
.sort();
|
|
30
47
|
}
|
|
31
48
|
|
|
32
|
-
function
|
|
49
|
+
function exclusionsFragment(exclusions: readonly string[]): string {
|
|
50
|
+
return [
|
|
51
|
+
"RUN mkdir -p /etc/agent-infra && cat > /etc/agent-infra/dotfiles-exclusions <<'EXCLUSIONS'",
|
|
52
|
+
...exclusions,
|
|
53
|
+
'EXCLUSIONS'
|
|
54
|
+
].join('\n');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function adapterFragmentPath(fragment: string): string {
|
|
58
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(fragment)) {
|
|
59
|
+
throw new Error(`Invalid Agent Client Dockerfile fragment: ${fragment}`);
|
|
60
|
+
}
|
|
61
|
+
const fragmentPath = path.join(AGENT_CLIENT_RUNTIMES_DIR, `${fragment}.dockerfile`);
|
|
62
|
+
if (!fs.existsSync(fragmentPath)) {
|
|
63
|
+
throw new Error(`Unknown Agent Client Dockerfile fragment: ${fragment}`);
|
|
64
|
+
}
|
|
65
|
+
return fragmentPath;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function dockerfileContent(
|
|
69
|
+
config: DockerfileConfig,
|
|
70
|
+
image: DockerfileImageContribution = EMPTY_IMAGE_CONTRIBUTION
|
|
71
|
+
): string {
|
|
33
72
|
if (config.dockerfile) {
|
|
34
73
|
const customPath = path.resolve(config.repoRoot, config.dockerfile);
|
|
35
74
|
if (!fs.existsSync(customPath)) {
|
|
@@ -48,26 +87,37 @@ function dockerfileContent(config: DockerfileConfig): string {
|
|
|
48
87
|
}
|
|
49
88
|
|
|
50
89
|
const fragments = [
|
|
51
|
-
'base.dockerfile',
|
|
52
|
-
|
|
53
|
-
|
|
90
|
+
fs.readFileSync(path.join(RUNTIMES_DIR, 'base.dockerfile'), 'utf8').trimEnd(),
|
|
91
|
+
exclusionsFragment(image.dotfilesExclusions),
|
|
92
|
+
...image.dockerfileFragments.map((fragment) =>
|
|
93
|
+
fs.readFileSync(adapterFragmentPath(fragment), 'utf8').trimEnd()
|
|
94
|
+
),
|
|
95
|
+
...config.runtimes.map((runtime) =>
|
|
96
|
+
fs.readFileSync(path.join(RUNTIMES_DIR, `${runtime}.dockerfile`), 'utf8').trimEnd()
|
|
97
|
+
),
|
|
98
|
+
fs.readFileSync(path.join(RUNTIMES_DIR, 'ai-tools.dockerfile'), 'utf8').trimEnd()
|
|
54
99
|
];
|
|
55
100
|
|
|
56
101
|
const content = fragments
|
|
57
|
-
.map((fragment) => fs.readFileSync(path.join(RUNTIMES_DIR, fragment), 'utf8').trimEnd())
|
|
58
102
|
.join('\n\n');
|
|
59
103
|
|
|
60
104
|
return `${content}\n`;
|
|
61
105
|
}
|
|
62
106
|
|
|
63
|
-
export function dockerfileSignature(
|
|
107
|
+
export function dockerfileSignature(
|
|
108
|
+
config: DockerfileConfig,
|
|
109
|
+
image: DockerfileImageContribution = EMPTY_IMAGE_CONTRIBUTION
|
|
110
|
+
): string {
|
|
64
111
|
return createHash('sha256')
|
|
65
|
-
.update(dockerfileContent(config))
|
|
112
|
+
.update(dockerfileContent(config, image))
|
|
66
113
|
.digest('hex')
|
|
67
114
|
.slice(0, 12);
|
|
68
115
|
}
|
|
69
116
|
|
|
70
|
-
export function prepareDockerfile(
|
|
117
|
+
export function prepareDockerfile(
|
|
118
|
+
config: DockerfileConfig,
|
|
119
|
+
image: DockerfileImageContribution = EMPTY_IMAGE_CONTRIBUTION
|
|
120
|
+
): { path: string; signature: string; cleanup: () => void } {
|
|
71
121
|
if (config.dockerfile) {
|
|
72
122
|
const customPath = path.resolve(config.repoRoot, config.dockerfile);
|
|
73
123
|
if (!fs.existsSync(customPath)) {
|
|
@@ -76,26 +126,29 @@ export function prepareDockerfile(config: DockerfileConfig): { path: string; sig
|
|
|
76
126
|
|
|
77
127
|
return {
|
|
78
128
|
path: customPath,
|
|
79
|
-
signature: dockerfileSignature(config),
|
|
129
|
+
signature: dockerfileSignature(config, image),
|
|
80
130
|
cleanup() {}
|
|
81
131
|
};
|
|
82
132
|
}
|
|
83
133
|
|
|
84
134
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${config.project}-sandbox-`));
|
|
85
135
|
const tempPath = path.join(tempDir, 'Dockerfile');
|
|
86
|
-
fs.writeFileSync(tempPath, dockerfileContent(config), 'utf8');
|
|
136
|
+
fs.writeFileSync(tempPath, dockerfileContent(config, image), 'utf8');
|
|
87
137
|
|
|
88
138
|
return {
|
|
89
139
|
path: tempPath,
|
|
90
|
-
signature: dockerfileSignature(config),
|
|
140
|
+
signature: dockerfileSignature(config, image),
|
|
91
141
|
cleanup() {
|
|
92
142
|
removeDirRecursive(tempDir);
|
|
93
143
|
}
|
|
94
144
|
};
|
|
95
145
|
}
|
|
96
146
|
|
|
97
|
-
export function composeDockerfile(
|
|
98
|
-
|
|
147
|
+
export function composeDockerfile(
|
|
148
|
+
config: DockerfileConfig,
|
|
149
|
+
image: DockerfileImageContribution = EMPTY_IMAGE_CONTRIBUTION
|
|
150
|
+
): string {
|
|
151
|
+
const content = dockerfileContent(config, image);
|
|
99
152
|
|
|
100
153
|
const tempPath = path.join(os.tmpdir(), `${config.project}-sandbox.Dockerfile`);
|
|
101
154
|
fs.writeFileSync(tempPath, content, 'utf8');
|
package/lib/sandbox/mounts.ts
CHANGED
|
@@ -19,9 +19,16 @@ export function sandboxCoreBindMounts(
|
|
|
19
19
|
'repoRoot' | 'worktreeBase' | 'shareBase' | 'shellConfigBase'
|
|
20
20
|
>,
|
|
21
21
|
branch: string,
|
|
22
|
-
overrides: {
|
|
22
|
+
overrides: {
|
|
23
|
+
worktree?: string;
|
|
24
|
+
shellConfigHostDir?: string;
|
|
25
|
+
workspaceViewRoot: string;
|
|
26
|
+
controlDir: string;
|
|
27
|
+
taskSource?: string;
|
|
28
|
+
taskId?: string;
|
|
29
|
+
}
|
|
23
30
|
): SandboxBindMountDeclaration[] {
|
|
24
|
-
|
|
31
|
+
const mounts: SandboxBindMountDeclaration[] = [
|
|
25
32
|
{
|
|
26
33
|
hostPaths: overrides.worktree
|
|
27
34
|
? [overrides.worktree]
|
|
@@ -30,9 +37,9 @@ export function sandboxCoreBindMounts(
|
|
|
30
37
|
readOnly: false
|
|
31
38
|
},
|
|
32
39
|
{
|
|
33
|
-
hostPaths: [
|
|
40
|
+
hostPaths: [overrides.workspaceViewRoot],
|
|
34
41
|
containerPath: '/workspace/.agents/workspace',
|
|
35
|
-
readOnly:
|
|
42
|
+
readOnly: true
|
|
36
43
|
},
|
|
37
44
|
{
|
|
38
45
|
hostPaths: [shareCommonDir(config)],
|
|
@@ -52,4 +59,17 @@ export function sandboxCoreBindMounts(
|
|
|
52
59
|
readOnly: true
|
|
53
60
|
}
|
|
54
61
|
];
|
|
62
|
+
if (overrides.taskSource && overrides.taskId) {
|
|
63
|
+
mounts.splice(2, 0, {
|
|
64
|
+
hostPaths: [overrides.taskSource],
|
|
65
|
+
containerPath: path.posix.join('/workspace/.agents/workspace/active', overrides.taskId),
|
|
66
|
+
readOnly: false
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
mounts.push({
|
|
70
|
+
hostPaths: [overrides.controlDir],
|
|
71
|
+
containerPath: '/run/agent-infra/control',
|
|
72
|
+
readOnly: false
|
|
73
|
+
});
|
|
74
|
+
return mounts;
|
|
55
75
|
}
|
package/lib/sandbox/recovery.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
3
5
|
import {
|
|
4
6
|
containerNameCandidates,
|
|
5
7
|
sandboxBranchLabel,
|
|
6
8
|
sandboxLabel,
|
|
7
|
-
sandboxRuntimeCapabilityLabel
|
|
9
|
+
sandboxRuntimeCapabilityLabel,
|
|
10
|
+
sandboxTaskIdLabel,
|
|
11
|
+
sandboxWorkspaceModeLabel
|
|
8
12
|
} from './constants.ts';
|
|
9
13
|
import { isAgentClientId } from '../agent-clients/types.ts';
|
|
10
14
|
import {
|
|
@@ -17,6 +21,16 @@ import type {
|
|
|
17
21
|
import type { SandboxConfig } from './config.ts';
|
|
18
22
|
import { toEnginePath } from './engines/wsl2-paths.ts';
|
|
19
23
|
import { sandboxCoreBindMounts } from './mounts.ts';
|
|
24
|
+
import {
|
|
25
|
+
assertSandboxTaskSource,
|
|
26
|
+
sandboxControlPaths,
|
|
27
|
+
sandboxWorkspaceViewPaths
|
|
28
|
+
} from './workspace-view.ts';
|
|
29
|
+
import {
|
|
30
|
+
parseSandboxWorkspaceIdentity,
|
|
31
|
+
sameSandboxWorkspaceIdentity,
|
|
32
|
+
type SandboxWorkspaceIdentity
|
|
33
|
+
} from './workspace-identity.ts';
|
|
20
34
|
import { runEngine, runOkEngine, runVerboseEngine } from './shell.ts';
|
|
21
35
|
import {
|
|
22
36
|
declaredTmpfsSeedEntries,
|
|
@@ -31,6 +45,7 @@ import {
|
|
|
31
45
|
startSandboxContainer,
|
|
32
46
|
type SandboxRow
|
|
33
47
|
} from './commands/list-running.ts';
|
|
48
|
+
import { processIdentityMatches } from '../server/process-state.ts';
|
|
34
49
|
|
|
35
50
|
export type SandboxRecoveryFinding = {
|
|
36
51
|
repairKind: 'permissions' | 'missing-seed' | 'builtin-link' | 'hard-failure';
|
|
@@ -109,6 +124,7 @@ type EnsureSandboxReadyParams = {
|
|
|
109
124
|
config: SandboxConfig;
|
|
110
125
|
engine: string;
|
|
111
126
|
branch: string;
|
|
127
|
+
workspace?: SandboxWorkspaceIdentity;
|
|
112
128
|
row: SandboxRow;
|
|
113
129
|
allowRecreate?: boolean;
|
|
114
130
|
recreate?: (branch: string) => Promise<void>;
|
|
@@ -123,6 +139,54 @@ type ExpectedMount = {
|
|
|
123
139
|
expectedRW: boolean;
|
|
124
140
|
};
|
|
125
141
|
|
|
142
|
+
function ensureSandboxControlBroker(params: {
|
|
143
|
+
config: SandboxConfig;
|
|
144
|
+
container: string;
|
|
145
|
+
workspace: SandboxWorkspaceIdentity;
|
|
146
|
+
}): void {
|
|
147
|
+
const control = sandboxControlPaths({
|
|
148
|
+
base: params.config.controlBase ?? path.join(params.config.home, '.agent-infra', 'sandbox-control'),
|
|
149
|
+
project: params.config.project,
|
|
150
|
+
container: params.container,
|
|
151
|
+
identity: params.workspace
|
|
152
|
+
});
|
|
153
|
+
if (!fs.existsSync(control.manifestPath)) return;
|
|
154
|
+
const manifest = JSON.parse(fs.readFileSync(control.manifestPath, 'utf8')) as { token?: unknown };
|
|
155
|
+
const brokerPath = path.join(control.root, 'broker.json');
|
|
156
|
+
try {
|
|
157
|
+
const broker = JSON.parse(fs.readFileSync(brokerPath, 'utf8')) as {
|
|
158
|
+
version?: unknown;
|
|
159
|
+
pid?: unknown;
|
|
160
|
+
startTime?: unknown;
|
|
161
|
+
token?: unknown;
|
|
162
|
+
};
|
|
163
|
+
if (
|
|
164
|
+
broker.version === 1
|
|
165
|
+
&& typeof broker.pid === 'number'
|
|
166
|
+
&& typeof broker.startTime === 'string'
|
|
167
|
+
&& broker.token === manifest.token
|
|
168
|
+
&& processIdentityMatches({ version: 1, pid: broker.pid, startTime: broker.startTime })
|
|
169
|
+
) return;
|
|
170
|
+
} catch {
|
|
171
|
+
// A missing, stale, or malformed broker record is replaced below.
|
|
172
|
+
}
|
|
173
|
+
fs.rmSync(brokerPath, { force: true });
|
|
174
|
+
const directory = path.dirname(fileURLToPath(import.meta.url));
|
|
175
|
+
const extension = path.extname(fileURLToPath(import.meta.url));
|
|
176
|
+
const internalCli = path.resolve(directory, '..', '..', 'bin', `internal-cli${extension}`);
|
|
177
|
+
const child = spawn(
|
|
178
|
+
process.execPath,
|
|
179
|
+
[internalCli, 'sandbox-control', 'serve', '--manifest', control.manifestPath],
|
|
180
|
+
{ cwd: params.config.repoRoot, detached: true, stdio: 'ignore' }
|
|
181
|
+
);
|
|
182
|
+
child.once('error', () => {
|
|
183
|
+
// Readiness remains fail-closed; a later control request reports the
|
|
184
|
+
// unavailable broker without turning this detached spawn into an
|
|
185
|
+
// unhandled process error.
|
|
186
|
+
});
|
|
187
|
+
child.unref();
|
|
188
|
+
}
|
|
189
|
+
|
|
126
190
|
function findingKey(finding: SandboxRecoveryFinding): string {
|
|
127
191
|
return `${finding.repairKind}:${finding.path ?? finding.seed?.targetPath ?? finding.message}`;
|
|
128
192
|
}
|
|
@@ -297,11 +361,34 @@ function hostSourceAccessible(hostPath: string, writable: boolean): boolean {
|
|
|
297
361
|
function expectedMounts(params: {
|
|
298
362
|
config: SandboxConfig;
|
|
299
363
|
branch: string;
|
|
364
|
+
container: string;
|
|
365
|
+
workspace: SandboxWorkspaceIdentity;
|
|
300
366
|
tools: readonly SandboxTool[];
|
|
301
367
|
actualMounts: Map<string, DockerMount>;
|
|
302
368
|
}): ExpectedMount[] {
|
|
303
369
|
const { config, branch, tools, actualMounts } = params;
|
|
304
|
-
const
|
|
370
|
+
const view = sandboxWorkspaceViewPaths({
|
|
371
|
+
base: config.workspaceViewBase ?? path.join(config.home, '.agent-infra', 'workspace-views'),
|
|
372
|
+
project: config.project,
|
|
373
|
+
container: params.container,
|
|
374
|
+
identity: params.workspace
|
|
375
|
+
});
|
|
376
|
+
const control = sandboxControlPaths({
|
|
377
|
+
base: config.controlBase ?? path.join(config.home, '.agent-infra', 'sandbox-control'),
|
|
378
|
+
project: config.project,
|
|
379
|
+
container: params.container,
|
|
380
|
+
identity: params.workspace
|
|
381
|
+
});
|
|
382
|
+
const core = sandboxCoreBindMounts(config, branch, {
|
|
383
|
+
workspaceViewRoot: view.root,
|
|
384
|
+
controlDir: control.channelDir,
|
|
385
|
+
...(params.workspace.mode === 'task-bound'
|
|
386
|
+
? {
|
|
387
|
+
taskSource: assertSandboxTaskSource(config.repoRoot, params.workspace.taskId),
|
|
388
|
+
taskId: params.workspace.taskId
|
|
389
|
+
}
|
|
390
|
+
: {})
|
|
391
|
+
}).map((mount) => ({
|
|
305
392
|
path: mount.containerPath,
|
|
306
393
|
expectedType: 'bind' as const,
|
|
307
394
|
hostPaths: mount.hostPaths,
|
|
@@ -399,6 +486,7 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
399
486
|
config: SandboxConfig;
|
|
400
487
|
engine: string;
|
|
401
488
|
branch: string;
|
|
489
|
+
workspace?: SandboxWorkspaceIdentity;
|
|
402
490
|
container: string;
|
|
403
491
|
deps?: RecoveryCommandDeps;
|
|
404
492
|
}): SandboxRecoverySnapshot {
|
|
@@ -427,6 +515,7 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
427
515
|
}
|
|
428
516
|
}
|
|
429
517
|
const capabilityPlan = createSandboxCapabilityPlan(params.config);
|
|
518
|
+
const workspace = params.workspace ?? { mode: 'branch-only' as const };
|
|
430
519
|
const tools = [...capabilityPlan.tools];
|
|
431
520
|
const seeds = declaredTmpfsSeedEntries(tools).map((seed) => {
|
|
432
521
|
const mounted = mountsByDestination.get(seed.stagingPath)?.Type === 'bind';
|
|
@@ -460,6 +549,10 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
460
549
|
}));
|
|
461
550
|
const labels = inspection.Config?.Labels ?? {};
|
|
462
551
|
const branchLabel = labels[sandboxBranchLabel(params.config)];
|
|
552
|
+
const containerWorkspace = parseSandboxWorkspaceIdentity(labels, {
|
|
553
|
+
mode: sandboxWorkspaceModeLabel(params.config),
|
|
554
|
+
taskId: sandboxTaskIdLabel(params.config)
|
|
555
|
+
});
|
|
463
556
|
const selectedToolIds = new Set(tools.map((tool) => tool.id));
|
|
464
557
|
const enforceCanonicalPlan = params.config.agentClientSource === 'canonical';
|
|
465
558
|
const unexpectedCapabilityMounts = enforceCanonicalPlan
|
|
@@ -471,6 +564,8 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
471
564
|
const mountSnapshots = expectedMounts({
|
|
472
565
|
config: params.config,
|
|
473
566
|
branch: params.branch,
|
|
567
|
+
container: params.container,
|
|
568
|
+
workspace,
|
|
474
569
|
tools,
|
|
475
570
|
actualMounts: mountsByDestination
|
|
476
571
|
}).map((expected) => {
|
|
@@ -510,7 +605,8 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
510
605
|
return {
|
|
511
606
|
identityOk: typeof inspection.Id === 'string'
|
|
512
607
|
&& inspection.Id.length > 0
|
|
513
|
-
&& branchLabel === params.branch
|
|
608
|
+
&& branchLabel === params.branch
|
|
609
|
+
&& sameSandboxWorkspaceIdentity(containerWorkspace, workspace),
|
|
514
610
|
runtimeCapabilityOk: enforceCanonicalPlan
|
|
515
611
|
? labels[sandboxRuntimeCapabilityLabel(params.config)] === capabilityPlan.runtimeSignature
|
|
516
612
|
: undefined,
|
|
@@ -673,6 +769,7 @@ function assess(params: {
|
|
|
673
769
|
config: SandboxConfig;
|
|
674
770
|
engine: string;
|
|
675
771
|
branch: string;
|
|
772
|
+
workspace?: SandboxWorkspaceIdentity;
|
|
676
773
|
container: string;
|
|
677
774
|
deps?: RecoveryCommandDeps;
|
|
678
775
|
}): { snapshot: SandboxRecoverySnapshot; findings: SandboxRecoveryFinding[] } {
|
|
@@ -680,21 +777,64 @@ function assess(params: {
|
|
|
680
777
|
return { snapshot, findings: classifySandboxRecovery(snapshot) };
|
|
681
778
|
}
|
|
682
779
|
|
|
683
|
-
|
|
780
|
+
function canRetryFreshReadiness(findings: SandboxRecoveryFinding[]): boolean {
|
|
781
|
+
return findings.length > 0 && findings.every((finding) =>
|
|
782
|
+
finding.repairKind === 'hard-failure'
|
|
783
|
+
&& finding.path !== undefined
|
|
784
|
+
&& (
|
|
785
|
+
finding.path.startsWith('/workspace/.agents/workspace')
|
|
786
|
+
|| finding.path === '/home/devuser/.host-shell-config'
|
|
787
|
+
|| finding.path === '/home/devuser/.bash_aliases'
|
|
788
|
+
)
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
export async function assertFreshSandboxReady(params: {
|
|
684
793
|
config: SandboxConfig;
|
|
685
794
|
engine: string;
|
|
686
795
|
branch: string;
|
|
796
|
+
workspace?: SandboxWorkspaceIdentity;
|
|
687
797
|
container: string;
|
|
688
798
|
copiedEntries: TmpfsSeedEntry[];
|
|
689
799
|
deps?: RecoveryCommandDeps;
|
|
690
|
-
}): void {
|
|
800
|
+
}): Promise<void> {
|
|
691
801
|
validateTmpfsSeedEntries({
|
|
692
802
|
engine: params.engine,
|
|
693
803
|
container: params.container,
|
|
694
804
|
entries: params.copiedEntries,
|
|
695
805
|
deps: params.deps
|
|
696
806
|
});
|
|
697
|
-
|
|
807
|
+
let { findings } = assess(params);
|
|
808
|
+
if (canRetryFreshReadiness(findings)) {
|
|
809
|
+
const runVerboseFn = params.deps?.runVerbose ?? runVerboseEngine;
|
|
810
|
+
runVerboseFn(params.engine, 'docker', ['restart', params.container]);
|
|
811
|
+
prepareTmpfsMounts({
|
|
812
|
+
engine: params.engine,
|
|
813
|
+
container: params.container,
|
|
814
|
+
mountPaths: resolveTools(params.config)
|
|
815
|
+
.filter((tool) => tool.tmpfs)
|
|
816
|
+
.map((tool) => tool.containerMount),
|
|
817
|
+
deps: params.deps
|
|
818
|
+
});
|
|
819
|
+
hydrateTmpfsSeedEntries({
|
|
820
|
+
engine: params.engine,
|
|
821
|
+
container: params.container,
|
|
822
|
+
entries: params.copiedEntries,
|
|
823
|
+
replace: true,
|
|
824
|
+
deps: params.deps
|
|
825
|
+
});
|
|
826
|
+
let retried = assess(params);
|
|
827
|
+
if (!retried.findings.some((finding) => finding.repairKind === 'hard-failure')) {
|
|
828
|
+
repairFindings({
|
|
829
|
+
engine: params.engine,
|
|
830
|
+
container: params.container,
|
|
831
|
+
findings: retried.findings,
|
|
832
|
+
deps: params.deps
|
|
833
|
+
});
|
|
834
|
+
retried = assess(params);
|
|
835
|
+
}
|
|
836
|
+
findings = retried.findings;
|
|
837
|
+
}
|
|
698
838
|
if (findings.length > 0) {
|
|
699
839
|
throw new Error(`Fresh sandbox readiness check failed: ${describeFindings(findings)}`);
|
|
700
840
|
}
|
|
@@ -705,6 +845,11 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
705
845
|
const startFn = deps?.start ?? startSandboxContainer;
|
|
706
846
|
const warnings: string[] = [];
|
|
707
847
|
let failure: Error | null = null;
|
|
848
|
+
ensureSandboxControlBroker({
|
|
849
|
+
config: params.config,
|
|
850
|
+
container: params.row.name,
|
|
851
|
+
workspace: params.workspace ?? { mode: 'branch-only' }
|
|
852
|
+
});
|
|
708
853
|
|
|
709
854
|
try {
|
|
710
855
|
if (!params.row.running) {
|
|
@@ -713,6 +858,7 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
713
858
|
config: params.config,
|
|
714
859
|
engine: params.engine,
|
|
715
860
|
branch: params.branch,
|
|
861
|
+
workspace: params.workspace,
|
|
716
862
|
container: params.row.name,
|
|
717
863
|
deps
|
|
718
864
|
});
|
|
@@ -739,6 +885,7 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
739
885
|
config: params.config,
|
|
740
886
|
engine: params.engine,
|
|
741
887
|
branch: params.branch,
|
|
888
|
+
workspace: params.workspace,
|
|
742
889
|
container: params.row.name,
|
|
743
890
|
deps
|
|
744
891
|
});
|
|
@@ -753,6 +900,7 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
753
900
|
config: params.config,
|
|
754
901
|
engine: params.engine,
|
|
755
902
|
branch: params.branch,
|
|
903
|
+
workspace: params.workspace,
|
|
756
904
|
container: params.row.name,
|
|
757
905
|
deps
|
|
758
906
|
});
|
|
@@ -767,6 +915,7 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
767
915
|
config: params.config,
|
|
768
916
|
engine: params.engine,
|
|
769
917
|
branch: params.branch,
|
|
918
|
+
workspace: params.workspace,
|
|
770
919
|
container: params.row.name,
|
|
771
920
|
deps
|
|
772
921
|
});
|
|
@@ -791,6 +940,7 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
791
940
|
config: params.config,
|
|
792
941
|
engine: params.engine,
|
|
793
942
|
branch: params.branch,
|
|
943
|
+
workspace: params.workspace,
|
|
794
944
|
container: params.row.name,
|
|
795
945
|
deps
|
|
796
946
|
});
|
|
@@ -811,6 +961,7 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
811
961
|
config: params.config,
|
|
812
962
|
engine: params.engine,
|
|
813
963
|
branch: params.branch,
|
|
964
|
+
workspace: params.workspace,
|
|
814
965
|
container: params.row.name,
|
|
815
966
|
deps
|
|
816
967
|
});
|
|
@@ -831,6 +982,11 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
831
982
|
const warning = `Sandbox recovery failed in place. Replacing only the container. ${dataBoundary}`;
|
|
832
983
|
warnings.push(warning);
|
|
833
984
|
(params.writeWarning ?? ((message) => process.stderr.write(`${message}\n`)))(warning);
|
|
985
|
+
const runVerboseFn = deps?.runVerbose ?? runVerboseEngine;
|
|
986
|
+
if (params.row.running) {
|
|
987
|
+
runVerboseFn(params.engine, 'docker', ['stop', params.row.name]);
|
|
988
|
+
}
|
|
989
|
+
runVerboseFn(params.engine, 'docker', ['rm', params.row.name]);
|
|
834
990
|
await params.recreate(params.branch);
|
|
835
991
|
|
|
836
992
|
const rows = (deps?.fetchRows ?? fetchSandboxRows)(
|
|
@@ -849,6 +1005,7 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
849
1005
|
config: params.config,
|
|
850
1006
|
engine: params.engine,
|
|
851
1007
|
branch: params.branch,
|
|
1008
|
+
workspace: params.workspace,
|
|
852
1009
|
container: replacement.name,
|
|
853
1010
|
deps
|
|
854
1011
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const SENSITIVE_OUTPUT_PATTERNS = [
|
|
2
|
+
{ pattern: /\{[^{}]*"claudeAiOauth"[\s\S]*?\}\s*\}/g, replacement: '[REDACTED credentials blob]' },
|
|
3
|
+
{ pattern: /sk-ant-[A-Za-z0-9_-]{20,}/g, replacement: '[REDACTED claude token]' },
|
|
4
|
+
{ pattern: /gh[psoru]_[A-Za-z0-9]{30,}/g, replacement: '[REDACTED github token]' },
|
|
5
|
+
{ pattern: /Bearer\s+[A-Za-z0-9._~+/=-]{20,}/gi, replacement: 'Bearer [REDACTED]' }
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export function redactCommandError(text: unknown): string {
|
|
9
|
+
if (!text || typeof text !== 'string') return '';
|
|
10
|
+
return SENSITIVE_OUTPUT_PATTERNS.reduce(
|
|
11
|
+
(result, { pattern, replacement }) => result.replace(pattern, replacement),
|
|
12
|
+
text
|
|
13
|
+
);
|
|
14
|
+
}
|