@fitlab-ai/agent-infra 0.9.10 → 0.9.12
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 +19 -9
- package/README.zh-CN.md +19 -9
- 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 +71 -10
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +7 -8
- 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 +5 -15
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +8 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +3 -2
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-pr.js +8 -2
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/task-delivery.js +59 -0
- 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/internal/task-validate.js +2 -1
- package/dist/lib/merge.js +205 -103
- package/dist/lib/platform/github-client.js +7 -1
- package/dist/lib/platform/pr-checks.js +8 -5
- package/dist/lib/platform/pr-summary.js +12 -8
- package/dist/lib/platform/pull-requests.js +393 -91
- package/dist/lib/platform/verification-required.js +13 -2
- package/dist/lib/platform/verification-sync.js +9 -4
- package/dist/lib/pr-review/evidence-grading.js +12 -4
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- 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 +8 -12
- package/dist/lib/sandbox/control/executor.js +131 -39
- package/dist/lib/sandbox/control/lifecycle.js +55 -24
- 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/process-state.js +2 -2
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +6 -4
- package/dist/lib/task/codex-orchestration.js +1 -1
- 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-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +181 -91
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +39 -8
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +218 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/human-override.js +5 -2
- package/dist/lib/task/index.js +19 -20
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +122 -9
- package/dist/lib/task/orchestration.js +105 -75
- package/dist/lib/task/pr-delivery-fact.js +182 -0
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +13 -4
- package/dist/lib/task/review-fingerprint.js +8 -0
- package/dist/lib/task/verification-engine.js +86 -39
- package/dist/lib/task/verification-review.js +2 -2
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +13 -35
- 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 +105 -10
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +7 -8
- 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 +5 -18
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +8 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +5 -2
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-pr.ts +8 -1
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/task-delivery.ts +53 -0
- 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/internal/task-validate.ts +3 -2
- package/lib/merge.ts +236 -120
- package/lib/platform/github-client.ts +8 -1
- package/lib/platform/pr-checks.ts +7 -5
- package/lib/platform/pr-summary.ts +10 -7
- package/lib/platform/pull-requests.ts +425 -87
- package/lib/platform/verification-required.ts +12 -2
- package/lib/platform/verification-sync.ts +9 -4
- package/lib/pr-review/evidence-grading.ts +7 -4
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- 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 +9 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/executor.ts +148 -51
- package/lib/sandbox/control/lifecycle.ts +59 -31
- 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/process-state.ts +2 -2
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +7 -5
- package/lib/task/codex-orchestration.ts +2 -1
- 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-intent.ts +137 -0
- package/lib/task/commit-operation.ts +161 -79
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +40 -10
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +280 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +10 -5
- package/lib/task/human-override.ts +5 -2
- package/lib/task/index.ts +19 -20
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +113 -4
- package/lib/task/orchestration.ts +102 -80
- package/lib/task/pr-delivery-fact.ts +254 -0
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +21 -5
- package/lib/task/review-fingerprint.ts +10 -0
- package/lib/task/verification-engine.ts +78 -46
- package/lib/task/verification-review.ts +3 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +14 -38
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- package/templates/.agents/rules/compatibility-policy.en.md +5 -4
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
- package/templates/.agents/rules/create-issue.en.md +11 -2
- package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
- 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 → issue-fields.en.md} +1 -1
- package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +26 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
- package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
- package/templates/.agents/rules/milestone-inference.en.md +12 -2
- package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
- 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 +23 -14
- package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
- package/templates/.agents/rules/release-commands.en.md +42 -3
- package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
- 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/security-alerts.en.md +17 -2
- package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
- package/templates/.agents/rules/sync-content-generation.en.md +8 -0
- package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
- package/templates/.agents/rules/task-management.en.md +2 -3
- package/templates/.agents/rules/task-management.zh-CN.md +2 -3
- package/templates/.agents/scripts/security-alerts.github.sh +151 -0
- package/templates/.agents/scripts/security-alerts.sh +31 -0
- 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 +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/commit/SKILL.en.md +30 -10
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
- package/templates/.agents/skills/commit/config/verify.json +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
- 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 +2 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- 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 +13 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
- 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 +2 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/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/init-labels/SKILL.en.md +9 -10
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
- 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 +8 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
- 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-analysis/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
- 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/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 +2 -1
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
- 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/review-plan/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
- 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 +144 -109
- package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
- package/templates/.agents/templates/task.en.md +6 -2
- package/templates/.agents/templates/task.zh-CN.md +6 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/feature-development.en.yaml +9 -8
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/refactoring.en.yaml +9 -8
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
- 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/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
- package/templates/.agents/rules/create-issue.github.en.md +0 -14
- package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
- package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
- package/templates/.agents/rules/issue-sync.github.en.md +0 -40
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
- package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
- package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
- package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
- package/templates/.agents/rules/pr-sync.github.en.md +0 -43
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
- package/templates/.agents/rules/release-commands.github.en.md +0 -38
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import { spawn,
|
|
1
|
+
import { spawn, type ChildProcess } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
5
|
import { getProcessStartTime } from '../../server/process-state.ts';
|
|
6
6
|
import { createTask } from '../../task/create-service.ts';
|
|
7
7
|
import { applyTaskFinalization } from '../../task/finalization.ts';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { bindSandboxControlTask, validateSandboxControlRequest, type SandboxControlExecution, type SandboxControlManifest, type SandboxControlRequest } from './protocol.ts';
|
|
9
|
+
import { atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.ts';
|
|
10
|
+
import {
|
|
11
|
+
createSandboxExecutorExecutionContext,
|
|
12
|
+
dispatchTaskControlOperation,
|
|
13
|
+
parseTaskControlOperation,
|
|
14
|
+
type TaskControlOperation
|
|
15
|
+
} from '../../task/control-authority.ts';
|
|
16
|
+
import { assertSandboxControlBrokerOwner, readSandboxControlManifest, type BrokerOwner } from './lifecycle.ts';
|
|
11
17
|
import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.ts';
|
|
12
18
|
import {
|
|
13
19
|
closeCodexControllerRegistration,
|
|
@@ -29,6 +35,26 @@ export type PreparedSandboxControlExecution = {
|
|
|
29
35
|
terminate(updateState?: boolean): boolean;
|
|
30
36
|
};
|
|
31
37
|
|
|
38
|
+
function sameBrokerOwner(left: BrokerOwner, right: BrokerOwner): boolean {
|
|
39
|
+
return left.version === right.version
|
|
40
|
+
&& left.pid === right.pid
|
|
41
|
+
&& left.startTime === right.startTime
|
|
42
|
+
&& left.brokerId === right.brokerId
|
|
43
|
+
&& left.token === right.token
|
|
44
|
+
&& left.generation === right.generation;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function assertSandboxControlExecutorAuthority(
|
|
48
|
+
manifest: SandboxControlManifest,
|
|
49
|
+
gateOwner?: BrokerOwner
|
|
50
|
+
): void {
|
|
51
|
+
const owner = assertSandboxControlBrokerOwner(manifest);
|
|
52
|
+
if (gateOwner && !sameBrokerOwner(owner, gateOwner)) {
|
|
53
|
+
throw new Error('SANDBOX_CONTROL_GATE_OWNER_MISMATCH');
|
|
54
|
+
}
|
|
55
|
+
if (readActiveLease(manifest)) throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
function safeEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
33
59
|
return Object.fromEntries(Object.entries(env).filter(([key]) => !key.toUpperCase().startsWith('AGENT_INFRA_CONTROL_')));
|
|
34
60
|
}
|
|
@@ -104,9 +130,11 @@ export async function prepareSandboxControlExecution(params: {
|
|
|
104
130
|
start(canWrite = () => true) {
|
|
105
131
|
if (!child.connected) throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_CLOSED');
|
|
106
132
|
if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
|
|
133
|
+
const gateOwner = assertSandboxControlBrokerOwner(params.manifest);
|
|
134
|
+
if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
|
|
107
135
|
atomicWriteJson(executionPath(params.manifest, params.request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
|
|
108
136
|
if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
|
|
109
|
-
child.send({ version: 1, nonce });
|
|
137
|
+
child.send({ version: 1, nonce, owner: gateOwner });
|
|
110
138
|
},
|
|
111
139
|
completion,
|
|
112
140
|
terminate(updateState = true) {
|
|
@@ -120,7 +148,7 @@ export async function prepareSandboxControlExecution(params: {
|
|
|
120
148
|
};
|
|
121
149
|
}
|
|
122
150
|
|
|
123
|
-
function waitForGate(nonce: string, timeoutMs = 2_000): Promise<
|
|
151
|
+
function waitForGate(nonce: string, timeoutMs = 2_000): Promise<BrokerOwner> {
|
|
124
152
|
return new Promise((resolve, reject) => {
|
|
125
153
|
const timer = setTimeout(() => reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_TIMEOUT')), timeoutMs);
|
|
126
154
|
const onDisconnect = () => {
|
|
@@ -131,12 +159,21 @@ function waitForGate(nonce: string, timeoutMs = 2_000): Promise<void> {
|
|
|
131
159
|
process.once('message', (message: unknown) => {
|
|
132
160
|
clearTimeout(timer);
|
|
133
161
|
process.off('disconnect', onDisconnect);
|
|
134
|
-
const value = message as {
|
|
135
|
-
|
|
162
|
+
const value = message as {
|
|
163
|
+
version?: unknown;
|
|
164
|
+
nonce?: unknown;
|
|
165
|
+
owner?: Partial<BrokerOwner> | null;
|
|
166
|
+
} | null;
|
|
167
|
+
const owner = value?.owner;
|
|
168
|
+
if (!value || value.version !== 1 || value.nonce !== nonce
|
|
169
|
+
|| !owner || owner.version !== 3 || !Number.isSafeInteger(owner.pid) || (owner.pid ?? 0) <= 0
|
|
170
|
+
|| typeof owner.startTime !== 'number' || !Number.isSafeInteger(owner.startTime)
|
|
171
|
+
|| typeof owner.brokerId !== 'string' || owner.brokerId.length === 0
|
|
172
|
+
|| typeof owner.token !== 'string' || typeof owner.generation !== 'string') {
|
|
136
173
|
reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_INVALID'));
|
|
137
174
|
return;
|
|
138
175
|
}
|
|
139
|
-
resolve();
|
|
176
|
+
resolve(owner as BrokerOwner);
|
|
140
177
|
});
|
|
141
178
|
});
|
|
142
179
|
}
|
|
@@ -170,6 +207,17 @@ function orchestrationFailure(code: string, message: string): SandboxControlExec
|
|
|
170
207
|
};
|
|
171
208
|
}
|
|
172
209
|
|
|
210
|
+
function lifecycleFailure(code: string, message: string): SandboxControlExecutionResult {
|
|
211
|
+
return {
|
|
212
|
+
exitCode: 1,
|
|
213
|
+
stdout: `${JSON.stringify({
|
|
214
|
+
status: 'failed', changed: false,
|
|
215
|
+
error: { code, message }
|
|
216
|
+
})}\n`,
|
|
217
|
+
stderr: ''
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
173
221
|
function finalizationResult(result: ReturnType<typeof applyTaskFinalization>): SandboxControlExecutionResult {
|
|
174
222
|
const payload = {
|
|
175
223
|
version: 1,
|
|
@@ -186,28 +234,17 @@ function finalizationResult(result: ReturnType<typeof applyTaskFinalization>): S
|
|
|
186
234
|
};
|
|
187
235
|
}
|
|
188
236
|
|
|
189
|
-
function isCodexPrepare(args: readonly string[]): boolean {
|
|
190
|
-
if (args[1] !== 'prepare') return false;
|
|
191
|
-
const values: string[] = [];
|
|
192
|
-
for (let index = 2; index < args.length; index += 1) {
|
|
193
|
-
if (args[index] === '--client') values.push(args[index + 1] ?? '');
|
|
194
|
-
else if (args[index]?.startsWith('--client=')) values.push(args[index]!.slice('--client='.length));
|
|
195
|
-
}
|
|
196
|
-
return values.length === 1 && values[0] === 'codex';
|
|
197
|
-
}
|
|
198
|
-
|
|
199
237
|
type ExecuteRequestOptions = Readonly<{
|
|
200
238
|
buildIdentity?: typeof computeLifecycleBuildIdentity;
|
|
201
239
|
resolveControllerBinding?: typeof resolveCodexControllerBinding;
|
|
202
|
-
spawnDomain?: typeof spawnSync;
|
|
203
240
|
}>;
|
|
204
241
|
|
|
205
|
-
export function executeRequest(
|
|
242
|
+
export async function executeRequest(
|
|
206
243
|
manifest: SandboxControlManifest,
|
|
207
244
|
manifestPath: string,
|
|
208
245
|
request: SandboxControlRequest,
|
|
209
246
|
options: ExecuteRequestOptions = {}
|
|
210
|
-
): SandboxControlExecutionResult {
|
|
247
|
+
): Promise<SandboxControlExecutionResult> {
|
|
211
248
|
if (request.family === 'task-create') {
|
|
212
249
|
const result = createTask(request.candidate, { repoRoot: manifest.repoRoot });
|
|
213
250
|
return {
|
|
@@ -260,20 +297,47 @@ export function executeRequest(
|
|
|
260
297
|
}
|
|
261
298
|
}
|
|
262
299
|
if (request.family === 'task-finalization') {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
300
|
+
const operation = parseTaskControlOperation(
|
|
301
|
+
'task-finalization', [manifest.taskId!, 'complete', '--agent', request.agent]
|
|
302
|
+
);
|
|
303
|
+
if (operation.family !== 'task-finalization') throw new Error('SANDBOX_CONTROL_FINALIZATION_OPERATION_INVALID');
|
|
304
|
+
const result = dispatchTaskControlOperation(
|
|
305
|
+
createSandboxExecutorExecutionContext({
|
|
306
|
+
repoRoot: manifest.repoRoot,
|
|
307
|
+
worktreeRoot: manifest.worktreeRoot,
|
|
308
|
+
runtimeDir: manifest.runtimeDir,
|
|
309
|
+
taskId: manifest.taskId!,
|
|
310
|
+
generation: manifest.generation,
|
|
311
|
+
manifestPath,
|
|
312
|
+
requestId: request.id
|
|
313
|
+
}),
|
|
314
|
+
operation
|
|
315
|
+
);
|
|
316
|
+
return finalizationResult(result);
|
|
269
317
|
}
|
|
270
318
|
const boundArgs = bindSandboxControlTask(request, manifest.taskId!);
|
|
319
|
+
if (request.family === 'task-orchestration') {
|
|
320
|
+
boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
|
|
321
|
+
}
|
|
322
|
+
let operation: TaskControlOperation;
|
|
323
|
+
try {
|
|
324
|
+
operation = parseTaskControlOperation(request.family, boundArgs);
|
|
325
|
+
} catch (error) {
|
|
326
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
327
|
+
const code = /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_CONTROL_OPERATION_INVALID';
|
|
328
|
+
return request.family === 'task-lifecycle'
|
|
329
|
+
? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
|
|
330
|
+
: orchestrationFailure(code, message);
|
|
331
|
+
}
|
|
271
332
|
let controllerBinding: Readonly<{
|
|
272
333
|
instanceDigest: string;
|
|
273
334
|
controlGeneration: string;
|
|
274
335
|
}> | null = null;
|
|
275
336
|
if (request.family === 'task-orchestration') {
|
|
276
|
-
|
|
337
|
+
const codexPrepare = operation.family === 'task-orchestration'
|
|
338
|
+
&& operation.intent === 'prepare'
|
|
339
|
+
&& operation.input.client === 'codex';
|
|
340
|
+
if (codexPrepare) {
|
|
277
341
|
if (!request.controllerProof) {
|
|
278
342
|
return orchestrationFailure(
|
|
279
343
|
'CODEX_SANDBOX_CONTROLLER_PROOF_REQUIRED',
|
|
@@ -299,36 +363,69 @@ export function executeRequest(
|
|
|
299
363
|
'Controller proof is only accepted for canonical Codex prepare'
|
|
300
364
|
);
|
|
301
365
|
}
|
|
302
|
-
boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
|
|
303
366
|
}
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
367
|
+
const context = createSandboxExecutorExecutionContext({
|
|
368
|
+
repoRoot: manifest.repoRoot,
|
|
369
|
+
worktreeRoot: manifest.worktreeRoot,
|
|
370
|
+
runtimeDir: manifest.runtimeDir,
|
|
371
|
+
taskId: manifest.taskId!,
|
|
372
|
+
generation: manifest.generation,
|
|
373
|
+
manifestPath,
|
|
374
|
+
requestId: request.id,
|
|
375
|
+
...(controllerBinding ? { controllerBinding } : {})
|
|
376
|
+
});
|
|
377
|
+
const format = (value: unknown): SandboxControlExecutionResult => {
|
|
378
|
+
const result = value as { status?: string };
|
|
379
|
+
return {
|
|
380
|
+
exitCode: result.status === 'failed' ? 1 : 0,
|
|
381
|
+
stdout: `${JSON.stringify(value)}\n`,
|
|
382
|
+
stderr: ''
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
if (request.family === 'task-orchestration') {
|
|
386
|
+
if (operation.family !== 'task-orchestration') throw new Error('SANDBOX_CONTROL_ORCHESTRATION_OPERATION_INVALID');
|
|
387
|
+
return format(await dispatchTaskControlOperation(context, operation));
|
|
388
|
+
}
|
|
389
|
+
if (operation.family !== 'task-lifecycle') throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
|
|
390
|
+
return format(dispatchTaskControlOperation(context, operation));
|
|
320
391
|
}
|
|
321
392
|
|
|
322
393
|
export async function runSandboxControlExecutor(requestPath: string, nonce: string): Promise<void> {
|
|
323
|
-
await waitForGate(nonce);
|
|
394
|
+
const gateOwner = await waitForGate(nonce);
|
|
324
395
|
process.disconnect?.();
|
|
325
|
-
const root = fs.realpathSync.native(process.cwd());
|
|
326
|
-
const request = JSON.parse(fs.readFileSync(requestPath, 'utf8')) as SandboxControlRequest;
|
|
327
396
|
const manifestPath = process.env.AGENT_INFRA_EXECUTOR_MANIFEST;
|
|
328
397
|
if (!manifestPath) throw new Error('SANDBOX_CONTROL_EXECUTOR_MANIFEST_MISSING');
|
|
329
|
-
const manifest =
|
|
398
|
+
const manifest = readSandboxControlManifest(manifestPath);
|
|
399
|
+
const root = fs.realpathSync.native(process.cwd());
|
|
330
400
|
if (fs.realpathSync.native(manifest.repoRoot) !== root) throw new Error('SANDBOX_CONTROL_EXECUTOR_ROOT_INVALID');
|
|
331
|
-
const
|
|
401
|
+
const raw = JSON.parse(fs.readFileSync(requestPath, 'utf8')) as unknown;
|
|
402
|
+
const request = validateSandboxControlRequest(raw, manifest);
|
|
403
|
+
const requestDirectory = path.resolve(path.dirname(requestPath));
|
|
404
|
+
const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
|
|
405
|
+
const channelRequestDirectory = path.resolve(path.join(manifest.channelDir, 'requests'));
|
|
406
|
+
if (requestDirectory !== processingDirectory && requestDirectory !== channelRequestDirectory) {
|
|
407
|
+
throw new Error('SANDBOX_CONTROL_EXECUTOR_REQUEST_INVALID');
|
|
408
|
+
}
|
|
409
|
+
let result: SandboxControlExecutionResult;
|
|
410
|
+
try {
|
|
411
|
+
assertSandboxControlExecutorAuthority(manifest, gateOwner);
|
|
412
|
+
result = await executeRequest(manifest, manifestPath, request);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
415
|
+
const code = /^([A-Z][A-Z0-9_]+)/u.exec(detail)?.[1] ?? 'SANDBOX_CONTROL_EXECUTOR_FAILED';
|
|
416
|
+
result = request.family === 'task-finalization'
|
|
417
|
+
? {
|
|
418
|
+
exitCode: 1,
|
|
419
|
+
stdout: `${JSON.stringify({
|
|
420
|
+
version: 1, status: 'failed', changed: false, accepted: true, result: null,
|
|
421
|
+
error: { code, message: detail, retryable: false }
|
|
422
|
+
})}\n`,
|
|
423
|
+
stderr: ''
|
|
424
|
+
}
|
|
425
|
+
: request.family === 'task-lifecycle'
|
|
426
|
+
? lifecycleFailure(code, detail)
|
|
427
|
+
: orchestrationFailure(code, detail);
|
|
428
|
+
}
|
|
332
429
|
process.stdout.write(result.stdout);
|
|
333
430
|
process.stderr.write(result.stderr);
|
|
334
431
|
process.exitCode = result.exitCode;
|
|
@@ -10,9 +10,7 @@ import {
|
|
|
10
10
|
import type { ProcessIdentity, ProcessIdentityProbe, ProcessIdentityState } from '../../server/process-state.ts';
|
|
11
11
|
import type {
|
|
12
12
|
SandboxControlExecution,
|
|
13
|
-
SandboxControlManifestLike,
|
|
14
13
|
SandboxControlManifest,
|
|
15
|
-
SandboxControlLegacyManifest,
|
|
16
14
|
SandboxControlStatus,
|
|
17
15
|
SandboxControlTimingPolicy
|
|
18
16
|
} from './protocol.ts';
|
|
@@ -468,7 +466,6 @@ function readProcessingSnapshot(root: string): SandboxControlCutoverSnapshot['pr
|
|
|
468
466
|
export function captureSandboxControlCutoverSnapshot(root: string): SandboxControlCutoverSnapshot {
|
|
469
467
|
const resolvedRoot = path.resolve(root);
|
|
470
468
|
const manifestPath = path.join(resolvedRoot, 'manifest.json');
|
|
471
|
-
if (fs.existsSync(manifestPath)) readSandboxControlManifestForTransition(manifestPath);
|
|
472
469
|
return {
|
|
473
470
|
root: resolvedRoot,
|
|
474
471
|
manifestRaw: readOptionalRaw(manifestPath),
|
|
@@ -485,10 +482,7 @@ export function assertSandboxControlCutoverSnapshot(snapshot: SandboxControlCuto
|
|
|
485
482
|
}
|
|
486
483
|
}
|
|
487
484
|
|
|
488
|
-
function readSandboxControlManifestValue(
|
|
489
|
-
manifestPath: string,
|
|
490
|
-
allowLegacy: boolean
|
|
491
|
-
): SandboxControlManifestLike {
|
|
485
|
+
function readSandboxControlManifestValue(manifestPath: string): SandboxControlManifest {
|
|
492
486
|
if (!regularFile(manifestPath)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
493
487
|
let manifest: Record<string, unknown>;
|
|
494
488
|
try {
|
|
@@ -500,49 +494,69 @@ function readSandboxControlManifestValue(
|
|
|
500
494
|
} catch {
|
|
501
495
|
throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
502
496
|
}
|
|
503
|
-
const candidate = manifest as Partial<SandboxControlManifest
|
|
504
|
-
|
|
505
|
-
|
|
497
|
+
const candidate = manifest as Partial<SandboxControlManifest>;
|
|
498
|
+
const expectedKeys = [
|
|
499
|
+
'branch', 'channelDir', 'container', 'containerIdentity', 'engine', 'generation',
|
|
500
|
+
'mode', 'processingDir', 'project', 'publicStatusDir', 'repoRoot', 'runtimeDir',
|
|
501
|
+
'taskId', 'token', 'worktreeRoot'
|
|
502
|
+
];
|
|
503
|
+
if (Object.keys(manifest).sort().join(',') !== expectedKeys.sort().join(',')) {
|
|
504
|
+
throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
506
505
|
}
|
|
507
506
|
if (typeof candidate.containerIdentity !== 'object' || candidate.containerIdentity === null
|
|
508
507
|
|| Array.isArray(candidate.containerIdentity)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
509
508
|
const containerIdentity = candidate.containerIdentity as Record<string, unknown>;
|
|
510
|
-
if (
|
|
509
|
+
if (Object.keys(containerIdentity).sort().join(',') !== 'id,labels'
|
|
510
|
+
|| typeof candidate.engine !== 'string' || candidate.engine.length === 0
|
|
511
511
|
|| typeof candidate.repoRoot !== 'string' || typeof candidate.worktreeRoot !== 'string'
|
|
512
512
|
|| typeof candidate.project !== 'string' || typeof candidate.container !== 'string'
|
|
513
513
|
|| typeof containerIdentity.id !== 'string' || containerIdentity.id.length === 0
|
|
514
514
|
|| typeof containerIdentity.labels !== 'object' || containerIdentity.labels === null
|
|
515
515
|
|| Array.isArray(containerIdentity.labels)
|
|
516
516
|
|| Object.values(containerIdentity.labels).some((value) => typeof value !== 'string')
|
|
517
|
-
|| typeof candidate.branch !== 'string'
|
|
517
|
+
|| typeof candidate.branch !== 'string'
|
|
518
|
+
|| (candidate.mode !== 'task-bound' && candidate.mode !== 'branch-only')
|
|
518
519
|
|| (candidate.taskId !== null && typeof candidate.taskId !== 'string')
|
|
519
520
|
|| typeof candidate.channelDir !== 'string' || typeof candidate.publicStatusDir !== 'string'
|
|
520
|
-
|| typeof candidate.processingDir !== 'string' || typeof candidate.
|
|
521
|
-
|| typeof candidate.
|
|
521
|
+
|| typeof candidate.processingDir !== 'string' || typeof candidate.runtimeDir !== 'string'
|
|
522
|
+
|| typeof candidate.token !== 'string'
|
|
523
|
+
|| typeof candidate.generation !== 'string'
|
|
524
|
+
|| (candidate.mode === 'task-bound' && (typeof candidate.taskId !== 'string' || candidate.taskId.length === 0))
|
|
525
|
+
|| (candidate.mode === 'branch-only' && candidate.taskId !== null)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
522
526
|
const root = path.dirname(path.resolve(manifestPath));
|
|
523
527
|
if (path.resolve(candidate.channelDir) !== path.join(root, 'channel')
|
|
524
528
|
|| path.resolve(candidate.publicStatusDir) !== path.join(root, 'public')
|
|
525
529
|
|| path.resolve(candidate.processingDir) !== path.join(root, 'processing')) {
|
|
526
530
|
throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
527
531
|
}
|
|
528
|
-
if (candidate.
|
|
529
|
-
if (!allowLegacy) {
|
|
530
|
-
throw new Error('SANDBOX_CONTROL_MANIFEST_VERSION_INVALID: expected version 5; container-only recreation is required');
|
|
531
|
-
}
|
|
532
|
-
return candidate as unknown as SandboxControlLegacyManifest;
|
|
533
|
-
}
|
|
534
|
-
if (typeof candidate.runtimeDir !== 'string' || path.resolve(candidate.runtimeDir) !== path.join(root, 'runtime')) {
|
|
532
|
+
if (path.resolve(candidate.runtimeDir) !== path.join(root, 'runtime')) {
|
|
535
533
|
throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
|
|
536
534
|
}
|
|
537
|
-
|
|
535
|
+
const normalized: SandboxControlManifest = {
|
|
536
|
+
engine: candidate.engine,
|
|
537
|
+
repoRoot: candidate.repoRoot,
|
|
538
|
+
worktreeRoot: candidate.worktreeRoot,
|
|
539
|
+
project: candidate.project,
|
|
540
|
+
container: candidate.container,
|
|
541
|
+
containerIdentity: {
|
|
542
|
+
id: containerIdentity.id as string,
|
|
543
|
+
labels: { ...(containerIdentity.labels as Record<string, string>) }
|
|
544
|
+
},
|
|
545
|
+
branch: candidate.branch,
|
|
546
|
+
mode: candidate.mode,
|
|
547
|
+
taskId: candidate.taskId,
|
|
548
|
+
token: candidate.token,
|
|
549
|
+
generation: candidate.generation,
|
|
550
|
+
channelDir: candidate.channelDir,
|
|
551
|
+
publicStatusDir: candidate.publicStatusDir,
|
|
552
|
+
processingDir: candidate.processingDir,
|
|
553
|
+
runtimeDir: candidate.runtimeDir
|
|
554
|
+
};
|
|
555
|
+
return normalized;
|
|
538
556
|
}
|
|
539
557
|
|
|
540
558
|
export function readSandboxControlManifest(manifestPath: string): SandboxControlManifest {
|
|
541
|
-
return readSandboxControlManifestValue(manifestPath
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
export function readSandboxControlManifestForTransition(manifestPath: string): SandboxControlManifestLike {
|
|
545
|
-
return readSandboxControlManifestValue(manifestPath, true);
|
|
559
|
+
return readSandboxControlManifestValue(manifestPath);
|
|
546
560
|
}
|
|
547
561
|
|
|
548
562
|
function readBrokerOwner(filePath: string): BrokerOwner | null {
|
|
@@ -561,6 +575,20 @@ function readBrokerOwner(filePath: string): BrokerOwner | null {
|
|
|
561
575
|
return owner as BrokerOwner;
|
|
562
576
|
}
|
|
563
577
|
|
|
578
|
+
export function assertSandboxControlBrokerOwner(
|
|
579
|
+
manifest: SandboxControlManifest,
|
|
580
|
+
options: Readonly<{ identityProbe?: ProcessIdentityProbe }> = {}
|
|
581
|
+
): BrokerOwner {
|
|
582
|
+
const root = path.dirname(path.resolve(manifest.publicStatusDir));
|
|
583
|
+
const owner = readBrokerOwner(path.join(root, 'broker.json'));
|
|
584
|
+
if (!owner) throw new Error('SANDBOX_CONTROL_OWNER_EVIDENCE_MISSING');
|
|
585
|
+
if (owner.token !== manifest.token || owner.generation !== manifest.generation) {
|
|
586
|
+
throw new Error('SANDBOX_CONTROL_OWNER_MISMATCH');
|
|
587
|
+
}
|
|
588
|
+
if (!ownerLive(owner, options.identityProbe)) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
|
|
589
|
+
return owner;
|
|
590
|
+
}
|
|
591
|
+
|
|
564
592
|
function readStatusOwner(filePath: string): SandboxControlStatus | null {
|
|
565
593
|
if (!fs.existsSync(filePath)) return null;
|
|
566
594
|
if (!regularFile(filePath)) throw new Error('SANDBOX_CONTROL_OWNER_MISMATCH');
|
|
@@ -585,7 +613,7 @@ function ownerLive(
|
|
|
585
613
|
return state === 'alive';
|
|
586
614
|
}
|
|
587
615
|
|
|
588
|
-
function readExecutions(manifest:
|
|
616
|
+
function readExecutions(manifest: SandboxControlManifest): SandboxControlExecution[] {
|
|
589
617
|
if (!fs.existsSync(manifest.processingDir)) return [];
|
|
590
618
|
const stat = fs.lstatSync(manifest.processingDir);
|
|
591
619
|
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('SANDBOX_CONTROL_EXECUTION_STILL_RUNNING');
|
|
@@ -705,7 +733,7 @@ export async function quiesceSandboxControlRoot(
|
|
|
705
733
|
await waitForStartupTransition(resolvedRoot, Math.max(0, forceAt - Date.now()), identityProbe);
|
|
706
734
|
|
|
707
735
|
const manifestPath = path.join(resolvedRoot, 'manifest.json');
|
|
708
|
-
const manifest = fs.existsSync(manifestPath) ?
|
|
736
|
+
const manifest = fs.existsSync(manifestPath) ? readSandboxControlManifest(manifestPath) : null;
|
|
709
737
|
const broker = readBrokerOwner(path.join(resolvedRoot, 'broker.json'));
|
|
710
738
|
const status = readStatusOwner(path.join(resolvedRoot, 'public', 'status.json'));
|
|
711
739
|
const statusOwner = status ? status.broker : null;
|
|
@@ -799,7 +827,7 @@ export async function removeSandboxControlRoot(
|
|
|
799
827
|
const stat = fs.lstatSync(resolvedRoot);
|
|
800
828
|
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('SANDBOX_CONTROL_CHANNEL_INVALID');
|
|
801
829
|
const manifestPath = path.join(resolvedRoot, 'manifest.json');
|
|
802
|
-
const manifest =
|
|
830
|
+
const manifest = readSandboxControlManifest(manifestPath);
|
|
803
831
|
const inspectContainer = options.inspectContainer
|
|
804
832
|
?? ((timeoutMs) => inspectSandboxControlContainer(manifest, { timeoutMs }));
|
|
805
833
|
const timeoutMs = options.timeoutMs ?? options.timing?.quiesceDeadlineMs ?? DEFAULT_QUIESCE_TIMEOUT_MS;
|
|
@@ -870,7 +898,7 @@ export async function removeSandboxControlRoot(
|
|
|
870
898
|
}
|
|
871
899
|
}
|
|
872
900
|
|
|
873
|
-
const currentManifest =
|
|
901
|
+
const currentManifest = readSandboxControlManifest(manifestPath);
|
|
874
902
|
if (currentManifest.token !== manifest.token || currentManifest.generation !== manifest.generation
|
|
875
903
|
|| !isSandboxControlRootQuiescing(resolvedRoot)) {
|
|
876
904
|
throw new Error('SANDBOX_CONTROL_OWNER_TRANSITION');
|
|
@@ -35,9 +35,7 @@ export type SandboxControlManifestBase = Readonly<{
|
|
|
35
35
|
branch: string; mode: 'task-bound' | 'branch-only'; taskId: string | null; token: string;
|
|
36
36
|
generation: string; channelDir: string; publicStatusDir: string; processingDir: string;
|
|
37
37
|
}>;
|
|
38
|
-
export type
|
|
39
|
-
export type SandboxControlManifest = SandboxControlManifestBase & Readonly<{ version: 5; runtimeDir: string }>;
|
|
40
|
-
export type SandboxControlManifestLike = SandboxControlLegacyManifest | SandboxControlManifest;
|
|
38
|
+
export type SandboxControlManifest = SandboxControlManifestBase & Readonly<{ runtimeDir: string }>;
|
|
41
39
|
export type SandboxControlBrokerOwner = ProcessIdentity & Readonly<{
|
|
42
40
|
version: 3;
|
|
43
41
|
brokerId: string;
|
|
@@ -228,6 +228,7 @@ export async function serveSandboxControl(
|
|
|
228
228
|
appendSandboxControlAudit(manifest, 'broker-start', { pid: broker.pid });
|
|
229
229
|
if (!recoverProcessing(manifest, brokerOwns)) return;
|
|
230
230
|
while (!signal.aborted) {
|
|
231
|
+
let settledExecution: ActiveExecution | null = null;
|
|
231
232
|
if (!brokerOwns()) break;
|
|
232
233
|
let current: SandboxControlManifest;
|
|
233
234
|
try {
|
|
@@ -277,18 +278,7 @@ export async function serveSandboxControl(
|
|
|
277
278
|
|
|
278
279
|
if (active?.settled) {
|
|
279
280
|
if (!brokerOwns()) break;
|
|
280
|
-
|
|
281
|
-
if (!brokerOwns()) break;
|
|
282
|
-
writeResponse(manifest, {
|
|
283
|
-
version: 2, id: active.request.id, phase: 'completed', exitCode: active.result.exitCode,
|
|
284
|
-
stdout: active.result.stdout, stderr: active.result.stderr, error: null
|
|
285
|
-
});
|
|
286
|
-
} else {
|
|
287
|
-
if (!brokerOwns()) break;
|
|
288
|
-
writeResponse(manifest, unknown(active.request.id));
|
|
289
|
-
}
|
|
290
|
-
if (!brokerOwns()) break;
|
|
291
|
-
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
281
|
+
settledExecution = active;
|
|
292
282
|
active = null;
|
|
293
283
|
}
|
|
294
284
|
|
|
@@ -327,6 +317,19 @@ export async function serveSandboxControl(
|
|
|
327
317
|
appendSandboxControlAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
|
|
328
318
|
lastState = stateKey;
|
|
329
319
|
}
|
|
320
|
+
if (settledExecution) {
|
|
321
|
+
if (!brokerOwns()) break;
|
|
322
|
+
if (settledExecution.result) {
|
|
323
|
+
writeResponse(manifest, {
|
|
324
|
+
version: 2, id: settledExecution.request.id, phase: 'completed', exitCode: settledExecution.result.exitCode,
|
|
325
|
+
stdout: settledExecution.result.stdout, stderr: settledExecution.result.stderr, error: null
|
|
326
|
+
});
|
|
327
|
+
} else {
|
|
328
|
+
writeResponse(manifest, unknown(settledExecution.request.id));
|
|
329
|
+
}
|
|
330
|
+
if (!brokerOwns()) break;
|
|
331
|
+
fs.rmSync(path.join(manifest.processingDir, settledExecution.request.id), { recursive: true, force: true });
|
|
332
|
+
}
|
|
330
333
|
|
|
331
334
|
let retiring = false;
|
|
332
335
|
for (const name of fs.readdirSync(requestsDir).sort()) {
|
|
@@ -8,7 +8,7 @@ import type { ProcessIdentity, ProcessIdentityProbe } from '../../server/process
|
|
|
8
8
|
import type {
|
|
9
9
|
SandboxControlExecution,
|
|
10
10
|
SandboxControlLease,
|
|
11
|
-
|
|
11
|
+
SandboxControlManifest,
|
|
12
12
|
SandboxControlStatus
|
|
13
13
|
} from './protocol.ts';
|
|
14
14
|
|
|
@@ -36,12 +36,12 @@ export function readJsonFile(filePath: string): unknown {
|
|
|
36
36
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export function statusPath(manifest:
|
|
39
|
+
export function statusPath(manifest: SandboxControlManifest): string {
|
|
40
40
|
return path.join(manifest.publicStatusDir, 'status.json');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export function writeSandboxControlStatus(
|
|
44
|
-
manifest:
|
|
44
|
+
manifest: SandboxControlManifest,
|
|
45
45
|
broker: ProcessIdentity & { brokerId: string },
|
|
46
46
|
state: SandboxControlStatus['state'],
|
|
47
47
|
reasonCode: string | null,
|
|
@@ -72,7 +72,7 @@ export function readSandboxControlStatus(directory: string): SandboxControlStatu
|
|
|
72
72
|
return parseSandboxControlStatus(readJsonFile(path.join(directory, 'status.json')));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export function readActiveLease(manifest:
|
|
75
|
+
export function readActiveLease(manifest: SandboxControlManifest, now = Date.now()): SandboxControlLease | null {
|
|
76
76
|
const filePath = path.join(path.dirname(manifest.publicStatusDir), 'lease.json');
|
|
77
77
|
if (!fs.existsSync(filePath)) return null;
|
|
78
78
|
const lease = readJsonFile(filePath) as Partial<SandboxControlLease> | null;
|
|
@@ -91,7 +91,7 @@ export function readActiveLease(manifest: SandboxControlManifestLike, now = Date
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
export function cleanupStaleSandboxControlLease(
|
|
94
|
-
manifest:
|
|
94
|
+
manifest: SandboxControlManifest,
|
|
95
95
|
now = Date.now(),
|
|
96
96
|
options: Readonly<{ identityProbe?: ProcessIdentityProbe }> = {}
|
|
97
97
|
): boolean {
|
|
@@ -121,7 +121,7 @@ export function cleanupStaleSandboxControlLease(
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
export function appendSandboxControlAudit(
|
|
124
|
-
manifest:
|
|
124
|
+
manifest: SandboxControlManifest,
|
|
125
125
|
event: string,
|
|
126
126
|
fields: Record<string, string | number | boolean | null> = {},
|
|
127
127
|
now = Date.now()
|
|
@@ -136,7 +136,7 @@ export function appendSandboxControlAudit(
|
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
export function executionPath(manifest:
|
|
139
|
+
export function executionPath(manifest: SandboxControlManifest, requestId: string): string {
|
|
140
140
|
return path.join(manifest.processingDir, requestId, 'execution.json');
|
|
141
141
|
}
|
|
142
142
|
|
package/lib/sandbox/mounts.ts
CHANGED
|
@@ -27,11 +27,12 @@ export function sandboxCoreBindMounts(
|
|
|
27
27
|
controlDir: string;
|
|
28
28
|
controlStatusDir: string;
|
|
29
29
|
runtimeDir?: string;
|
|
30
|
-
|
|
30
|
+
taskSources?: string[];
|
|
31
31
|
taskId?: string;
|
|
32
32
|
}
|
|
33
33
|
): SandboxBindMountDeclaration[] {
|
|
34
|
-
const
|
|
34
|
+
const taskSources = overrides.taskSources ?? [];
|
|
35
|
+
const taskBound = Boolean(taskSources.length > 0 && overrides.taskId);
|
|
35
36
|
const workspaceMounts = sandboxWorkspaceViewStatePaths(overrides.workspaceViewRoot).map(
|
|
36
37
|
({ state, hostPath }) => taskBound && state === 'active'
|
|
37
38
|
? {
|
|
@@ -57,7 +58,7 @@ export function sandboxCoreBindMounts(
|
|
|
57
58
|
];
|
|
58
59
|
if (taskBound) {
|
|
59
60
|
mounts.push({
|
|
60
|
-
hostPaths:
|
|
61
|
+
hostPaths: taskSources,
|
|
61
62
|
containerPath: path.posix.join('/workspace/.agents/workspace/active', overrides.taskId!),
|
|
62
63
|
readOnly: false
|
|
63
64
|
});
|