@fitlab-ai/agent-infra 0.9.2 → 0.9.3
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 +10 -9
- package/README.zh-CN.md +10 -9
- package/dist/bin/internal-cli.js +15 -0
- package/dist/lib/agent-clients/adapter.js +45 -2
- package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +10 -1
- package/dist/lib/agent-clients/adapters/codex.js +9 -0
- package/dist/lib/agent-clients/adapters/index.js +2 -2
- package/dist/lib/agent-clients/adapters/opencode.js +9 -0
- package/dist/lib/agent-clients/config.js +42 -4
- package/dist/lib/agent-clients/next-steps.js +11 -2
- package/dist/lib/agent-clients/reconcile.js +2 -1
- package/dist/lib/agent-clients/registry.js +7 -1
- package/dist/lib/agent-clients/schema.js +18 -0
- package/dist/lib/agent-clients/tokens.js +49 -0
- package/dist/lib/agent-clients/types.js +1 -1
- package/dist/lib/defaults.json +1 -1
- package/dist/lib/git/workflow.js +54 -1
- package/dist/lib/internal/agent-client.js +61 -4
- package/dist/lib/internal/git-workflow.js +6 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +29 -2
- package/dist/lib/internal/platform-issue.js +7 -0
- package/dist/lib/internal/platform-pr-review.js +132 -0
- package/dist/lib/internal/platform-pr.js +20 -2
- package/dist/lib/internal/platform-release-notes.js +52 -16
- package/dist/lib/internal/pr-review-grade.js +140 -0
- package/dist/lib/internal/release-workflow.js +13 -6
- package/dist/lib/internal/task-activity.js +73 -0
- package/dist/lib/internal/task-event.js +17 -1
- package/dist/lib/internal/task-lifecycle.js +7 -0
- package/dist/lib/internal/task-orchestration.js +51 -6
- package/dist/lib/internal/task-review.js +8 -3
- package/dist/lib/platform/adapters.js +8 -1
- package/dist/lib/platform/completion-backfill.js +122 -0
- package/dist/lib/platform/issue-comments.js +3 -2
- package/dist/lib/platform/merged-pr-equivalence.js +115 -5
- package/dist/lib/platform/pr-checks.js +64 -41
- package/dist/lib/platform/pr-review.js +166 -0
- package/dist/lib/platform/pull-requests.js +324 -3
- package/dist/lib/platform/release-notes.js +85 -3
- package/dist/lib/pr-review/evidence-grading.js +229 -0
- package/dist/lib/run/tui.js +3 -5
- package/dist/lib/sandbox/commands/create.js +0 -20
- package/dist/lib/sandbox/commands/show.js +1 -1
- package/dist/lib/task/activity-intent.js +101 -0
- package/dist/lib/task/artifact-lifecycle.js +2 -1
- package/dist/lib/task/commands/log.js +15 -14
- package/dist/lib/task/delegation-receipts.js +23 -2
- package/dist/lib/task/events.js +67 -28
- package/dist/lib/task/finalization-artifacts.js +35 -0
- package/dist/lib/task/lifecycle-execution.js +46 -0
- package/dist/lib/task/orchestration.js +408 -44
- package/dist/lib/task/review-finalization.js +29 -9
- package/dist/lib/task/task-execution-lock.js +231 -0
- package/dist/lib/task/verification-checks.js +1 -1
- package/dist/lib/task/verification-engine.js +218 -2
- package/dist/lib/task/verification.js +4 -3
- package/dist/lib/task/workspace-snapshot.js +17 -5
- package/lib/agent-clients/adapter.ts +75 -2
- package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
- package/lib/agent-clients/adapters/claude-code.ts +10 -1
- package/lib/agent-clients/adapters/codex.ts +9 -0
- package/lib/agent-clients/adapters/index.ts +2 -2
- package/lib/agent-clients/adapters/opencode.ts +9 -0
- package/lib/agent-clients/config.ts +49 -4
- package/lib/agent-clients/next-steps.ts +15 -2
- package/lib/agent-clients/reconcile.ts +2 -1
- package/lib/agent-clients/registry.ts +10 -0
- package/lib/agent-clients/schema.ts +18 -0
- package/lib/agent-clients/tokens.ts +53 -0
- package/lib/agent-clients/types.ts +15 -1
- package/lib/defaults.json +1 -1
- package/lib/git/workflow.ts +66 -2
- package/lib/internal/agent-client.ts +59 -4
- package/lib/internal/git-workflow.ts +5 -1
- package/lib/internal/platform-checks.ts +4 -4
- package/lib/internal/platform-comment.ts +17 -2
- package/lib/internal/platform-issue.ts +4 -0
- package/lib/internal/platform-pr-review.ts +104 -0
- package/lib/internal/platform-pr.ts +14 -1
- package/lib/internal/platform-release-notes.ts +59 -16
- package/lib/internal/pr-review-grade.ts +130 -0
- package/lib/internal/release-workflow.ts +13 -6
- package/lib/internal/task-activity.ts +55 -0
- package/lib/internal/task-event.ts +9 -1
- package/lib/internal/task-lifecycle.ts +4 -0
- package/lib/internal/task-orchestration.ts +45 -6
- package/lib/internal/task-review.ts +8 -3
- package/lib/platform/adapters.ts +29 -2
- package/lib/platform/completion-backfill.ts +134 -0
- package/lib/platform/issue-comments.ts +3 -2
- package/lib/platform/merged-pr-equivalence.ts +127 -5
- package/lib/platform/pr-checks.ts +72 -45
- package/lib/platform/pr-review.ts +201 -0
- package/lib/platform/pull-requests.ts +395 -2
- package/lib/platform/release-notes.ts +100 -4
- package/lib/pr-review/evidence-grading.ts +318 -0
- package/lib/run/tui.ts +3 -4
- package/lib/sandbox/commands/create.ts +0 -20
- package/lib/sandbox/commands/show.ts +1 -1
- package/lib/server/server.schema.json +2 -2
- package/lib/task/activity-intent.ts +128 -0
- package/lib/task/artifact-lifecycle.ts +2 -1
- package/lib/task/commands/log.ts +15 -14
- package/lib/task/delegation-receipts.ts +32 -3
- package/lib/task/events.ts +75 -30
- package/lib/task/finalization-artifacts.ts +58 -0
- package/lib/task/lifecycle-execution.ts +106 -0
- package/lib/task/orchestration.ts +543 -64
- package/lib/task/review-finalization.ts +40 -9
- package/lib/task/task-execution-lock.ts +296 -0
- package/lib/task/verification-checks.ts +1 -1
- package/lib/task/verification-engine.ts +249 -2
- package/lib/task/verification.ts +5 -3
- package/lib/task/workspace-snapshot.ts +26 -5
- package/package.json +3 -3
- package/templates/.agents/QUICKSTART.en.md +25 -8
- package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
- package/templates/.agents/README.en.md +10 -7
- package/templates/.agents/README.zh-CN.md +10 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
- package/templates/.agents/rules/create-issue.github.en.md +4 -2
- package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
- package/templates/.agents/rules/issue-fields.github.en.md +3 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-sync.github.en.md +7 -3
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
- package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
- package/templates/.agents/rules/next-step-output.en.md +3 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
- package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +2 -2
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-sync.en.md +3 -1
- package/templates/.agents/rules/pr-sync.github.en.md +11 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
- package/templates/.agents/rules/release-commands.en.md +1 -1
- package/templates/.agents/rules/release-commands.github.en.md +12 -2
- package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
- package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +11 -0
- package/templates/.agents/rules/task-management.zh-CN.md +11 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
- package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +9 -6
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +11 -8
- package/templates/.agents/skills/commit/SKILL.en.md +3 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +21 -6
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +21 -6
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +3 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/release/SKILL.en.md +16 -10
- package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
- package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-code/SKILL.en.md +9 -6
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-pr/SKILL.en.md +158 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +158 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +35 -61
- package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
- package/templates/.agents/workflows/code-review.en.yaml +1 -1
- package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +4 -4
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
- package/templates/.agents/workflows/refactoring.en.yaml +3 -3
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
- package/templates/.claude/commands/post-release.en.md +1 -0
- package/templates/.claude/commands/post-release.zh-CN.md +1 -0
- package/templates/.claude/commands/review-pr.en.md +8 -0
- package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
- package/templates/.gemini/commands/_project_/review-pr.en.toml +10 -0
- package/templates/.gemini/commands/_project_/review-pr.zh-CN.toml +10 -0
- package/templates/.opencode/commands/post-release.en.md +2 -0
- package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
- package/templates/.opencode/commands/review-pr.en.md +11 -0
- package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
- package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
- package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
- package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
- package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
- package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/release.en.toml +0 -8
- package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/test.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
- package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
- package/templates/.gemini/settings.json +0 -3
|
@@ -7,8 +7,18 @@ import { inspectArtifactDirectory } from './artifact-lifecycle.js';
|
|
|
7
7
|
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
8
8
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
9
|
import { activateDelegation, completeDelegationStage, consumeDelegation, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
11
|
+
import { inspectPlatformPullRequest } from '../platform/pull-requests.js';
|
|
12
|
+
import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
13
|
+
import { isAgentClientId } from '../agent-clients/types.js';
|
|
14
|
+
import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
|
|
15
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
16
|
+
function supportsLifecycleDelegation(client) {
|
|
17
|
+
return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
|
|
18
|
+
&& getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
|
|
19
|
+
&& getAgentClientDelegationEvidence(client).actualModel !== 'unavailable'
|
|
20
|
+
&& getAgentClientDelegationEvidence(client).actualReasoningEffort !== 'unavailable';
|
|
21
|
+
}
|
|
12
22
|
function orchestrationPath(taskDir) {
|
|
13
23
|
return path.join(taskDir, 'orchestration.json');
|
|
14
24
|
}
|
|
@@ -16,7 +26,22 @@ function readRun(taskDir) {
|
|
|
16
26
|
const file = orchestrationPath(taskDir);
|
|
17
27
|
if (!fs.existsSync(file))
|
|
18
28
|
return null;
|
|
19
|
-
|
|
29
|
+
const run = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
30
|
+
if (run.schemaVersion !== 2 || !isV2Policy(run.modelPolicy))
|
|
31
|
+
return run;
|
|
32
|
+
return {
|
|
33
|
+
...run,
|
|
34
|
+
modelPolicy: {
|
|
35
|
+
executor: {
|
|
36
|
+
model: run.modelPolicy.executor.model,
|
|
37
|
+
reasoningEffort: run.modelPolicy.executor.reasoningEffort
|
|
38
|
+
},
|
|
39
|
+
reviewer: {
|
|
40
|
+
model: run.modelPolicy.reviewer.model,
|
|
41
|
+
reasoningEffort: run.modelPolicy.reviewer.reasoningEffort
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
20
45
|
}
|
|
21
46
|
function atomicWrite(file, value) {
|
|
22
47
|
const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
@@ -29,36 +54,209 @@ function saveRun(taskDir, run) {
|
|
|
29
54
|
function withUpdatedRun(run, updates) {
|
|
30
55
|
return Object.freeze({ ...run, ...updates, updatedAt: new Date().toISOString() });
|
|
31
56
|
}
|
|
32
|
-
function failed(code, message, taskId = null) {
|
|
33
|
-
return { status: 'failed', changed: false, taskId, run: null, next: null, error: { code, message } };
|
|
57
|
+
function failed(code, message, taskId = null, details = {}) {
|
|
58
|
+
return { status: 'failed', changed: false, taskId, run: null, next: null, error: { code, message, ...details } };
|
|
59
|
+
}
|
|
60
|
+
function validModel(value) {
|
|
61
|
+
return typeof value === 'string' && value.length > 0 && value.trim() === value;
|
|
62
|
+
}
|
|
63
|
+
function validateModelPolicy(policy) {
|
|
64
|
+
if (!policy
|
|
65
|
+
|| !validModel(policy.executor?.model)
|
|
66
|
+
|| !validModel(policy.executor?.reasoningEffort)
|
|
67
|
+
|| !validModel(policy.reviewer?.model)
|
|
68
|
+
|| !validModel(policy.reviewer?.reasoningEffort)) {
|
|
69
|
+
return { code: 'ORCHESTRATION_MODEL_POLICY_REQUIRED', message: 'executor and reviewer model and reasoning effort are required' };
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function sameModelPolicy(left, right) {
|
|
74
|
+
return left.executor.model === right.executor.model
|
|
75
|
+
&& left.executor.reasoningEffort === right.executor.reasoningEffort
|
|
76
|
+
&& left.reviewer.model === right.reviewer.model
|
|
77
|
+
&& left.reviewer.reasoningEffort === right.reviewer.reasoningEffort;
|
|
78
|
+
}
|
|
79
|
+
function isV2Policy(policy) {
|
|
80
|
+
return typeof policy?.executor === 'object' && typeof policy?.reviewer === 'object';
|
|
81
|
+
}
|
|
82
|
+
function rolePolicy(run, role) {
|
|
83
|
+
return isV2Policy(run.modelPolicy) ? run.modelPolicy[role] : null;
|
|
84
|
+
}
|
|
85
|
+
function resolveProjectPolicy(repoRoot, client) {
|
|
86
|
+
const configPath = path.join(repoRoot, '.agents', '.airc.json');
|
|
87
|
+
if (!fs.existsSync(configPath))
|
|
88
|
+
return undefined;
|
|
89
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
90
|
+
return normalizeAgentClients(raw).state[client].orchestration;
|
|
34
91
|
}
|
|
35
92
|
function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
36
93
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
37
94
|
if (!resolved.ok)
|
|
38
95
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
39
|
-
|
|
96
|
+
if (!isAgentClientId(options.client)) {
|
|
97
|
+
return failed('ORCHESTRATION_PAYLOAD_INVALID', 'begin-or-resume requires a known client', resolved.taskId);
|
|
98
|
+
}
|
|
99
|
+
let existing;
|
|
100
|
+
try {
|
|
101
|
+
existing = readRun(resolved.taskDir);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
105
|
+
}
|
|
40
106
|
if (existing) {
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return
|
|
107
|
+
if (![1, 2].includes(existing.schemaVersion)
|
|
108
|
+
|| !Array.isArray(existing.receipts)
|
|
109
|
+
|| !Object.prototype.hasOwnProperty.call(existing, 'pendingDelegation')) {
|
|
110
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'orchestration state has an invalid schema', resolved.taskId);
|
|
111
|
+
}
|
|
112
|
+
if (existing.status === 'completed' && existing.schemaVersion === 1) {
|
|
113
|
+
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
114
|
+
}
|
|
115
|
+
if (existing.schemaVersion === 2) {
|
|
116
|
+
const policyError = validateModelPolicy(isV2Policy(existing.modelPolicy) ? existing.modelPolicy : undefined);
|
|
117
|
+
if (policyError
|
|
118
|
+
|| !existing.modelPolicySource
|
|
119
|
+
|| !isAgentClientId(existing.modelPolicySource.client)
|
|
120
|
+
|| !Array.isArray(existing.recoveryHistory)) {
|
|
121
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'schemaVersion 2 orchestration state is incomplete', resolved.taskId);
|
|
122
|
+
}
|
|
123
|
+
if (existing.status === 'completed') {
|
|
124
|
+
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
125
|
+
}
|
|
126
|
+
if (existing.modelPolicySource.client !== options.client) {
|
|
127
|
+
return failed('ORCHESTRATION_CLIENT_MISMATCH', 'provided client does not match the persisted run policy source', resolved.taskId);
|
|
128
|
+
}
|
|
129
|
+
if (options.modelPolicy) {
|
|
130
|
+
const suppliedError = validateModelPolicy(options.modelPolicy);
|
|
131
|
+
if (suppliedError)
|
|
132
|
+
return failed(suppliedError.code, suppliedError.message, resolved.taskId);
|
|
133
|
+
if (!sameModelPolicy(existing.modelPolicy, options.modelPolicy)) {
|
|
134
|
+
return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
|
|
138
|
+
const resumed = withUpdatedRun(existing, { status: 'running', pause: null });
|
|
139
|
+
saveRun(resolved.taskDir, resumed);
|
|
140
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
141
|
+
}
|
|
142
|
+
return { status: existing.status, changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
143
|
+
}
|
|
144
|
+
let policy = options.modelPolicy;
|
|
145
|
+
let sourceKind = 'explicit';
|
|
146
|
+
if (!policy) {
|
|
147
|
+
try {
|
|
148
|
+
policy = resolveProjectPolicy(resolved.repoRoot, options.client);
|
|
149
|
+
sourceKind = 'project-config';
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const policyError = validateModelPolicy(policy);
|
|
156
|
+
if (policyError) {
|
|
157
|
+
return failed(policyError.code, policyError.message, resolved.taskId, {
|
|
158
|
+
client: options.client,
|
|
159
|
+
missingFields: ['executor.model', 'executor.reasoningEffort', 'reviewer.model', 'reviewer.reasoningEffort'],
|
|
160
|
+
modelSelectionContext: getAgentClientModelSelection(options.client)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (existing.pendingDelegation !== null) {
|
|
164
|
+
const alreadyPaused = existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_DELEGATION_BUSY';
|
|
165
|
+
if (alreadyPaused)
|
|
166
|
+
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
167
|
+
const paused = withUpdatedRun(existing, {
|
|
168
|
+
status: 'paused',
|
|
169
|
+
pause: { code: 'ORCHESTRATION_DELEGATION_BUSY', message: 'legacy run has a pending delegation', recoverable: false }
|
|
170
|
+
});
|
|
171
|
+
saveRun(resolved.taskDir, paused);
|
|
172
|
+
return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
|
|
173
|
+
}
|
|
174
|
+
if (existing.receipts.length > 0) {
|
|
175
|
+
const alreadyPaused = existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED';
|
|
176
|
+
if (alreadyPaused)
|
|
177
|
+
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
178
|
+
const paused = withUpdatedRun(existing, {
|
|
179
|
+
status: 'paused',
|
|
180
|
+
pause: {
|
|
181
|
+
code: 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED',
|
|
182
|
+
message: 'legacy receipts do not contain actual reasoning-effort evidence',
|
|
183
|
+
recoverable: false
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
saveRun(resolved.taskDir, paused);
|
|
187
|
+
return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
|
|
188
|
+
}
|
|
189
|
+
if (existing.modelPolicy
|
|
190
|
+
&& typeof existing.modelPolicy.executor === 'string'
|
|
191
|
+
&& (existing.modelPolicy.executor !== policy.executor.model
|
|
192
|
+
|| existing.modelPolicy.reviewer !== policy.reviewer.model)) {
|
|
193
|
+
return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
|
|
194
|
+
}
|
|
195
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
196
|
+
const source = { kind: sourceKind, client: options.client, resolvedAt: now };
|
|
197
|
+
const clearsModelPause = existing.pause?.code === 'ORCHESTRATION_MODEL_EVIDENCE_MISSING';
|
|
198
|
+
const resultingStatus = clearsModelPause ? 'running' : existing.status;
|
|
199
|
+
const recovery = {
|
|
200
|
+
code: 'MODEL_POLICY_SUPPLEMENTED',
|
|
201
|
+
recoveredAt: now,
|
|
202
|
+
previousSchemaVersion: 1,
|
|
203
|
+
previousStatus: existing.status,
|
|
204
|
+
previousPause: existing.pause,
|
|
205
|
+
policySource: source,
|
|
206
|
+
receiptCount: 0,
|
|
207
|
+
pendingDelegation: false,
|
|
208
|
+
resultingStatus
|
|
209
|
+
};
|
|
210
|
+
const recovered = Object.freeze({
|
|
211
|
+
...existing,
|
|
212
|
+
schemaVersion: 2,
|
|
213
|
+
status: resultingStatus,
|
|
214
|
+
modelPolicy: policy,
|
|
215
|
+
modelPolicySource: source,
|
|
216
|
+
recoveryHistory: Object.freeze([recovery]),
|
|
217
|
+
pause: clearsModelPause ? null : existing.pause,
|
|
218
|
+
updatedAt: now
|
|
219
|
+
});
|
|
220
|
+
saveRun(resolved.taskDir, recovered);
|
|
221
|
+
return { status: resultingStatus, changed: true, taskId: resolved.taskId, run: recovered, next: null, error: null };
|
|
222
|
+
}
|
|
223
|
+
let policy = options.modelPolicy;
|
|
224
|
+
let sourceKind = 'explicit';
|
|
225
|
+
if (!policy) {
|
|
226
|
+
try {
|
|
227
|
+
policy = resolveProjectPolicy(resolved.repoRoot, options.client);
|
|
228
|
+
sourceKind = 'project-config';
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
45
232
|
}
|
|
46
|
-
return { status: existing.status, changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
47
233
|
}
|
|
234
|
+
const policyError = validateModelPolicy(policy);
|
|
235
|
+
if (policyError)
|
|
236
|
+
return failed(policyError.code, policyError.message, resolved.taskId, {
|
|
237
|
+
client: options.client,
|
|
238
|
+
missingFields: ['executor.model', 'executor.reasoningEffort', 'reviewer.model', 'reviewer.reasoningEffort'],
|
|
239
|
+
modelSelectionContext: getAgentClientModelSelection(options.client)
|
|
240
|
+
});
|
|
48
241
|
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
242
|
+
const source = { kind: sourceKind, client: options.client, resolvedAt: now };
|
|
49
243
|
const run = {
|
|
50
|
-
schemaVersion:
|
|
244
|
+
schemaVersion: 2,
|
|
51
245
|
taskId: resolved.taskId,
|
|
52
246
|
runId: (options.id ?? randomUUID)(),
|
|
53
247
|
status: 'running',
|
|
54
248
|
nextStage: null,
|
|
55
249
|
stepCount: 0,
|
|
56
250
|
maxSteps: options.maxSteps ?? 24,
|
|
251
|
+
modelPolicy: policy,
|
|
252
|
+
modelPolicySource: source,
|
|
253
|
+
recoveryHistory: [],
|
|
57
254
|
baseline: '',
|
|
58
255
|
pendingDelegation: null,
|
|
59
256
|
receipts: [],
|
|
60
257
|
pause: null,
|
|
61
258
|
commitAuthorization: { issuedAt: null, consumedAt: null },
|
|
259
|
+
completionEvidence: null,
|
|
62
260
|
createdAt: now,
|
|
63
261
|
updatedAt: now
|
|
64
262
|
};
|
|
@@ -146,24 +344,29 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
146
344
|
catch (error) {
|
|
147
345
|
return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
|
|
148
346
|
}
|
|
347
|
+
let metadata;
|
|
149
348
|
try {
|
|
150
|
-
parseTypedTaskFrontmatter(content);
|
|
349
|
+
metadata = parseTypedTaskFrontmatter(content);
|
|
151
350
|
}
|
|
152
351
|
catch (error) {
|
|
153
352
|
return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
154
353
|
}
|
|
155
|
-
const
|
|
156
|
-
if (!
|
|
354
|
+
const routed = routeFromFacts(resolved.taskDir);
|
|
355
|
+
if (!routed)
|
|
157
356
|
return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
|
|
357
|
+
const run = readRun(resolved.taskDir);
|
|
358
|
+
const policy = run ? rolePolicy(run, routed.role) : null;
|
|
359
|
+
const next = {
|
|
360
|
+
...routed,
|
|
361
|
+
requestedModel: policy?.model ?? null,
|
|
362
|
+
requestedReasoningEffort: policy?.reasoningEffort ?? null
|
|
363
|
+
};
|
|
158
364
|
if (next.stage === 'commit') {
|
|
159
365
|
const reviewRound = highestRound(resolved.taskDir, 'review-code');
|
|
160
366
|
const review = parseReviewSummary(fs.readFileSync(path.join(resolved.taskDir, artifactName('review-code', reviewRound)), 'utf8'));
|
|
161
367
|
if (!review.ok || review.summary.manualValidation === null) {
|
|
162
368
|
return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
|
|
163
369
|
}
|
|
164
|
-
if (review.summary.manualValidation > 0) {
|
|
165
|
-
return failed('ORCHESTRATION_MANUAL_VALIDATION_PENDING', 'manual validation must be completed before commit', resolved.taskId);
|
|
166
|
-
}
|
|
167
370
|
const rows = parseLedger(content);
|
|
168
371
|
const ledgerError = validateLedgerRows(rows);
|
|
169
372
|
if (ledgerError)
|
|
@@ -171,8 +374,107 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
171
374
|
if (!summarizeLedgerStage(rows, 'code').canAdvance) {
|
|
172
375
|
return failed('ORCHESTRATION_LEDGER_BLOCKED', 'code review ledger has unresolved findings or human decisions', resolved.taskId);
|
|
173
376
|
}
|
|
377
|
+
if (run?.status === 'completed') {
|
|
378
|
+
return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
379
|
+
}
|
|
380
|
+
let config;
|
|
381
|
+
try {
|
|
382
|
+
config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
386
|
+
}
|
|
387
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
388
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', 'project configuration must be an object', resolved.taskId);
|
|
389
|
+
}
|
|
390
|
+
const projectConfig = config;
|
|
391
|
+
if (!Object.hasOwn(projectConfig, 'prFlow') || projectConfig.prFlow === 'disabled') {
|
|
392
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
393
|
+
}
|
|
394
|
+
if (projectConfig.prFlow !== 'required') {
|
|
395
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', "project configuration 'prFlow' must be 'required' or 'disabled'", resolved.taskId);
|
|
396
|
+
}
|
|
397
|
+
const prNumber = Number(metadata.pr_number);
|
|
398
|
+
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
399
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
400
|
+
}
|
|
401
|
+
if (!run) {
|
|
402
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
403
|
+
}
|
|
404
|
+
if (run.status !== 'running' || run.pendingDelegation !== null) {
|
|
405
|
+
return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
|
|
406
|
+
}
|
|
407
|
+
const capture = options.captureRepository ?? captureRepositorySnapshot;
|
|
408
|
+
let before;
|
|
409
|
+
try {
|
|
410
|
+
before = capture(resolved.repoRoot);
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
414
|
+
}
|
|
415
|
+
if (before.headTree !== before.worktreeTree) {
|
|
416
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
417
|
+
}
|
|
418
|
+
const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
|
|
419
|
+
if (before.head !== lastReviewedCommit) {
|
|
420
|
+
return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
|
|
421
|
+
}
|
|
422
|
+
const inspect = options.inspectPullRequest ?? inspectPlatformPullRequest;
|
|
423
|
+
let inspection;
|
|
424
|
+
try {
|
|
425
|
+
inspection = inspect(resolved.taskId, { cwd: resolved.repoRoot });
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
429
|
+
}
|
|
430
|
+
if (inspection.status === 'blocked') {
|
|
431
|
+
return failed('ORCHESTRATION_PR_INSPECTION_BLOCKED', inspection.error?.message ?? 'pull request inspection is blocked', resolved.taskId);
|
|
432
|
+
}
|
|
433
|
+
if (inspection.status === 'failed') {
|
|
434
|
+
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', inspection.error?.message ?? 'pull request inspection failed', resolved.taskId);
|
|
435
|
+
}
|
|
436
|
+
const inspectedPrNumber = inspection.task?.prNumber ?? inspection.prNumber;
|
|
437
|
+
const prHead = inspection.pullRequest?.head?.sha;
|
|
438
|
+
if (typeof prHead !== 'string') {
|
|
439
|
+
return failed('ORCHESTRATION_PR_INSPECTION_INVALID', 'pull request inspection returned incomplete identity evidence', resolved.taskId);
|
|
440
|
+
}
|
|
441
|
+
if (inspectedPrNumber !== prNumber || prHead !== before.head) {
|
|
442
|
+
return failed('ORCHESTRATION_PR_HEAD_MISMATCH', 'pull request identity does not match the reviewed local HEAD', resolved.taskId);
|
|
443
|
+
}
|
|
444
|
+
let after;
|
|
445
|
+
try {
|
|
446
|
+
after = capture(resolved.repoRoot);
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
450
|
+
}
|
|
451
|
+
if (after.headTree !== after.worktreeTree) {
|
|
452
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
453
|
+
}
|
|
454
|
+
if (after.head !== before.head || after.headTree !== before.headTree) {
|
|
455
|
+
return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
|
|
456
|
+
}
|
|
457
|
+
const observedAt = (options.now ?? (() => new Date().toISOString()))();
|
|
458
|
+
const completionEvidence = {
|
|
459
|
+
kind: 'reviewed-head-clean',
|
|
460
|
+
observedAt,
|
|
461
|
+
head: after.head,
|
|
462
|
+
headTree: after.headTree,
|
|
463
|
+
worktreeTree: after.worktreeTree,
|
|
464
|
+
lastReviewedCommit,
|
|
465
|
+
prNumber,
|
|
466
|
+
prHead
|
|
467
|
+
};
|
|
468
|
+
const completed = withUpdatedRun(run, {
|
|
469
|
+
status: 'completed',
|
|
470
|
+
nextStage: null,
|
|
471
|
+
pause: null,
|
|
472
|
+
completionEvidence
|
|
473
|
+
});
|
|
474
|
+
saveRun(resolved.taskDir, completed);
|
|
475
|
+
return { status: 'completed', changed: true, taskId: resolved.taskId, run: completed, next: null, error: null };
|
|
174
476
|
}
|
|
175
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run
|
|
477
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
176
478
|
}
|
|
177
479
|
function statusOrchestration(taskRef, options = {}) {
|
|
178
480
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
@@ -183,17 +485,18 @@ function statusOrchestration(taskRef, options = {}) {
|
|
|
183
485
|
return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
|
|
184
486
|
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
185
487
|
}
|
|
186
|
-
function
|
|
488
|
+
function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
187
489
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
188
490
|
if (!resolved.ok)
|
|
189
491
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
190
|
-
if (
|
|
191
|
-
|| getAgentClientCapability(input.client, 'orchestration').level === 'unsupported') {
|
|
492
|
+
if (!(options.supportsLifecycleDelegation ?? supportsLifecycleDelegation)(input.client)) {
|
|
192
493
|
return failed('ORCHESTRATION_CLIENT_UNSUPPORTED', `client '${input.client}' does not support lifecycle orchestration`, resolved.taskId);
|
|
193
494
|
}
|
|
194
495
|
const run = readRun(resolved.taskDir);
|
|
195
496
|
if (!run || run.status !== 'running')
|
|
196
497
|
return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
|
|
498
|
+
if (!isV2Policy(run.modelPolicy))
|
|
499
|
+
return failed('ORCHESTRATION_MODEL_EVIDENCE_MISSING', 'running orchestration has no persisted model policy', resolved.taskId);
|
|
197
500
|
if (run.pendingDelegation)
|
|
198
501
|
return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
|
|
199
502
|
const repositoryPending = ['claude-code', 'codex']
|
|
@@ -207,9 +510,22 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
|
207
510
|
if (!routed.next)
|
|
208
511
|
return routed;
|
|
209
512
|
const next = routed.next;
|
|
513
|
+
if (!validModel(input.requestedModel)) {
|
|
514
|
+
return failed('ORCHESTRATION_REQUESTED_MODEL_REQUIRED', 'prepare requires the exact requested model identity', resolved.taskId);
|
|
515
|
+
}
|
|
516
|
+
if (!validModel(input.requestedReasoningEffort)) {
|
|
517
|
+
return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', resolved.taskId);
|
|
518
|
+
}
|
|
519
|
+
const expectedPolicy = run.modelPolicy[next.role];
|
|
520
|
+
if (input.requestedModel !== expectedPolicy.model) {
|
|
521
|
+
return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
|
|
522
|
+
}
|
|
523
|
+
if (input.requestedReasoningEffort !== expectedPolicy.reasoningEffort) {
|
|
524
|
+
return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_MISMATCH', `requested reasoning effort does not match the persisted ${next.role} policy`, resolved.taskId);
|
|
525
|
+
}
|
|
210
526
|
let beforeFingerprint;
|
|
211
527
|
try {
|
|
212
|
-
beforeFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(resolved.repoRoot);
|
|
528
|
+
beforeFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(resolved.repoRoot, resolved.taskId);
|
|
213
529
|
}
|
|
214
530
|
catch (error) {
|
|
215
531
|
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
@@ -222,7 +538,9 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
|
222
538
|
round: next.round,
|
|
223
539
|
artifact: next.artifact,
|
|
224
540
|
client: input.client,
|
|
225
|
-
requestedModel: input.requestedModel
|
|
541
|
+
requestedModel: input.requestedModel,
|
|
542
|
+
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
543
|
+
workspaceSnapshotScope: 'task',
|
|
226
544
|
beforeFingerprint
|
|
227
545
|
}, { id: options.id, now: options.now });
|
|
228
546
|
const updated = withUpdatedRun(run, {
|
|
@@ -235,6 +553,19 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
|
235
553
|
saveRun(resolved.taskDir, updated);
|
|
236
554
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
|
|
237
555
|
}
|
|
556
|
+
function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
557
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
558
|
+
if (!resolved.ok)
|
|
559
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
560
|
+
try {
|
|
561
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.prepare', () => prepareOrchestrationDelegationUnlocked(taskRef, input, options));
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
if (error instanceof TaskExecutionLockError)
|
|
565
|
+
return failed(error.code, error.message, resolved.taskId);
|
|
566
|
+
throw error;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
238
569
|
function matchingDelegations(client, predicate, options) {
|
|
239
570
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
240
571
|
const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
|
|
@@ -285,7 +616,8 @@ function sealMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
|
285
616
|
}
|
|
286
617
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
287
618
|
try {
|
|
288
|
-
const
|
|
619
|
+
const snapshotTaskId = receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null;
|
|
620
|
+
const afterFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(repoRoot, snapshotTaskId);
|
|
289
621
|
const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(repoRoot, receipt.beforeFingerprint, afterFingerprint);
|
|
290
622
|
return sealOrchestrationDelegation(matched.taskId, {
|
|
291
623
|
childId: event.childId,
|
|
@@ -331,6 +663,56 @@ function completeOrchestrationStage(taskRef, event, options = {}) {
|
|
|
331
663
|
saveRun(resolved.taskDir, updated);
|
|
332
664
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
333
665
|
}
|
|
666
|
+
function inspectOrchestrationStage(taskRef, event, options = {}) {
|
|
667
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
668
|
+
if (!resolved.ok)
|
|
669
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
670
|
+
let run;
|
|
671
|
+
try {
|
|
672
|
+
run = readRun(resolved.taskDir);
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
676
|
+
}
|
|
677
|
+
if (!run)
|
|
678
|
+
return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
|
|
679
|
+
const receipt = run.pendingDelegation;
|
|
680
|
+
if (!receipt
|
|
681
|
+
|| receipt.status !== 'activated'
|
|
682
|
+
|| receipt.stage !== event.stage
|
|
683
|
+
|| receipt.round !== event.round
|
|
684
|
+
|| receipt.artifact !== event.artifact
|
|
685
|
+
|| receipt.role !== event.role) {
|
|
686
|
+
return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'active run does not have one matching activated delegation', resolved.taskId);
|
|
687
|
+
}
|
|
688
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
689
|
+
}
|
|
690
|
+
function planOrchestrationStageCompletion(taskRef, event, options = {}) {
|
|
691
|
+
const inspected = inspectOrchestrationStage(taskRef, event, options);
|
|
692
|
+
if (inspected.status === 'failed' || !inspected.run?.pendingDelegation) {
|
|
693
|
+
return { result: inspected, plan: null };
|
|
694
|
+
}
|
|
695
|
+
const receipt = inspected.run.pendingDelegation;
|
|
696
|
+
const completed = completeDelegationStage(receipt, event);
|
|
697
|
+
if (!completed.ok) {
|
|
698
|
+
return { result: failed(completed.code, completed.message, inspected.taskId), plan: null };
|
|
699
|
+
}
|
|
700
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
701
|
+
if (!resolved.ok)
|
|
702
|
+
return { result: failed(resolved.code, resolved.message, resolved.taskId), plan: null };
|
|
703
|
+
const updatedRun = withUpdatedRun(inspected.run, { pendingDelegation: completed.receipt });
|
|
704
|
+
return {
|
|
705
|
+
result: { status: 'running', changed: true, taskId: resolved.taskId, run: updatedRun, next: null, error: null },
|
|
706
|
+
plan: {
|
|
707
|
+
taskId: resolved.taskId,
|
|
708
|
+
taskDir: resolved.taskDir,
|
|
709
|
+
updatedRun
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function commitOrchestrationStageCompletion(plan) {
|
|
714
|
+
saveRun(plan.taskDir, plan.updatedRun);
|
|
715
|
+
}
|
|
334
716
|
function completeCommitOrchestrationStage(taskRef, agent, options = {}) {
|
|
335
717
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
336
718
|
if (!resolved.ok)
|
|
@@ -349,24 +731,6 @@ function completeCommitOrchestrationStage(taskRef, agent, options = {}) {
|
|
|
349
731
|
agent
|
|
350
732
|
}, options);
|
|
351
733
|
}
|
|
352
|
-
function validateOrchestrationStage(taskRef, event, options = {}) {
|
|
353
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
354
|
-
if (!resolved.ok)
|
|
355
|
-
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
356
|
-
const run = readRun(resolved.taskDir);
|
|
357
|
-
if (!run)
|
|
358
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
|
|
359
|
-
const receipt = run.pendingDelegation;
|
|
360
|
-
if (!receipt
|
|
361
|
-
|| receipt.status !== 'activated'
|
|
362
|
-
|| receipt.stage !== event.stage
|
|
363
|
-
|| receipt.round !== event.round
|
|
364
|
-
|| receipt.artifact !== event.artifact
|
|
365
|
-
|| receipt.role !== event.role) {
|
|
366
|
-
return pauseOrchestration(taskRef, 'ORCHESTRATION_PROVENANCE_MISMATCH', 'active run does not have one matching activated delegation', true, options);
|
|
367
|
-
}
|
|
368
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
369
|
-
}
|
|
370
734
|
function sealOrchestrationDelegation(taskRef, event, options = {}) {
|
|
371
735
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
372
736
|
if (!resolved.ok)
|
|
@@ -416,5 +780,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
|
|
|
416
780
|
saveRun(resolved.taskDir, updated);
|
|
417
781
|
return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
418
782
|
}
|
|
419
|
-
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginOrResumeOrchestration, completeCommitOrchestrationStage, completeOrchestrationStage, orchestrationPath, pauseOrchestration, prepareOrchestrationDelegation, readRun, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration
|
|
783
|
+
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeCommitOrchestrationStage, completeOrchestrationStage, inspectOrchestrationStage, orchestrationPath, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration };
|
|
420
784
|
//# sourceMappingURL=orchestration.js.map
|
|
@@ -6,7 +6,8 @@ import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecyc
|
|
|
6
6
|
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
7
7
|
import { finalizeReviewSummaryContent } from './review-artifacts.js';
|
|
8
8
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
|
-
import {
|
|
9
|
+
import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
10
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
10
11
|
const STAGES = {
|
|
11
12
|
analysis: { family: 'review-analysis', action: 'Review Analysis' },
|
|
12
13
|
plan: { family: 'review-plan', action: 'Review Plan' },
|
|
@@ -48,7 +49,7 @@ function cleanupTemp(fileSystem, tempPath) {
|
|
|
48
49
|
// Best-effort cleanup preserves the primary failure.
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
|
-
function
|
|
52
|
+
function finalizeReviewSummaryUnlocked(request, options = {}) {
|
|
52
53
|
const stage = request.stage;
|
|
53
54
|
const spec = STAGES[stage];
|
|
54
55
|
if (!spec)
|
|
@@ -86,14 +87,18 @@ function finalizeReviewSummary(request, options = {}) {
|
|
|
86
87
|
if (!openReviewRound(taskContent, spec.action, parsedArtifact.round)) {
|
|
87
88
|
return failed(request, 'REVIEW_ARTIFACT_IDENTITY_INVALID', `${request.artifact} does not have one matching open started review event`, resolved.taskId);
|
|
88
89
|
}
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
const execution = validateLifecycleExecution(request.taskRef, {
|
|
91
|
+
mode: request.orchestrated ? 'orchestrated' : 'standalone',
|
|
92
|
+
identity: {
|
|
93
|
+
stage: spec.family,
|
|
94
|
+
round: parsedArtifact.round,
|
|
95
|
+
artifact: request.artifact,
|
|
96
|
+
role: 'reviewer'
|
|
97
|
+
},
|
|
98
|
+
dryRun: request.dryRun
|
|
94
99
|
}, { repoRoot: options.repoRoot });
|
|
95
|
-
if (
|
|
96
|
-
return failed(request, 'REVIEW_PROVENANCE_INVALID',
|
|
100
|
+
if (!execution.ok) {
|
|
101
|
+
return failed(request, 'REVIEW_PROVENANCE_INVALID', `${execution.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH'}: ${execution.error?.message ?? 'orchestration provenance validation failed'}`, resolved.taskId);
|
|
97
102
|
}
|
|
98
103
|
let rows;
|
|
99
104
|
try {
|
|
@@ -162,5 +167,20 @@ function finalizeReviewSummary(request, options = {}) {
|
|
|
162
167
|
error: null
|
|
163
168
|
};
|
|
164
169
|
}
|
|
170
|
+
function finalizeReviewSummary(request, options = {}) {
|
|
171
|
+
if (request.dryRun)
|
|
172
|
+
return finalizeReviewSummaryUnlocked(request, options);
|
|
173
|
+
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
174
|
+
if (!resolved.ok)
|
|
175
|
+
return finalizeReviewSummaryUnlocked(request, options);
|
|
176
|
+
try {
|
|
177
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-review.finalize-summary', () => finalizeReviewSummaryUnlocked(request, options));
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
181
|
+
throw error;
|
|
182
|
+
return failed(request, 'REVIEW_PROVENANCE_INVALID', `${error.code}: ${error.message}`, resolved.taskId);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
165
185
|
export { finalizeReviewSummary };
|
|
166
186
|
//# sourceMappingURL=review-finalization.js.map
|