@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
|
@@ -16,24 +16,75 @@ import {
|
|
|
16
16
|
sealDelegation
|
|
17
17
|
} from './delegation-receipts.ts';
|
|
18
18
|
import type { DelegationReceipt, DelegationRole, DelegationStage } from './delegation-receipts.ts';
|
|
19
|
-
import {
|
|
20
|
-
import
|
|
21
|
-
import {
|
|
19
|
+
import { normalizeAgentClients } from '../agent-clients/config.ts';
|
|
20
|
+
import { inspectPlatformPullRequest } from '../platform/pull-requests.ts';
|
|
21
|
+
import {
|
|
22
|
+
getAgentClientCapability,
|
|
23
|
+
getAgentClientDelegationEvidence,
|
|
24
|
+
getAgentClientModelSelection
|
|
25
|
+
} from '../agent-clients/registry.ts';
|
|
26
|
+
import { isAgentClientId } from '../agent-clients/types.ts';
|
|
27
|
+
import type {
|
|
28
|
+
AgentClientId,
|
|
29
|
+
OrchestrationModelPolicy,
|
|
30
|
+
OrchestrationRolePolicy
|
|
31
|
+
} from '../agent-clients/types.ts';
|
|
32
|
+
import {
|
|
33
|
+
captureRepositorySnapshot,
|
|
34
|
+
captureWorkspaceSnapshot,
|
|
35
|
+
diffWorkspaceSnapshots
|
|
36
|
+
} from './workspace-snapshot.ts';
|
|
37
|
+
import type { RepositorySnapshot } from './workspace-snapshot.ts';
|
|
38
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
22
39
|
|
|
23
40
|
type OrchestrationStatus = 'running' | 'paused' | 'completed';
|
|
41
|
+
type LegacyOrchestrationModelPolicy = Readonly<{
|
|
42
|
+
executor: string;
|
|
43
|
+
reviewer: string;
|
|
44
|
+
}>;
|
|
45
|
+
type ModelPolicySource = Readonly<{
|
|
46
|
+
kind: 'explicit' | 'project-config';
|
|
47
|
+
client: AgentClientId;
|
|
48
|
+
resolvedAt: string;
|
|
49
|
+
}>;
|
|
50
|
+
type OrchestrationRecovery = Readonly<{
|
|
51
|
+
code: 'MODEL_POLICY_SUPPLEMENTED';
|
|
52
|
+
recoveredAt: string;
|
|
53
|
+
previousSchemaVersion: 1;
|
|
54
|
+
previousStatus: OrchestrationStatus;
|
|
55
|
+
previousPause: Readonly<{ code: string; message: string; recoverable: boolean }> | null;
|
|
56
|
+
policySource: ModelPolicySource;
|
|
57
|
+
receiptCount: 0;
|
|
58
|
+
pendingDelegation: false;
|
|
59
|
+
resultingStatus: OrchestrationStatus;
|
|
60
|
+
}>;
|
|
61
|
+
type CleanCompletionEvidence = Readonly<{
|
|
62
|
+
kind: 'reviewed-head-clean';
|
|
63
|
+
observedAt: string;
|
|
64
|
+
head: string;
|
|
65
|
+
headTree: string;
|
|
66
|
+
worktreeTree: string;
|
|
67
|
+
lastReviewedCommit: string;
|
|
68
|
+
prNumber: number;
|
|
69
|
+
prHead: string;
|
|
70
|
+
}>;
|
|
24
71
|
type OrchestrationRun = Readonly<{
|
|
25
|
-
schemaVersion: 1;
|
|
72
|
+
schemaVersion: 1 | 2;
|
|
26
73
|
taskId: string;
|
|
27
74
|
runId: string;
|
|
28
75
|
status: OrchestrationStatus;
|
|
29
76
|
nextStage: DelegationStage | null;
|
|
30
77
|
stepCount: number;
|
|
31
78
|
maxSteps: number;
|
|
79
|
+
modelPolicy?: OrchestrationModelPolicy | LegacyOrchestrationModelPolicy;
|
|
80
|
+
modelPolicySource?: ModelPolicySource;
|
|
81
|
+
recoveryHistory?: readonly OrchestrationRecovery[];
|
|
32
82
|
baseline: string;
|
|
33
83
|
pendingDelegation: DelegationReceipt | null;
|
|
34
84
|
receipts: readonly DelegationReceipt[];
|
|
35
85
|
pause: Readonly<{ code: string; message: string; recoverable: boolean }> | null;
|
|
36
86
|
commitAuthorization: Readonly<{ issuedAt: string | null; consumedAt: string | null }>;
|
|
87
|
+
completionEvidence?: CleanCompletionEvidence | null;
|
|
37
88
|
createdAt: string;
|
|
38
89
|
updatedAt: string;
|
|
39
90
|
}>;
|
|
@@ -43,6 +94,8 @@ type OrchestrationNext = Readonly<{
|
|
|
43
94
|
stage: DelegationStage;
|
|
44
95
|
round: number;
|
|
45
96
|
artifact: string;
|
|
97
|
+
requestedModel: string | null;
|
|
98
|
+
requestedReasoningEffort: string | null;
|
|
46
99
|
}>;
|
|
47
100
|
type OrchestrationResult = Readonly<{
|
|
48
101
|
status: OrchestrationStatus | 'failed';
|
|
@@ -50,17 +103,56 @@ type OrchestrationResult = Readonly<{
|
|
|
50
103
|
taskId: string | null;
|
|
51
104
|
run: OrchestrationRun | null;
|
|
52
105
|
next: OrchestrationNext | null;
|
|
53
|
-
error: Readonly<{
|
|
106
|
+
error: Readonly<{
|
|
107
|
+
code: string;
|
|
108
|
+
message: string;
|
|
109
|
+
client?: AgentClientId;
|
|
110
|
+
missingFields?: readonly string[];
|
|
111
|
+
modelSelectionContext?: ReturnType<typeof getAgentClientModelSelection>;
|
|
112
|
+
}> | null;
|
|
113
|
+
}>;
|
|
114
|
+
type OrchestrationStageIdentity = Readonly<{
|
|
115
|
+
stage: DelegationStage;
|
|
116
|
+
round: number;
|
|
117
|
+
artifact: string;
|
|
118
|
+
role: DelegationRole;
|
|
119
|
+
}>;
|
|
120
|
+
type OrchestrationStageCompletion = Readonly<{
|
|
121
|
+
taskId: string;
|
|
122
|
+
taskDir: string;
|
|
123
|
+
updatedRun: OrchestrationRun;
|
|
124
|
+
}>;
|
|
125
|
+
type OrchestrationCompletionPlanResult = Readonly<{
|
|
126
|
+
result: OrchestrationResult;
|
|
127
|
+
plan: OrchestrationStageCompletion | null;
|
|
54
128
|
}>;
|
|
55
129
|
type OrchestrationOptions = {
|
|
56
130
|
repoRoot?: string;
|
|
57
131
|
id?: () => string;
|
|
58
132
|
now?: () => string;
|
|
59
133
|
maxSteps?: number;
|
|
60
|
-
|
|
134
|
+
client?: AgentClientId;
|
|
135
|
+
modelPolicy?: OrchestrationModelPolicy;
|
|
136
|
+
captureWorkspace?: (repoRoot: string, taskId: string | null) => string;
|
|
137
|
+
captureRepository?: (repoRoot: string) => RepositorySnapshot;
|
|
138
|
+
inspectPullRequest?: (taskId: string, options: { cwd: string }) => Readonly<{
|
|
139
|
+
status: string;
|
|
140
|
+
task?: Readonly<{ prNumber?: number | null }>;
|
|
141
|
+
prNumber?: number | null;
|
|
142
|
+
pullRequest?: Readonly<{ head?: Readonly<{ sha?: string }> }> | null;
|
|
143
|
+
error?: Readonly<{ message?: string }> | null;
|
|
144
|
+
}>;
|
|
61
145
|
diffWorkspace?: (repoRoot: string, before: string, after: string) => string[];
|
|
146
|
+
supportsLifecycleDelegation?: (client: AgentClientId) => boolean;
|
|
62
147
|
};
|
|
63
148
|
|
|
149
|
+
function supportsLifecycleDelegation(client: AgentClientId): boolean {
|
|
150
|
+
return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
|
|
151
|
+
&& getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
|
|
152
|
+
&& getAgentClientDelegationEvidence(client).actualModel !== 'unavailable'
|
|
153
|
+
&& getAgentClientDelegationEvidence(client).actualReasoningEffort !== 'unavailable';
|
|
154
|
+
}
|
|
155
|
+
|
|
64
156
|
function orchestrationPath(taskDir: string): string {
|
|
65
157
|
return path.join(taskDir, 'orchestration.json');
|
|
66
158
|
}
|
|
@@ -68,7 +160,21 @@ function orchestrationPath(taskDir: string): string {
|
|
|
68
160
|
function readRun(taskDir: string): OrchestrationRun | null {
|
|
69
161
|
const file = orchestrationPath(taskDir);
|
|
70
162
|
if (!fs.existsSync(file)) return null;
|
|
71
|
-
|
|
163
|
+
const run = JSON.parse(fs.readFileSync(file, 'utf8')) as OrchestrationRun;
|
|
164
|
+
if (run.schemaVersion !== 2 || !isV2Policy(run.modelPolicy)) return run;
|
|
165
|
+
return {
|
|
166
|
+
...run,
|
|
167
|
+
modelPolicy: {
|
|
168
|
+
executor: {
|
|
169
|
+
model: run.modelPolicy.executor.model,
|
|
170
|
+
reasoningEffort: run.modelPolicy.executor.reasoningEffort
|
|
171
|
+
},
|
|
172
|
+
reviewer: {
|
|
173
|
+
model: run.modelPolicy.reviewer.model,
|
|
174
|
+
reasoningEffort: run.modelPolicy.reviewer.reasoningEffort
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
72
178
|
}
|
|
73
179
|
|
|
74
180
|
function atomicWrite(file: string, value: unknown): void {
|
|
@@ -85,36 +191,224 @@ function withUpdatedRun(run: OrchestrationRun, updates: Partial<OrchestrationRun
|
|
|
85
191
|
return Object.freeze({ ...run, ...updates, updatedAt: new Date().toISOString() });
|
|
86
192
|
}
|
|
87
193
|
|
|
88
|
-
function failed(
|
|
89
|
-
|
|
194
|
+
function failed(
|
|
195
|
+
code: string,
|
|
196
|
+
message: string,
|
|
197
|
+
taskId: string | null = null,
|
|
198
|
+
details: Omit<NonNullable<OrchestrationResult['error']>, 'code' | 'message'> = {}
|
|
199
|
+
): OrchestrationResult {
|
|
200
|
+
return { status: 'failed', changed: false, taskId, run: null, next: null, error: { code, message, ...details } };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function validModel(value: unknown): value is string {
|
|
204
|
+
return typeof value === 'string' && value.length > 0 && value.trim() === value;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function validateModelPolicy(policy: OrchestrationModelPolicy | undefined): Readonly<{ code: string; message: string }> | null {
|
|
208
|
+
if (
|
|
209
|
+
!policy
|
|
210
|
+
|| !validModel(policy.executor?.model)
|
|
211
|
+
|| !validModel(policy.executor?.reasoningEffort)
|
|
212
|
+
|| !validModel(policy.reviewer?.model)
|
|
213
|
+
|| !validModel(policy.reviewer?.reasoningEffort)
|
|
214
|
+
) {
|
|
215
|
+
return { code: 'ORCHESTRATION_MODEL_POLICY_REQUIRED', message: 'executor and reviewer model and reasoning effort are required' };
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function sameModelPolicy(left: OrchestrationModelPolicy, right: OrchestrationModelPolicy): boolean {
|
|
221
|
+
return left.executor.model === right.executor.model
|
|
222
|
+
&& left.executor.reasoningEffort === right.executor.reasoningEffort
|
|
223
|
+
&& left.reviewer.model === right.reviewer.model
|
|
224
|
+
&& left.reviewer.reasoningEffort === right.reviewer.reasoningEffort;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function isV2Policy(policy: OrchestrationRun['modelPolicy']): policy is OrchestrationModelPolicy {
|
|
228
|
+
return typeof policy?.executor === 'object' && typeof policy?.reviewer === 'object';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function rolePolicy(run: OrchestrationRun, role: DelegationRole): OrchestrationRolePolicy | null {
|
|
232
|
+
return isV2Policy(run.modelPolicy) ? run.modelPolicy[role] : null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function resolveProjectPolicy(repoRoot: string, client: AgentClientId): OrchestrationModelPolicy | undefined {
|
|
236
|
+
const configPath = path.join(repoRoot, '.agents', '.airc.json');
|
|
237
|
+
if (!fs.existsSync(configPath)) return undefined;
|
|
238
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8')) as Record<string, unknown>;
|
|
239
|
+
return normalizeAgentClients(raw).state[client].orchestration;
|
|
90
240
|
}
|
|
91
241
|
|
|
92
242
|
function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
|
|
93
243
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
94
244
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
95
|
-
|
|
245
|
+
if (!isAgentClientId(options.client)) {
|
|
246
|
+
return failed('ORCHESTRATION_PAYLOAD_INVALID', 'begin-or-resume requires a known client', resolved.taskId);
|
|
247
|
+
}
|
|
248
|
+
let existing: OrchestrationRun | null;
|
|
249
|
+
try {
|
|
250
|
+
existing = readRun(resolved.taskDir);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
253
|
+
}
|
|
96
254
|
if (existing) {
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
255
|
+
if (
|
|
256
|
+
![1, 2].includes(existing.schemaVersion)
|
|
257
|
+
|| !Array.isArray(existing.receipts)
|
|
258
|
+
|| !Object.prototype.hasOwnProperty.call(existing, 'pendingDelegation')
|
|
259
|
+
) {
|
|
260
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'orchestration state has an invalid schema', resolved.taskId);
|
|
261
|
+
}
|
|
262
|
+
if (existing.status === 'completed' && existing.schemaVersion === 1) {
|
|
263
|
+
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
264
|
+
}
|
|
265
|
+
if (existing.schemaVersion === 2) {
|
|
266
|
+
const policyError = validateModelPolicy(isV2Policy(existing.modelPolicy) ? existing.modelPolicy : undefined);
|
|
267
|
+
if (
|
|
268
|
+
policyError
|
|
269
|
+
|| !existing.modelPolicySource
|
|
270
|
+
|| !isAgentClientId(existing.modelPolicySource.client)
|
|
271
|
+
|| !Array.isArray(existing.recoveryHistory)
|
|
272
|
+
) {
|
|
273
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'schemaVersion 2 orchestration state is incomplete', resolved.taskId);
|
|
274
|
+
}
|
|
275
|
+
if (existing.status === 'completed') {
|
|
276
|
+
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
277
|
+
}
|
|
278
|
+
if (existing.modelPolicySource.client !== options.client) {
|
|
279
|
+
return failed('ORCHESTRATION_CLIENT_MISMATCH', 'provided client does not match the persisted run policy source', resolved.taskId);
|
|
280
|
+
}
|
|
281
|
+
if (options.modelPolicy) {
|
|
282
|
+
const suppliedError = validateModelPolicy(options.modelPolicy);
|
|
283
|
+
if (suppliedError) return failed(suppliedError.code, suppliedError.message, resolved.taskId);
|
|
284
|
+
if (!sameModelPolicy(existing.modelPolicy as OrchestrationModelPolicy, options.modelPolicy)) {
|
|
285
|
+
return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
|
|
289
|
+
const resumed = withUpdatedRun(existing, { status: 'running', pause: null });
|
|
290
|
+
saveRun(resolved.taskDir, resumed);
|
|
291
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
292
|
+
}
|
|
293
|
+
return { status: existing.status, changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let policy: OrchestrationModelPolicy | undefined = options.modelPolicy;
|
|
297
|
+
let sourceKind: ModelPolicySource['kind'] = 'explicit';
|
|
298
|
+
if (!policy) {
|
|
299
|
+
try {
|
|
300
|
+
policy = resolveProjectPolicy(resolved.repoRoot, options.client);
|
|
301
|
+
sourceKind = 'project-config';
|
|
302
|
+
} catch (error) {
|
|
303
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const policyError = validateModelPolicy(policy);
|
|
307
|
+
if (policyError) {
|
|
308
|
+
return failed(policyError.code, policyError.message, resolved.taskId, {
|
|
309
|
+
client: options.client,
|
|
310
|
+
missingFields: ['executor.model', 'executor.reasoningEffort', 'reviewer.model', 'reviewer.reasoningEffort'],
|
|
311
|
+
modelSelectionContext: getAgentClientModelSelection(options.client)
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (existing.pendingDelegation !== null) {
|
|
315
|
+
const alreadyPaused = existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_DELEGATION_BUSY';
|
|
316
|
+
if (alreadyPaused) return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
317
|
+
const paused = withUpdatedRun(existing, {
|
|
318
|
+
status: 'paused',
|
|
319
|
+
pause: { code: 'ORCHESTRATION_DELEGATION_BUSY', message: 'legacy run has a pending delegation', recoverable: false }
|
|
320
|
+
});
|
|
321
|
+
saveRun(resolved.taskDir, paused);
|
|
322
|
+
return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
|
|
323
|
+
}
|
|
324
|
+
if (existing.receipts.length > 0) {
|
|
325
|
+
const alreadyPaused = existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED';
|
|
326
|
+
if (alreadyPaused) return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
327
|
+
const paused = withUpdatedRun(existing, {
|
|
328
|
+
status: 'paused',
|
|
329
|
+
pause: {
|
|
330
|
+
code: 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED',
|
|
331
|
+
message: 'legacy receipts do not contain actual reasoning-effort evidence',
|
|
332
|
+
recoverable: false
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
saveRun(resolved.taskDir, paused);
|
|
336
|
+
return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
|
|
337
|
+
}
|
|
338
|
+
if (
|
|
339
|
+
existing.modelPolicy
|
|
340
|
+
&& typeof existing.modelPolicy.executor === 'string'
|
|
341
|
+
&& (
|
|
342
|
+
existing.modelPolicy.executor !== policy!.executor.model
|
|
343
|
+
|| existing.modelPolicy.reviewer !== policy!.reviewer.model
|
|
344
|
+
)
|
|
345
|
+
) {
|
|
346
|
+
return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
|
|
347
|
+
}
|
|
348
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
349
|
+
const source: ModelPolicySource = { kind: sourceKind, client: options.client, resolvedAt: now };
|
|
350
|
+
const clearsModelPause = existing.pause?.code === 'ORCHESTRATION_MODEL_EVIDENCE_MISSING';
|
|
351
|
+
const resultingStatus = clearsModelPause ? 'running' : existing.status;
|
|
352
|
+
const recovery: OrchestrationRecovery = {
|
|
353
|
+
code: 'MODEL_POLICY_SUPPLEMENTED',
|
|
354
|
+
recoveredAt: now,
|
|
355
|
+
previousSchemaVersion: 1,
|
|
356
|
+
previousStatus: existing.status,
|
|
357
|
+
previousPause: existing.pause,
|
|
358
|
+
policySource: source,
|
|
359
|
+
receiptCount: 0,
|
|
360
|
+
pendingDelegation: false,
|
|
361
|
+
resultingStatus
|
|
362
|
+
};
|
|
363
|
+
const recovered: OrchestrationRun = Object.freeze({
|
|
364
|
+
...existing,
|
|
365
|
+
schemaVersion: 2,
|
|
366
|
+
status: resultingStatus,
|
|
367
|
+
modelPolicy: policy!,
|
|
368
|
+
modelPolicySource: source,
|
|
369
|
+
recoveryHistory: Object.freeze([recovery]),
|
|
370
|
+
pause: clearsModelPause ? null : existing.pause,
|
|
371
|
+
updatedAt: now
|
|
372
|
+
});
|
|
373
|
+
saveRun(resolved.taskDir, recovered);
|
|
374
|
+
return { status: resultingStatus, changed: true, taskId: resolved.taskId, run: recovered, next: null, error: null };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
let policy = options.modelPolicy;
|
|
378
|
+
let sourceKind: ModelPolicySource['kind'] = 'explicit';
|
|
379
|
+
if (!policy) {
|
|
380
|
+
try {
|
|
381
|
+
policy = resolveProjectPolicy(resolved.repoRoot, options.client);
|
|
382
|
+
sourceKind = 'project-config';
|
|
383
|
+
} catch (error) {
|
|
384
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
101
385
|
}
|
|
102
|
-
return { status: existing.status, changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
103
386
|
}
|
|
387
|
+
const policyError = validateModelPolicy(policy);
|
|
388
|
+
if (policyError) return failed(policyError.code, policyError.message, resolved.taskId, {
|
|
389
|
+
client: options.client,
|
|
390
|
+
missingFields: ['executor.model', 'executor.reasoningEffort', 'reviewer.model', 'reviewer.reasoningEffort'],
|
|
391
|
+
modelSelectionContext: getAgentClientModelSelection(options.client)
|
|
392
|
+
});
|
|
104
393
|
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
394
|
+
const source: ModelPolicySource = { kind: sourceKind, client: options.client, resolvedAt: now };
|
|
105
395
|
const run: OrchestrationRun = {
|
|
106
|
-
schemaVersion:
|
|
396
|
+
schemaVersion: 2,
|
|
107
397
|
taskId: resolved.taskId,
|
|
108
398
|
runId: (options.id ?? randomUUID)(),
|
|
109
399
|
status: 'running',
|
|
110
400
|
nextStage: null,
|
|
111
401
|
stepCount: 0,
|
|
112
402
|
maxSteps: options.maxSteps ?? 24,
|
|
403
|
+
modelPolicy: policy!,
|
|
404
|
+
modelPolicySource: source,
|
|
405
|
+
recoveryHistory: [],
|
|
113
406
|
baseline: '',
|
|
114
407
|
pendingDelegation: null,
|
|
115
408
|
receipts: [],
|
|
116
409
|
pause: null,
|
|
117
410
|
commitAuthorization: { issuedAt: null, consumedAt: null },
|
|
411
|
+
completionEvidence: null,
|
|
118
412
|
createdAt: now,
|
|
119
413
|
updatedAt: now
|
|
120
414
|
};
|
|
@@ -150,7 +444,7 @@ function latestReviewApproved(taskDir: string, family: 'review-analysis' | 'revi
|
|
|
150
444
|
return parsed.ok && parsed.summary.verdict === 'Approved' && parsed.summary.counts !== null;
|
|
151
445
|
}
|
|
152
446
|
|
|
153
|
-
function routeFromFacts(taskDir: string): OrchestrationNext | null {
|
|
447
|
+
function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedModel' | 'requestedReasoningEffort'> | null {
|
|
154
448
|
const analysisRound = highestRound(taskDir, 'analysis');
|
|
155
449
|
const analysisReviewRound = highestRound(taskDir, 'review-analysis');
|
|
156
450
|
const planRound = highestRound(taskDir, 'plan');
|
|
@@ -205,13 +499,21 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
205
499
|
} catch (error) {
|
|
206
500
|
return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
|
|
207
501
|
}
|
|
502
|
+
let metadata: ReturnType<typeof parseTypedTaskFrontmatter>;
|
|
208
503
|
try {
|
|
209
|
-
parseTypedTaskFrontmatter(content);
|
|
504
|
+
metadata = parseTypedTaskFrontmatter(content);
|
|
210
505
|
} catch (error) {
|
|
211
506
|
return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
212
507
|
}
|
|
213
|
-
const
|
|
214
|
-
if (!
|
|
508
|
+
const routed = routeFromFacts(resolved.taskDir);
|
|
509
|
+
if (!routed) return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
|
|
510
|
+
const run = readRun(resolved.taskDir);
|
|
511
|
+
const policy = run ? rolePolicy(run, routed.role) : null;
|
|
512
|
+
const next: OrchestrationNext = {
|
|
513
|
+
...routed,
|
|
514
|
+
requestedModel: policy?.model ?? null,
|
|
515
|
+
requestedReasoningEffort: policy?.reasoningEffort ?? null
|
|
516
|
+
};
|
|
215
517
|
if (next.stage === 'commit') {
|
|
216
518
|
const reviewRound = highestRound(resolved.taskDir, 'review-code');
|
|
217
519
|
const review = parseReviewSummary(fs.readFileSync(
|
|
@@ -221,17 +523,115 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
221
523
|
if (!review.ok || review.summary.manualValidation === null) {
|
|
222
524
|
return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
|
|
223
525
|
}
|
|
224
|
-
if (review.summary.manualValidation > 0) {
|
|
225
|
-
return failed('ORCHESTRATION_MANUAL_VALIDATION_PENDING', 'manual validation must be completed before commit', resolved.taskId);
|
|
226
|
-
}
|
|
227
526
|
const rows = parseLedger(content);
|
|
228
527
|
const ledgerError = validateLedgerRows(rows);
|
|
229
528
|
if (ledgerError) return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
|
|
230
529
|
if (!summarizeLedgerStage(rows, 'code').canAdvance) {
|
|
231
530
|
return failed('ORCHESTRATION_LEDGER_BLOCKED', 'code review ledger has unresolved findings or human decisions', resolved.taskId);
|
|
232
531
|
}
|
|
532
|
+
if (run?.status === 'completed') {
|
|
533
|
+
return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
let config: unknown;
|
|
537
|
+
try {
|
|
538
|
+
config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
|
|
539
|
+
} catch (error) {
|
|
540
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
541
|
+
}
|
|
542
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
543
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', 'project configuration must be an object', resolved.taskId);
|
|
544
|
+
}
|
|
545
|
+
const projectConfig = config as Record<string, unknown>;
|
|
546
|
+
if (!Object.hasOwn(projectConfig, 'prFlow') || projectConfig.prFlow === 'disabled') {
|
|
547
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
548
|
+
}
|
|
549
|
+
if (projectConfig.prFlow !== 'required') {
|
|
550
|
+
return failed('ORCHESTRATION_CONFIG_INVALID', "project configuration 'prFlow' must be 'required' or 'disabled'", resolved.taskId);
|
|
551
|
+
}
|
|
552
|
+
const prNumber = Number(metadata.pr_number);
|
|
553
|
+
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
554
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
555
|
+
}
|
|
556
|
+
if (!run) {
|
|
557
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
558
|
+
}
|
|
559
|
+
if (run.status !== 'running' || run.pendingDelegation !== null) {
|
|
560
|
+
return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const capture = options.captureRepository ?? captureRepositorySnapshot;
|
|
564
|
+
let before: RepositorySnapshot;
|
|
565
|
+
try {
|
|
566
|
+
before = capture(resolved.repoRoot);
|
|
567
|
+
} catch (error) {
|
|
568
|
+
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
569
|
+
}
|
|
570
|
+
if (before.headTree !== before.worktreeTree) {
|
|
571
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
572
|
+
}
|
|
573
|
+
const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
|
|
574
|
+
if (before.head !== lastReviewedCommit) {
|
|
575
|
+
return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const inspect: NonNullable<OrchestrationOptions['inspectPullRequest']> =
|
|
579
|
+
options.inspectPullRequest ?? inspectPlatformPullRequest;
|
|
580
|
+
let inspection: ReturnType<NonNullable<OrchestrationOptions['inspectPullRequest']>>;
|
|
581
|
+
try {
|
|
582
|
+
inspection = inspect(resolved.taskId, { cwd: resolved.repoRoot });
|
|
583
|
+
} catch (error) {
|
|
584
|
+
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
585
|
+
}
|
|
586
|
+
if (inspection.status === 'blocked') {
|
|
587
|
+
return failed('ORCHESTRATION_PR_INSPECTION_BLOCKED', inspection.error?.message ?? 'pull request inspection is blocked', resolved.taskId);
|
|
588
|
+
}
|
|
589
|
+
if (inspection.status === 'failed') {
|
|
590
|
+
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', inspection.error?.message ?? 'pull request inspection failed', resolved.taskId);
|
|
591
|
+
}
|
|
592
|
+
const inspectedPrNumber = inspection.task?.prNumber ?? inspection.prNumber;
|
|
593
|
+
const prHead = inspection.pullRequest?.head?.sha;
|
|
594
|
+
if (typeof prHead !== 'string') {
|
|
595
|
+
return failed('ORCHESTRATION_PR_INSPECTION_INVALID', 'pull request inspection returned incomplete identity evidence', resolved.taskId);
|
|
596
|
+
}
|
|
597
|
+
if (inspectedPrNumber !== prNumber || prHead !== before.head) {
|
|
598
|
+
return failed('ORCHESTRATION_PR_HEAD_MISMATCH', 'pull request identity does not match the reviewed local HEAD', resolved.taskId);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
let after: RepositorySnapshot;
|
|
602
|
+
try {
|
|
603
|
+
after = capture(resolved.repoRoot);
|
|
604
|
+
} catch (error) {
|
|
605
|
+
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
606
|
+
}
|
|
607
|
+
if (after.headTree !== after.worktreeTree) {
|
|
608
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
609
|
+
}
|
|
610
|
+
if (after.head !== before.head || after.headTree !== before.headTree) {
|
|
611
|
+
return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const observedAt = (options.now ?? (() => new Date().toISOString()))();
|
|
615
|
+
const completionEvidence: CleanCompletionEvidence = {
|
|
616
|
+
kind: 'reviewed-head-clean',
|
|
617
|
+
observedAt,
|
|
618
|
+
head: after.head,
|
|
619
|
+
headTree: after.headTree,
|
|
620
|
+
worktreeTree: after.worktreeTree,
|
|
621
|
+
lastReviewedCommit,
|
|
622
|
+
prNumber,
|
|
623
|
+
prHead
|
|
624
|
+
};
|
|
625
|
+
const completed = withUpdatedRun(run, {
|
|
626
|
+
status: 'completed',
|
|
627
|
+
nextStage: null,
|
|
628
|
+
pause: null,
|
|
629
|
+
completionEvidence
|
|
630
|
+
});
|
|
631
|
+
saveRun(resolved.taskDir, completed);
|
|
632
|
+
return { status: 'completed', changed: true, taskId: resolved.taskId, run: completed, next: null, error: null };
|
|
233
633
|
}
|
|
234
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run
|
|
634
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
235
635
|
}
|
|
236
636
|
|
|
237
637
|
function statusOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
|
|
@@ -242,21 +642,19 @@ function statusOrchestration(taskRef: string, options: OrchestrationOptions = {}
|
|
|
242
642
|
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
243
643
|
}
|
|
244
644
|
|
|
245
|
-
function
|
|
645
|
+
function prepareOrchestrationDelegationUnlocked(
|
|
246
646
|
taskRef: string,
|
|
247
|
-
input: Readonly<{ client: AgentClientId; requestedModel?: string }>,
|
|
647
|
+
input: Readonly<{ client: AgentClientId; requestedModel?: string; requestedReasoningEffort?: string }>,
|
|
248
648
|
options: OrchestrationOptions = {}
|
|
249
649
|
): OrchestrationResult {
|
|
250
650
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
251
651
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
252
|
-
if (
|
|
253
|
-
getAgentClientCapability(input.client, 'subagents').level === 'unsupported'
|
|
254
|
-
|| getAgentClientCapability(input.client, 'orchestration').level === 'unsupported'
|
|
255
|
-
) {
|
|
652
|
+
if (!(options.supportsLifecycleDelegation ?? supportsLifecycleDelegation)(input.client)) {
|
|
256
653
|
return failed('ORCHESTRATION_CLIENT_UNSUPPORTED', `client '${input.client}' does not support lifecycle orchestration`, resolved.taskId);
|
|
257
654
|
}
|
|
258
655
|
const run = readRun(resolved.taskDir);
|
|
259
656
|
if (!run || run.status !== 'running') return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
|
|
657
|
+
if (!isV2Policy(run.modelPolicy)) return failed('ORCHESTRATION_MODEL_EVIDENCE_MISSING', 'running orchestration has no persisted model policy', resolved.taskId);
|
|
260
658
|
if (run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
|
|
261
659
|
const repositoryPending = (['claude-code', 'codex'] as AgentClientId[])
|
|
262
660
|
.flatMap((client) => matchingDelegations(client, () => true, options));
|
|
@@ -267,9 +665,22 @@ function prepareOrchestrationDelegation(
|
|
|
267
665
|
const routed = routeOrchestration(taskRef, options);
|
|
268
666
|
if (!routed.next) return routed;
|
|
269
667
|
const next = routed.next;
|
|
668
|
+
if (!validModel(input.requestedModel)) {
|
|
669
|
+
return failed('ORCHESTRATION_REQUESTED_MODEL_REQUIRED', 'prepare requires the exact requested model identity', resolved.taskId);
|
|
670
|
+
}
|
|
671
|
+
if (!validModel(input.requestedReasoningEffort)) {
|
|
672
|
+
return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', resolved.taskId);
|
|
673
|
+
}
|
|
674
|
+
const expectedPolicy = run.modelPolicy[next.role];
|
|
675
|
+
if (input.requestedModel !== expectedPolicy.model) {
|
|
676
|
+
return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
|
|
677
|
+
}
|
|
678
|
+
if (input.requestedReasoningEffort !== expectedPolicy.reasoningEffort) {
|
|
679
|
+
return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_MISMATCH', `requested reasoning effort does not match the persisted ${next.role} policy`, resolved.taskId);
|
|
680
|
+
}
|
|
270
681
|
let beforeFingerprint: string;
|
|
271
682
|
try {
|
|
272
|
-
beforeFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(resolved.repoRoot);
|
|
683
|
+
beforeFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(resolved.repoRoot, resolved.taskId);
|
|
273
684
|
} catch (error) {
|
|
274
685
|
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
275
686
|
}
|
|
@@ -281,7 +692,9 @@ function prepareOrchestrationDelegation(
|
|
|
281
692
|
round: next.round,
|
|
282
693
|
artifact: next.artifact,
|
|
283
694
|
client: input.client,
|
|
284
|
-
requestedModel: input.requestedModel
|
|
695
|
+
requestedModel: input.requestedModel,
|
|
696
|
+
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
697
|
+
workspaceSnapshotScope: 'task',
|
|
285
698
|
beforeFingerprint
|
|
286
699
|
}, { id: options.id, now: options.now });
|
|
287
700
|
const updated = withUpdatedRun(run, {
|
|
@@ -295,6 +708,26 @@ function prepareOrchestrationDelegation(
|
|
|
295
708
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
|
|
296
709
|
}
|
|
297
710
|
|
|
711
|
+
function prepareOrchestrationDelegation(
|
|
712
|
+
taskRef: string,
|
|
713
|
+
input: Readonly<{ client: AgentClientId; requestedModel?: string; requestedReasoningEffort?: string }>,
|
|
714
|
+
options: OrchestrationOptions = {}
|
|
715
|
+
): OrchestrationResult {
|
|
716
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
717
|
+
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
718
|
+
try {
|
|
719
|
+
return withTaskExecutionLock(
|
|
720
|
+
resolved.repoRoot,
|
|
721
|
+
resolved.taskId,
|
|
722
|
+
'task-orchestration.prepare',
|
|
723
|
+
() => prepareOrchestrationDelegationUnlocked(taskRef, input, options)
|
|
724
|
+
);
|
|
725
|
+
} catch (error) {
|
|
726
|
+
if (error instanceof TaskExecutionLockError) return failed(error.code, error.message, resolved.taskId);
|
|
727
|
+
throw error;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
298
731
|
function matchingDelegations(
|
|
299
732
|
client: AgentClientId,
|
|
300
733
|
predicate: (receipt: DelegationReceipt) => boolean,
|
|
@@ -364,7 +797,8 @@ function sealMatchingOrchestrationDelegation(
|
|
|
364
797
|
}
|
|
365
798
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
366
799
|
try {
|
|
367
|
-
const
|
|
800
|
+
const snapshotTaskId = receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null;
|
|
801
|
+
const afterFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(repoRoot, snapshotTaskId);
|
|
368
802
|
const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(repoRoot, receipt.beforeFingerprint, afterFingerprint);
|
|
369
803
|
return sealOrchestrationDelegation(matched.taskId, {
|
|
370
804
|
childId: event.childId,
|
|
@@ -419,36 +853,20 @@ function completeOrchestrationStage(
|
|
|
419
853
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
420
854
|
}
|
|
421
855
|
|
|
422
|
-
function
|
|
856
|
+
function inspectOrchestrationStage(
|
|
423
857
|
taskRef: string,
|
|
424
|
-
|
|
858
|
+
event: OrchestrationStageIdentity,
|
|
425
859
|
options: OrchestrationOptions = {}
|
|
426
860
|
): OrchestrationResult {
|
|
427
861
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
428
862
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
return failed('
|
|
863
|
+
let run: OrchestrationRun | null;
|
|
864
|
+
try {
|
|
865
|
+
run = readRun(resolved.taskDir);
|
|
866
|
+
} catch (error) {
|
|
867
|
+
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
434
868
|
}
|
|
435
|
-
return
|
|
436
|
-
stage: receipt.stage,
|
|
437
|
-
round: receipt.round,
|
|
438
|
-
artifact: receipt.artifact,
|
|
439
|
-
agent
|
|
440
|
-
}, options);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function validateOrchestrationStage(
|
|
444
|
-
taskRef: string,
|
|
445
|
-
event: Readonly<{ stage: DelegationStage; round: number; artifact: string; role: DelegationRole }>,
|
|
446
|
-
options: OrchestrationOptions = {}
|
|
447
|
-
): OrchestrationResult {
|
|
448
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
449
|
-
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
450
|
-
const run = readRun(resolved.taskDir);
|
|
451
|
-
if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
|
|
869
|
+
if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
|
|
452
870
|
const receipt = run.pendingDelegation;
|
|
453
871
|
if (
|
|
454
872
|
!receipt
|
|
@@ -458,17 +876,67 @@ function validateOrchestrationStage(
|
|
|
458
876
|
|| receipt.artifact !== event.artifact
|
|
459
877
|
|| receipt.role !== event.role
|
|
460
878
|
) {
|
|
461
|
-
return
|
|
462
|
-
taskRef,
|
|
879
|
+
return failed(
|
|
463
880
|
'ORCHESTRATION_PROVENANCE_MISMATCH',
|
|
464
881
|
'active run does not have one matching activated delegation',
|
|
465
|
-
|
|
466
|
-
options
|
|
882
|
+
resolved.taskId
|
|
467
883
|
);
|
|
468
884
|
}
|
|
469
885
|
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
470
886
|
}
|
|
471
887
|
|
|
888
|
+
function planOrchestrationStageCompletion(
|
|
889
|
+
taskRef: string,
|
|
890
|
+
event: Readonly<{ stage: DelegationStage; round: number; artifact: string; role: DelegationRole; agent: string }>,
|
|
891
|
+
options: OrchestrationOptions = {}
|
|
892
|
+
): OrchestrationCompletionPlanResult {
|
|
893
|
+
const inspected = inspectOrchestrationStage(taskRef, event, options);
|
|
894
|
+
if (inspected.status === 'failed' || !inspected.run?.pendingDelegation) {
|
|
895
|
+
return { result: inspected, plan: null };
|
|
896
|
+
}
|
|
897
|
+
const receipt = inspected.run.pendingDelegation;
|
|
898
|
+
const completed = completeDelegationStage(receipt, event);
|
|
899
|
+
if (!completed.ok) {
|
|
900
|
+
return { result: failed(completed.code, completed.message, inspected.taskId), plan: null };
|
|
901
|
+
}
|
|
902
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
903
|
+
if (!resolved.ok) return { result: failed(resolved.code, resolved.message, resolved.taskId), plan: null };
|
|
904
|
+
const updatedRun = withUpdatedRun(inspected.run, { pendingDelegation: completed.receipt });
|
|
905
|
+
return {
|
|
906
|
+
result: { status: 'running', changed: true, taskId: resolved.taskId, run: updatedRun, next: null, error: null },
|
|
907
|
+
plan: {
|
|
908
|
+
taskId: resolved.taskId,
|
|
909
|
+
taskDir: resolved.taskDir,
|
|
910
|
+
updatedRun
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function commitOrchestrationStageCompletion(plan: OrchestrationStageCompletion): void {
|
|
916
|
+
saveRun(plan.taskDir, plan.updatedRun);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function completeCommitOrchestrationStage(
|
|
920
|
+
taskRef: string,
|
|
921
|
+
agent: string,
|
|
922
|
+
options: OrchestrationOptions = {}
|
|
923
|
+
): OrchestrationResult {
|
|
924
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
925
|
+
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
926
|
+
const run = readRun(resolved.taskDir);
|
|
927
|
+
if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
|
|
928
|
+
const receipt = run.pendingDelegation;
|
|
929
|
+
if (!receipt || receipt.stage !== 'commit') {
|
|
930
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending commit delegation', resolved.taskId);
|
|
931
|
+
}
|
|
932
|
+
return completeOrchestrationStage(taskRef, {
|
|
933
|
+
stage: receipt.stage,
|
|
934
|
+
round: receipt.round,
|
|
935
|
+
artifact: receipt.artifact,
|
|
936
|
+
agent
|
|
937
|
+
}, options);
|
|
938
|
+
}
|
|
939
|
+
|
|
472
940
|
function sealOrchestrationDelegation(
|
|
473
941
|
taskRef: string,
|
|
474
942
|
event: Parameters<typeof sealDelegation>[1],
|
|
@@ -528,16 +996,27 @@ export {
|
|
|
528
996
|
activateOrchestrationDelegation,
|
|
529
997
|
advanceOrchestration,
|
|
530
998
|
beginOrResumeOrchestration,
|
|
999
|
+
commitOrchestrationStageCompletion,
|
|
531
1000
|
completeCommitOrchestrationStage,
|
|
532
1001
|
completeOrchestrationStage,
|
|
1002
|
+
inspectOrchestrationStage,
|
|
533
1003
|
orchestrationPath,
|
|
534
1004
|
pauseOrchestration,
|
|
1005
|
+
planOrchestrationStageCompletion,
|
|
535
1006
|
prepareOrchestrationDelegation,
|
|
536
1007
|
readRun,
|
|
537
1008
|
routeOrchestration,
|
|
538
1009
|
sealMatchingOrchestrationDelegation,
|
|
539
1010
|
sealOrchestrationDelegation,
|
|
540
|
-
statusOrchestration
|
|
541
|
-
|
|
1011
|
+
statusOrchestration
|
|
1012
|
+
};
|
|
1013
|
+
export type {
|
|
1014
|
+
OrchestrationCompletionPlanResult,
|
|
1015
|
+
OrchestrationModelPolicy,
|
|
1016
|
+
OrchestrationNext,
|
|
1017
|
+
OrchestrationResult,
|
|
1018
|
+
OrchestrationRun,
|
|
1019
|
+
OrchestrationStageCompletion,
|
|
1020
|
+
OrchestrationStageIdentity,
|
|
1021
|
+
OrchestrationStatus
|
|
542
1022
|
};
|
|
543
|
-
export type { OrchestrationNext, OrchestrationResult, OrchestrationRun, OrchestrationStatus };
|