@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,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { declareImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
4
|
-
import { LEDGER_COLUMNS, LEDGER_HEADINGS, nextHdId,
|
|
4
|
+
import { LEDGER_COLUMNS, LEDGER_HEADINGS, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, nextHdId, parseLedgerDocument, validateLedgerRows } from './ledger.js';
|
|
5
5
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
6
|
import { writeTask } from './write.js';
|
|
7
7
|
import { allowsManualOverride } from './guard-override.js';
|
|
@@ -26,6 +26,8 @@ function normalizedEvidence(value) {
|
|
|
26
26
|
}
|
|
27
27
|
function ledgerReadErrorCode(error) {
|
|
28
28
|
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
|
|
29
|
+
if (code === LEDGER_SECTION_MISSING_CODE)
|
|
30
|
+
return code;
|
|
29
31
|
return code === 'TABLE_DUPLICATE_KEY' ? 'LEDGER_DUPLICATE_ID' : 'LEDGER_DOCUMENT_INVALID';
|
|
30
32
|
}
|
|
31
33
|
function maxHandshakeRounds(repoRoot) {
|
|
@@ -60,13 +62,7 @@ function rowMutation(row) {
|
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
function ledgerSectionMutation(content) {
|
|
63
|
-
|
|
64
|
-
return [];
|
|
65
|
-
const english = /^##\s+Activity Log\s*$/m.test(content);
|
|
66
|
-
return [{
|
|
67
|
-
kind: 'section', aliases: LEDGER_HEADINGS, heading: english ? 'Review Disagreement Ledger' : '审查分歧账本',
|
|
68
|
-
body: `| ${LEDGER_COLUMNS.join(' | ')} |\n|----|-------|-------|----------|--------|----------|`
|
|
69
|
-
}];
|
|
65
|
+
return [];
|
|
70
66
|
}
|
|
71
67
|
function implementationInputMutation(content, rows) {
|
|
72
68
|
const english = /^##\s+Activity Log\s*$/m.test(content);
|
|
@@ -92,7 +88,10 @@ function applyLedgerIntent(intent, options = {}) {
|
|
|
92
88
|
let rows;
|
|
93
89
|
try {
|
|
94
90
|
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
95
|
-
|
|
91
|
+
const ledger = parseLedgerDocument(content);
|
|
92
|
+
if (!ledger.present)
|
|
93
|
+
return failed(intent, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, resolved.taskId);
|
|
94
|
+
rows = ledger.rows;
|
|
96
95
|
}
|
|
97
96
|
catch (error) {
|
|
98
97
|
return failed(intent, ledgerReadErrorCode(error), error instanceof Error ? error.message : String(error), resolved.taskId);
|
package/dist/lib/task/ledger.js
CHANGED
|
@@ -12,18 +12,25 @@ const LEDGER_TERMINAL = new Set(['confirmed', 'closed', 'human-decided']);
|
|
|
12
12
|
// Statuses that represent a decision row awaiting or carrying a human ruling.
|
|
13
13
|
const HUMAN_DECISION_STATUSES = new Set(['needs-human-decision', 'human-decided']);
|
|
14
14
|
const FINDING_STATUSES = new Set(['open', 'accepted', 'adjusted', 'refuted', 'cannot-judge', 'confirmed', 'needs-human-decision', 'closed', 'human-decided']);
|
|
15
|
+
const LEDGER_SECTION_MISSING_CODE = 'LEDGER_SECTION_MISSING';
|
|
16
|
+
const LEDGER_SECTION_MISSING_MESSAGE = 'review disagreement ledger section is missing';
|
|
15
17
|
function isReviewStage(stage) {
|
|
16
18
|
return stage === 'analysis' || stage === 'plan' || stage === 'code';
|
|
17
19
|
}
|
|
18
20
|
// Parse all rows of the disagreement ledger table. Skips the heading, the
|
|
19
21
|
// header row (`| id | ... |`) and the `|---|` separator; ignores non-`|` lines.
|
|
20
22
|
// Rows with fewer than 6 columns are skipped (mirrors the JS gate parser).
|
|
21
|
-
function
|
|
23
|
+
function parseLedgerDocument(content) {
|
|
22
24
|
const table = parseTable(content, { sectionAliases: LEDGER_HEADINGS, columns: LEDGER_COLUMNS });
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if (!table)
|
|
26
|
+
return { present: false, rows: [] };
|
|
27
|
+
return {
|
|
28
|
+
present: true,
|
|
29
|
+
rows: table.rows.map(({ values, sourceLine }) => ({
|
|
30
|
+
id: values.id, stage: values.stage, round: values.round, severity: values.severity,
|
|
31
|
+
status: values.status, evidence: values.evidence, sourceLine
|
|
32
|
+
}))
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
35
|
function validateLedgerRows(rows) {
|
|
29
36
|
const seen = new Set();
|
|
@@ -104,5 +111,5 @@ function nextHdId(rows) {
|
|
|
104
111
|
}
|
|
105
112
|
return `HD-${max + 1}`;
|
|
106
113
|
}
|
|
107
|
-
export { LEDGER_HEADINGS, LEDGER_COLUMNS,
|
|
114
|
+
export { LEDGER_HEADINGS, LEDGER_COLUMNS, parseLedgerDocument, nextHdId, isReviewStage, validateLedgerRows, summarizeLedgerStage, LEDGER_TERMINAL, HUMAN_DECISION_STATUSES, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE };
|
|
108
115
|
//# sourceMappingURL=ledger.js.map
|
|
@@ -7,11 +7,14 @@ import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
|
7
7
|
import { locateHotTaskDirs, resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
|
|
8
8
|
import { configuredShortIdLength, executeShortIdCommand, loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.js';
|
|
9
9
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
10
|
+
import { validateCurrentTaskContract } from './current-contract.js';
|
|
11
|
+
import { isValidAgentInfraVersion } from '../version.js';
|
|
10
12
|
const lifecycleIntentCatalog = [
|
|
11
13
|
'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore'
|
|
12
14
|
];
|
|
13
15
|
const lifecycleFailureCatalog = [
|
|
14
16
|
'LIFECYCLE_DIRECTORY_RENAME_FAILED',
|
|
17
|
+
'LIFECYCLE_DIRECTORY_COPY_FAILED',
|
|
15
18
|
'LIFECYCLE_DOCUMENT_INVALID',
|
|
16
19
|
'LIFECYCLE_FINAL_STATE_INVALID',
|
|
17
20
|
'LIFECYCLE_IDENTITY_CONFLICT',
|
|
@@ -57,6 +60,9 @@ function failed(request, error, extra = {}) {
|
|
|
57
60
|
error, ...extra
|
|
58
61
|
};
|
|
59
62
|
}
|
|
63
|
+
export function taskLifecycleFailure(request, error, taskId = null) {
|
|
64
|
+
return failed(request, error, { taskId });
|
|
65
|
+
}
|
|
60
66
|
function normalizedRequest(request) {
|
|
61
67
|
if (!request || typeof request !== 'object' || !lifecycleIntentCatalog.includes(request.intent)) {
|
|
62
68
|
return { code: 'LIFECYCLE_PAYLOAD_INVALID', message: 'intent is not in the lifecycle catalog' };
|
|
@@ -290,6 +296,65 @@ function parseJournal(content) {
|
|
|
290
296
|
}
|
|
291
297
|
return value;
|
|
292
298
|
}
|
|
299
|
+
function directoryManifest(root) {
|
|
300
|
+
const entries = [];
|
|
301
|
+
const visit = (current, relative = '') => {
|
|
302
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
303
|
+
const relativePath = relative ? path.join(relative, entry.name) : entry.name;
|
|
304
|
+
const absolute = path.join(current, entry.name);
|
|
305
|
+
const stat = fs.lstatSync(absolute);
|
|
306
|
+
if (stat.isSymbolicLink())
|
|
307
|
+
throw new Error(`symbolic link is not allowed in lifecycle directory: ${relativePath}`);
|
|
308
|
+
if (entry.isDirectory()) {
|
|
309
|
+
entries.push({ relativePath, kind: 'directory', size: 0, digest: '' });
|
|
310
|
+
visit(absolute, relativePath);
|
|
311
|
+
}
|
|
312
|
+
else if (entry.isFile()) {
|
|
313
|
+
const digest = createHash('sha256').update(fs.readFileSync(absolute)).digest('hex');
|
|
314
|
+
entries.push({ relativePath, kind: 'file', size: stat.size, digest });
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
throw new Error(`unsupported lifecycle directory entry: ${relativePath}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
visit(root);
|
|
322
|
+
return entries;
|
|
323
|
+
}
|
|
324
|
+
function copyDirectoryVerified(source, target) {
|
|
325
|
+
if (fs.existsSync(target))
|
|
326
|
+
throw new Error(`target directory already exists: ${target}`);
|
|
327
|
+
const copy = (currentSource, currentTarget) => {
|
|
328
|
+
fs.mkdirSync(currentTarget);
|
|
329
|
+
for (const entry of fs.readdirSync(currentSource, { withFileTypes: true })) {
|
|
330
|
+
const sourcePath = path.join(currentSource, entry.name);
|
|
331
|
+
const targetPath = path.join(currentTarget, entry.name);
|
|
332
|
+
const stat = fs.lstatSync(sourcePath);
|
|
333
|
+
if (stat.isSymbolicLink())
|
|
334
|
+
throw new Error(`symbolic link is not allowed in lifecycle directory: ${entry.name}`);
|
|
335
|
+
if (entry.isDirectory())
|
|
336
|
+
copy(sourcePath, targetPath);
|
|
337
|
+
else if (entry.isFile())
|
|
338
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
339
|
+
else
|
|
340
|
+
throw new Error(`unsupported lifecycle directory entry: ${entry.name}`);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
copy(source, target);
|
|
344
|
+
const sourceManifest = directoryManifest(source);
|
|
345
|
+
const targetManifest = directoryManifest(target);
|
|
346
|
+
if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
|
|
347
|
+
throw new Error('copied lifecycle directory failed manifest verification');
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function removeCopiedSource(source, target) {
|
|
351
|
+
const sourceManifest = directoryManifest(source);
|
|
352
|
+
const targetManifest = directoryManifest(target);
|
|
353
|
+
if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
|
|
354
|
+
throw new Error('lifecycle source and target diverged before source cleanup');
|
|
355
|
+
}
|
|
356
|
+
fs.rmSync(source, { recursive: true, force: false });
|
|
357
|
+
}
|
|
293
358
|
function applyTaskLifecycle(requestInput, options = {}) {
|
|
294
359
|
const normalized = normalizedRequest(requestInput);
|
|
295
360
|
if ('code' in normalized)
|
|
@@ -351,7 +416,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
351
416
|
if (!spec.sources.includes(sourceState) && !existingJournal && !allowsManualOverride(options, 'LIFECYCLE_SOURCE_INVALID')) {
|
|
352
417
|
return failed(request, { code: 'LIFECYCLE_SOURCE_INVALID', message: `${request.intent} is not allowed from ${sourceState}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
353
418
|
}
|
|
354
|
-
if (sourcePath !== targetPath && fs.existsSync(targetPath)) {
|
|
419
|
+
if (sourcePath !== targetPath && fs.existsSync(targetPath) && !existingJournal) {
|
|
355
420
|
return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
356
421
|
}
|
|
357
422
|
const content = io.readFileSync(currentTaskFile);
|
|
@@ -366,6 +431,11 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
366
431
|
return failed(request, { code: 'LIFECYCLE_IDENTITY_INVALID', message: 'task.md id does not match its lifecycle task id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
367
432
|
if (!locateActivityLog(content) && !allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'))
|
|
368
433
|
return failed(request, { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
434
|
+
if (spec.target === 'active') {
|
|
435
|
+
const contract = validateCurrentTaskContract(content);
|
|
436
|
+
if (!contract.ok)
|
|
437
|
+
return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: contract.message }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
438
|
+
}
|
|
369
439
|
const registryRequest = executeShortIdCommand({
|
|
370
440
|
operation: 'list', activeDir: path.join(repoRoot, '.agents', 'workspace', 'active'),
|
|
371
441
|
shortIdLength: configuredShortIdLength(repoRoot)
|
|
@@ -394,6 +464,9 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
394
464
|
}
|
|
395
465
|
if (journal.taskId !== taskId || journal.intent !== request.intent)
|
|
396
466
|
return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: 'an in-progress lifecycle journal belongs to a different request' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
467
|
+
if (spec.target === 'active' && !isValidAgentInfraVersion(journal.metadata.agentInfraVersion)) {
|
|
468
|
+
return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `journal metadata agentInfraVersion must be a valid v-prefixed semver (received ${journal.metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
469
|
+
}
|
|
397
470
|
if (request.dryRun) {
|
|
398
471
|
if (!journal.failure)
|
|
399
472
|
return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: 'existing lifecycle journal has no durable failure evidence for a read-only probe' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
@@ -411,6 +484,9 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
411
484
|
catch (error) {
|
|
412
485
|
return failed(request, { code: 'LIFECYCLE_METADATA_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
413
486
|
}
|
|
487
|
+
if (spec.target === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
|
|
488
|
+
return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `metadata agentInfraVersion must be a valid v-prefixed semver (received ${metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
489
|
+
}
|
|
414
490
|
journal = { version: 1, taskId, intent: request.intent, intentDigest: digest, sourceState, targetState: spec.target, metadata, completedSteps: [] };
|
|
415
491
|
if (request.dryRun) {
|
|
416
492
|
const plannedMutations = mutationsFor(request, content, metadata, restoredFiles, allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'));
|
|
@@ -485,18 +561,55 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
485
561
|
(options.directoryRenameSync ?? io.renameSync)(sourcePath, targetPath);
|
|
486
562
|
}
|
|
487
563
|
catch (error) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
564
|
+
if (error.code === 'EXDEV') {
|
|
565
|
+
try {
|
|
566
|
+
copyDirectoryVerified(sourcePath, targetPath);
|
|
567
|
+
completed.add('directory-moved');
|
|
568
|
+
journal.completedSteps = [...completed];
|
|
569
|
+
const copiedJournalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
570
|
+
writeJournal(initialJournalPath, journal, io);
|
|
571
|
+
writeJournal(copiedJournalPath, journal, io);
|
|
572
|
+
removeCopiedSource(sourcePath, targetPath);
|
|
573
|
+
journalPath = copiedJournalPath;
|
|
574
|
+
}
|
|
575
|
+
catch (copyError) {
|
|
576
|
+
if (fs.existsSync(targetPath))
|
|
577
|
+
fs.rmSync(targetPath, { recursive: true, force: false });
|
|
578
|
+
const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(copyError) };
|
|
579
|
+
rememberJournalFailure(initialJournalPath, journal, io, failure);
|
|
580
|
+
return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
const failure = { code: 'LIFECYCLE_DIRECTORY_RENAME_FAILED', message: String(error) };
|
|
585
|
+
rememberJournalFailure(journalPath, journal, io, failure);
|
|
586
|
+
return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
587
|
+
}
|
|
491
588
|
}
|
|
492
|
-
completed.
|
|
493
|
-
|
|
494
|
-
|
|
589
|
+
if (completed.has('directory-moved') && !fs.existsSync(sourcePath)) {
|
|
590
|
+
journalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
591
|
+
}
|
|
592
|
+
else if (!completed.has('directory-moved')) {
|
|
593
|
+
completed.add('directory-moved');
|
|
594
|
+
journal.completedSteps = [...completed];
|
|
595
|
+
journalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
596
|
+
try {
|
|
597
|
+
writeJournal(journalPath, journal, io);
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, directory: { effect: 'move', changed: true }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (completed.has('directory-moved') && sourcePath !== targetPath && fs.existsSync(sourcePath) && fs.existsSync(targetPath)) {
|
|
495
605
|
try {
|
|
496
|
-
|
|
606
|
+
removeCopiedSource(sourcePath, targetPath);
|
|
607
|
+
journalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
497
608
|
}
|
|
498
609
|
catch (error) {
|
|
499
|
-
|
|
610
|
+
const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(error) };
|
|
611
|
+
rememberJournalFailure(journalPath, journal, io, failure);
|
|
612
|
+
return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
500
613
|
}
|
|
501
614
|
}
|
|
502
615
|
let shortId = { effect: 'unchanged', shortId: null, changed: false };
|
|
@@ -2,20 +2,22 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
5
|
-
import {
|
|
5
|
+
import { validateCurrentTaskContract } from './current-contract.js';
|
|
6
|
+
import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.js';
|
|
6
7
|
import { inspectArtifactDirectory } from './artifact-lifecycle.js';
|
|
7
|
-
import {
|
|
8
|
+
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
8
9
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
10
|
import { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
10
11
|
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
11
12
|
import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.js';
|
|
12
|
-
import { inspectPlatformPullRequest } from '../platform/pull-requests.js';
|
|
13
13
|
import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
14
14
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
15
15
|
import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
|
|
16
16
|
import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
|
|
17
17
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
18
18
|
import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.js';
|
|
19
|
+
import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.js';
|
|
20
|
+
import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.js';
|
|
19
21
|
function supportsLifecycleDelegation(client) {
|
|
20
22
|
return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
|
|
21
23
|
&& getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
|
|
@@ -105,9 +107,8 @@ function isCompletionEvidence(value) {
|
|
|
105
107
|
&& exactText(value.headTree)
|
|
106
108
|
&& exactText(value.worktreeTree)
|
|
107
109
|
&& exactText(value.lastReviewedCommit)
|
|
108
|
-
&& Number.isSafeInteger(value.prNumber)
|
|
109
|
-
&& value.
|
|
110
|
-
&& exactText(value.prHead);
|
|
110
|
+
&& (value.prNumber === null || (Number.isSafeInteger(value.prNumber) && value.prNumber > 0))
|
|
111
|
+
&& (value.prHead === null || exactText(value.prHead));
|
|
111
112
|
}
|
|
112
113
|
const ORCHESTRATION_RUN_KEYS = [
|
|
113
114
|
'taskId', 'runId', 'status', 'nextStage', 'stepCount', 'maxSteps', 'modelPolicy',
|
|
@@ -339,6 +340,43 @@ function highestRound(taskDir, family) {
|
|
|
339
340
|
function artifactName(family, round) {
|
|
340
341
|
return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
|
|
341
342
|
}
|
|
343
|
+
function validateSavedCurrentDiffBase(repoRoot, metadata, taskDir, reviewedHead) {
|
|
344
|
+
const reviewRound = highestRound(taskDir, 'review-code');
|
|
345
|
+
const reviewContent = fs.readFileSync(path.join(taskDir, artifactName('review-code', reviewRound)), 'utf8');
|
|
346
|
+
const savedTargetHead = extractReviewTargetHead(reviewContent);
|
|
347
|
+
const savedDiffBase = extractReviewDiffBase(reviewContent);
|
|
348
|
+
const savedReviewedHead = extractReviewedHead(reviewContent) || extractReviewBaseline(reviewContent);
|
|
349
|
+
if (!savedTargetHead && !savedDiffBase && !savedReviewedHead)
|
|
350
|
+
return null;
|
|
351
|
+
if (savedReviewedHead && savedReviewedHead !== reviewedHead) {
|
|
352
|
+
return { code: 'ORCHESTRATION_REVIEWED_HEAD_MISMATCH', message: 'review-code saved reviewed head does not match last_reviewed_commit' };
|
|
353
|
+
}
|
|
354
|
+
if (!savedTargetHead || !savedDiffBase) {
|
|
355
|
+
return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: 'review-code must save target head and diff base together' };
|
|
356
|
+
}
|
|
357
|
+
const savedBase = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
|
|
358
|
+
if (!savedBase.ok)
|
|
359
|
+
return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: savedBase.message };
|
|
360
|
+
if (savedBase.diffBase !== savedDiffBase) {
|
|
361
|
+
return { code: 'ORCHESTRATION_REVIEW_DIFF_BASE_MISMATCH', message: 'saved review diff base does not match reviewed head and saved target head' };
|
|
362
|
+
}
|
|
363
|
+
const target = resolveDeliveryTarget(repoRoot, {
|
|
364
|
+
remote: typeof metadata.delivery_remote === 'string' ? metadata.delivery_remote : undefined,
|
|
365
|
+
baseRef: typeof metadata.delivery_base_ref === 'string' ? metadata.delivery_base_ref : undefined
|
|
366
|
+
});
|
|
367
|
+
if (!target.ok)
|
|
368
|
+
return { code: target.code, message: target.message };
|
|
369
|
+
const currentTarget = resolveTargetHead(repoRoot, target.value);
|
|
370
|
+
if (!currentTarget.ok)
|
|
371
|
+
return { code: currentTarget.code, message: currentTarget.message };
|
|
372
|
+
const currentBase = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head);
|
|
373
|
+
if (!currentBase.ok)
|
|
374
|
+
return { code: currentBase.code, message: currentBase.message };
|
|
375
|
+
if (currentBase.diffBase !== savedDiffBase) {
|
|
376
|
+
return { code: 'ORCHESTRATION_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
342
380
|
function hasReviewAfterArtifact(taskDir, family, reviewFamily) {
|
|
343
381
|
const artifact = inspectArtifactDirectory(taskDir, family);
|
|
344
382
|
const review = inspectArtifactDirectory(taskDir, reviewFamily);
|
|
@@ -350,9 +388,21 @@ function hasReviewAfterArtifact(taskDir, family, reviewFamily) {
|
|
|
350
388
|
function latestReviewApproved(taskDir, family) {
|
|
351
389
|
const round = highestRound(taskDir, family);
|
|
352
390
|
if (round === 0)
|
|
353
|
-
return false;
|
|
354
|
-
|
|
355
|
-
|
|
391
|
+
return { approved: false, error: null };
|
|
392
|
+
let content;
|
|
393
|
+
try {
|
|
394
|
+
content = fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8');
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
return { approved: false, error: String(error) };
|
|
398
|
+
}
|
|
399
|
+
const parsed = parseReviewSummary(content);
|
|
400
|
+
if (!parsed.ok)
|
|
401
|
+
return { approved: false, error: `${parsed.code}: ${parsed.message}` };
|
|
402
|
+
const verdict = resolveCanonicalVerdict(parsed.summary);
|
|
403
|
+
if (!verdict.ok)
|
|
404
|
+
return { approved: false, error: `${verdict.code}: ${verdict.message}` };
|
|
405
|
+
return { approved: verdict.verdict === 'Approved', error: null };
|
|
356
406
|
}
|
|
357
407
|
function routeFromFacts(taskDir) {
|
|
358
408
|
const analysisRound = highestRound(taskDir, 'analysis');
|
|
@@ -369,7 +419,10 @@ function routeFromFacts(taskDir) {
|
|
|
369
419
|
const round = analysisReviewRound + 1;
|
|
370
420
|
return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
|
|
371
421
|
}
|
|
372
|
-
|
|
422
|
+
const analysisVerdict = latestReviewApproved(taskDir, 'review-analysis');
|
|
423
|
+
if (analysisVerdict.error)
|
|
424
|
+
return { error: analysisVerdict.error };
|
|
425
|
+
if (!analysisVerdict.approved) {
|
|
373
426
|
const round = analysisRound + 1;
|
|
374
427
|
return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
|
|
375
428
|
}
|
|
@@ -381,7 +434,10 @@ function routeFromFacts(taskDir) {
|
|
|
381
434
|
const round = planReviewRound + 1;
|
|
382
435
|
return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
|
|
383
436
|
}
|
|
384
|
-
|
|
437
|
+
const planVerdict = latestReviewApproved(taskDir, 'review-plan');
|
|
438
|
+
if (planVerdict.error)
|
|
439
|
+
return { error: planVerdict.error };
|
|
440
|
+
if (!planVerdict.approved) {
|
|
385
441
|
const round = planRound + 1;
|
|
386
442
|
return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
|
|
387
443
|
}
|
|
@@ -393,7 +449,10 @@ function routeFromFacts(taskDir) {
|
|
|
393
449
|
const round = codeReviewRound + 1;
|
|
394
450
|
return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
|
|
395
451
|
}
|
|
396
|
-
|
|
452
|
+
const codeVerdict = latestReviewApproved(taskDir, 'review-code');
|
|
453
|
+
if (codeVerdict.error)
|
|
454
|
+
return { error: codeVerdict.error };
|
|
455
|
+
if (!codeVerdict.approved) {
|
|
397
456
|
const round = codeRound + 1;
|
|
398
457
|
return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
|
|
399
458
|
}
|
|
@@ -418,13 +477,25 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
418
477
|
return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
|
|
419
478
|
}
|
|
420
479
|
let metadata;
|
|
421
|
-
|
|
422
|
-
|
|
480
|
+
let currentLedger = null;
|
|
481
|
+
if (resolved.state === 'active') {
|
|
482
|
+
const contract = validateCurrentTaskContract(content);
|
|
483
|
+
if (!contract.ok)
|
|
484
|
+
return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', contract.message, resolved.taskId);
|
|
485
|
+
metadata = contract.metadata;
|
|
486
|
+
currentLedger = contract.ledger;
|
|
423
487
|
}
|
|
424
|
-
|
|
425
|
-
|
|
488
|
+
else {
|
|
489
|
+
try {
|
|
490
|
+
metadata = parseTypedTaskFrontmatter(content);
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
494
|
+
}
|
|
426
495
|
}
|
|
427
496
|
const routed = routeFromFacts(resolved.taskDir);
|
|
497
|
+
if (routed && 'error' in routed)
|
|
498
|
+
return failed('ORCHESTRATION_REVIEW_INVALID', routed.error, resolved.taskId);
|
|
428
499
|
if (!routed)
|
|
429
500
|
return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
|
|
430
501
|
const run = readRun(resolved.taskDir);
|
|
@@ -440,7 +511,10 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
440
511
|
if (!review.ok || review.summary.manualValidation === null) {
|
|
441
512
|
return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
|
|
442
513
|
}
|
|
443
|
-
const
|
|
514
|
+
const ledger = currentLedger ?? parseLedgerDocument(content);
|
|
515
|
+
if (!ledger.present)
|
|
516
|
+
return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
|
|
517
|
+
const rows = ledger.rows;
|
|
444
518
|
const ledgerError = validateLedgerRows(rows);
|
|
445
519
|
if (ledgerError)
|
|
446
520
|
return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
|
|
@@ -450,29 +524,8 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
450
524
|
if (run?.status === 'completed') {
|
|
451
525
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
452
526
|
}
|
|
453
|
-
let config;
|
|
454
|
-
try {
|
|
455
|
-
config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
|
|
456
|
-
}
|
|
457
|
-
catch (error) {
|
|
458
|
-
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
459
|
-
}
|
|
460
|
-
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
461
|
-
return failed('ORCHESTRATION_CONFIG_INVALID', 'project configuration must be an object', resolved.taskId);
|
|
462
|
-
}
|
|
463
|
-
const projectConfig = config;
|
|
464
|
-
if (!Object.hasOwn(projectConfig, 'prFlow') || projectConfig.prFlow === 'disabled') {
|
|
465
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
466
|
-
}
|
|
467
|
-
if (projectConfig.prFlow !== 'required') {
|
|
468
|
-
return failed('ORCHESTRATION_CONFIG_INVALID', "project configuration 'prFlow' must be 'required' or 'disabled'", resolved.taskId);
|
|
469
|
-
}
|
|
470
|
-
const prNumber = Number(metadata.pr_number);
|
|
471
|
-
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
472
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
473
|
-
}
|
|
474
527
|
if (!run) {
|
|
475
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
528
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
476
529
|
}
|
|
477
530
|
if (run.status !== 'running' || run.pendingDelegation !== null) {
|
|
478
531
|
return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
|
|
@@ -486,34 +539,15 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
486
539
|
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
487
540
|
}
|
|
488
541
|
if (before.headTree !== before.worktreeTree) {
|
|
489
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
542
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
490
543
|
}
|
|
491
544
|
const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
|
|
492
545
|
if (before.head !== lastReviewedCommit) {
|
|
493
546
|
return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
|
|
494
547
|
}
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
inspection = inspect(resolved.taskId, { cwd: gitRoot });
|
|
499
|
-
}
|
|
500
|
-
catch (error) {
|
|
501
|
-
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
502
|
-
}
|
|
503
|
-
if (inspection.status === 'blocked') {
|
|
504
|
-
return failed('ORCHESTRATION_PR_INSPECTION_BLOCKED', inspection.error?.message ?? 'pull request inspection is blocked', resolved.taskId);
|
|
505
|
-
}
|
|
506
|
-
if (inspection.status === 'failed') {
|
|
507
|
-
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', inspection.error?.message ?? 'pull request inspection failed', resolved.taskId);
|
|
508
|
-
}
|
|
509
|
-
const inspectedPrNumber = inspection.task?.prNumber ?? inspection.prNumber;
|
|
510
|
-
const prHead = inspection.pullRequest?.head?.sha;
|
|
511
|
-
if (typeof prHead !== 'string') {
|
|
512
|
-
return failed('ORCHESTRATION_PR_INSPECTION_INVALID', 'pull request inspection returned incomplete identity evidence', resolved.taskId);
|
|
513
|
-
}
|
|
514
|
-
if (inspectedPrNumber !== prNumber || prHead !== before.head) {
|
|
515
|
-
return failed('ORCHESTRATION_PR_HEAD_MISMATCH', 'pull request identity does not match the reviewed local HEAD', resolved.taskId);
|
|
516
|
-
}
|
|
548
|
+
const diffBaseError = validateSavedCurrentDiffBase(resolved.repoRoot, metadata, resolved.taskDir, lastReviewedCommit);
|
|
549
|
+
if (diffBaseError)
|
|
550
|
+
return failed(diffBaseError.code, diffBaseError.message, resolved.taskId);
|
|
517
551
|
let after;
|
|
518
552
|
try {
|
|
519
553
|
after = capture(gitRoot);
|
|
@@ -522,7 +556,7 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
522
556
|
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
523
557
|
}
|
|
524
558
|
if (after.headTree !== after.worktreeTree) {
|
|
525
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
559
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
526
560
|
}
|
|
527
561
|
if (after.head !== before.head || after.headTree !== before.headTree) {
|
|
528
562
|
return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
|
|
@@ -535,8 +569,8 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
535
569
|
headTree: after.headTree,
|
|
536
570
|
worktreeTree: after.worktreeTree,
|
|
537
571
|
lastReviewedCommit,
|
|
538
|
-
prNumber,
|
|
539
|
-
prHead
|
|
572
|
+
prNumber: null,
|
|
573
|
+
prHead: null
|
|
540
574
|
};
|
|
541
575
|
const completed = withUpdatedRun(run, {
|
|
542
576
|
status: 'completed',
|
|
@@ -631,10 +665,7 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
631
665
|
}
|
|
632
666
|
const updated = withUpdatedRun(run, {
|
|
633
667
|
nextStage: next.stage,
|
|
634
|
-
pendingDelegation: receipt
|
|
635
|
-
commitAuthorization: next.stage === 'commit' && run.commitAuthorization.issuedAt === null
|
|
636
|
-
? { issuedAt: (options.now ?? (() => new Date().toISOString()))(), consumedAt: null }
|
|
637
|
-
: run.commitAuthorization
|
|
668
|
+
pendingDelegation: receipt
|
|
638
669
|
});
|
|
639
670
|
saveRun(resolved.taskDir, updated);
|
|
640
671
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
|
|
@@ -1056,16 +1087,15 @@ function advanceOrchestration(taskRef, options = {}) {
|
|
|
1056
1087
|
const result = consumeDelegation(run.pendingDelegation, { now: options.now });
|
|
1057
1088
|
if (!result.ok)
|
|
1058
1089
|
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
1059
|
-
|
|
1090
|
+
if (result.receipt.stage === 'commit') {
|
|
1091
|
+
return failed('ORCHESTRATED_COMMIT_REMOVED', 'Orchestrated commit is no longer a lifecycle stage; use local checkpoint delivery from code-task', resolved.taskId);
|
|
1092
|
+
}
|
|
1060
1093
|
const updated = withUpdatedRun(run, {
|
|
1061
|
-
status:
|
|
1094
|
+
status: 'running',
|
|
1062
1095
|
nextStage: null,
|
|
1063
1096
|
stepCount: run.stepCount + 1,
|
|
1064
1097
|
pendingDelegation: null,
|
|
1065
|
-
receipts: Object.freeze([...run.receipts, result.receipt])
|
|
1066
|
-
commitAuthorization: completed
|
|
1067
|
-
? { ...run.commitAuthorization, consumedAt: (options.now ?? (() => new Date().toISOString()))() }
|
|
1068
|
-
: run.commitAuthorization
|
|
1098
|
+
receipts: Object.freeze([...run.receipts, result.receipt])
|
|
1069
1099
|
});
|
|
1070
1100
|
saveRun(resolved.taskDir, updated);
|
|
1071
1101
|
return { status: updated.status, changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|