@fitlab-ai/agent-infra 0.11.15 → 0.12.1
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/bin/cli.ts +1 -1
- package/dist/bin/cli.js +1 -1
- package/dist/bin/internal-cli.js +6 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +6 -6
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +8 -18
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -1
- package/dist/lib/decide.js +2 -7
- package/dist/lib/internal/cli-route-inventory.js +3 -0
- package/dist/lib/internal/manual-validation.js +19 -33
- package/dist/lib/internal/platform-comment.js +59 -9
- package/dist/lib/internal/task-activity.js +1 -12
- package/dist/lib/internal/task-event.js +1 -12
- package/dist/lib/internal/task-ledger.js +1 -12
- package/dist/lib/internal/task-operation-registry.js +2 -1
- package/dist/lib/internal/task-qualification.js +6 -15
- package/dist/lib/internal/task-review.js +3 -16
- package/dist/lib/internal/task-short-id.js +2 -1
- package/dist/lib/internal/task-verify.js +1 -11
- package/dist/lib/internal/task-warning.js +1 -12
- package/dist/lib/internal/task-workspace.js +43 -0
- package/dist/lib/merge.js +4 -3
- package/dist/lib/platform/issue-comments.js +373 -267
- package/dist/lib/platform/issue-summary-content.js +112 -0
- package/dist/lib/platform/pr-change-report.js +175 -26
- package/dist/lib/platform/pr-summary-content.js +54 -0
- package/dist/lib/platform/pr-summary.js +321 -296
- package/dist/lib/platform/pull-requests.js +3 -15
- package/dist/lib/platform/summary-content-pipeline.js +18 -0
- package/dist/lib/pr-review/evidence-grading.js +3 -2
- package/dist/lib/process-data/index.js +5 -4
- package/dist/lib/process-data/sources.js +6 -0
- package/dist/lib/run/index.js +3 -2
- package/dist/lib/sandbox/commands/create.js +5 -1
- package/dist/lib/sandbox/commands/enter.js +1 -1
- package/dist/lib/sandbox/commands/list-running.js +2 -1
- package/dist/lib/sandbox/control/client.js +8 -13
- package/dist/lib/sandbox/control/completed-reentry.js +2 -1
- package/dist/lib/sandbox/control/task-view.js +4 -2
- package/dist/lib/sandbox/mounts.js +4 -4
- package/dist/lib/sandbox/recovery.js +26 -16
- package/dist/lib/sandbox/removal.js +62 -12
- package/dist/lib/sandbox/task-resolver.js +9 -6
- package/dist/lib/sandbox/workspace-identity.js +6 -5
- package/dist/lib/sandbox/workspace-view.js +29 -22
- package/dist/lib/task/activity-intent.js +2 -12
- package/dist/lib/task/artifact-lifecycle.js +17 -11
- package/dist/lib/task/artifact-operations.js +41 -49
- package/dist/lib/task/capabilities.js +2 -2
- package/dist/lib/task/commands/ls.js +4 -3
- package/dist/lib/task/commands/status.js +1 -1
- package/dist/lib/task/commit-operation.js +6 -6
- package/dist/lib/task/control-authority.js +32 -32
- package/dist/lib/task/create-service.js +2 -4
- package/dist/lib/task/create.js +12 -65
- package/dist/lib/task/delegation-receipts.js +1 -1
- package/dist/lib/task/delivery.js +2 -8
- package/dist/lib/task/events.js +5 -31
- package/dist/lib/task/finalization.js +33 -29
- package/dist/lib/task/lifecycle.js +9 -48
- package/dist/lib/task/local-artifact-finalization.js +1 -2
- package/dist/lib/task/orchestration.js +24 -57
- package/dist/lib/task/platform-operation-recovery.js +87 -88
- package/dist/lib/task/resolve-ref.js +17 -12
- package/dist/lib/task/review-finalization.js +2 -35
- package/dist/lib/task/review-identity.js +3 -1
- package/dist/lib/task/short-id.js +10 -8
- package/dist/lib/task/verification-engine.js +19 -58
- package/dist/lib/task/workspace-root.js +59 -0
- package/dist/lib/task/workspace-snapshot.js +4 -3
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +6 -6
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +8 -34
- package/lib/agent-clients/adapters/codex-orchestration.ts +1 -1
- package/lib/decide.ts +2 -7
- package/lib/internal/cli-route-inventory.ts +2 -0
- package/lib/internal/manual-validation.ts +20 -37
- package/lib/internal/platform-comment.ts +49 -6
- package/lib/internal/task-activity.ts +1 -10
- package/lib/internal/task-event.ts +1 -10
- package/lib/internal/task-ledger.ts +1 -10
- package/lib/internal/task-operation-registry.ts +2 -1
- package/lib/internal/task-qualification.ts +5 -11
- package/lib/internal/task-review.ts +3 -14
- package/lib/internal/task-short-id.ts +2 -1
- package/lib/internal/task-verify.ts +1 -9
- package/lib/internal/task-warning.ts +1 -10
- package/lib/internal/task-workspace.ts +39 -0
- package/lib/merge.ts +4 -3
- package/lib/platform/issue-comments.ts +478 -280
- package/lib/platform/issue-summary-content.ts +132 -0
- package/lib/platform/pr-change-report.ts +165 -20
- package/lib/platform/pr-summary-content.ts +60 -0
- package/lib/platform/pr-summary.ts +367 -269
- package/lib/platform/pull-requests.ts +3 -24
- package/lib/platform/summary-content-pipeline.ts +23 -0
- package/lib/pr-review/evidence-grading.ts +3 -2
- package/lib/process-data/index.ts +4 -3
- package/lib/process-data/sources.ts +6 -0
- package/lib/run/index.ts +3 -2
- package/lib/sandbox/commands/create.ts +5 -1
- package/lib/sandbox/commands/enter.ts +1 -1
- package/lib/sandbox/commands/list-running.ts +2 -1
- package/lib/sandbox/control/client.ts +7 -12
- package/lib/sandbox/control/completed-reentry.ts +2 -1
- package/lib/sandbox/control/task-view.ts +4 -2
- package/lib/sandbox/mounts.ts +4 -4
- package/lib/sandbox/recovery.ts +26 -18
- package/lib/sandbox/removal.ts +60 -27
- package/lib/sandbox/task-resolver.ts +9 -6
- package/lib/sandbox/workspace-identity.ts +6 -5
- package/lib/sandbox/workspace-view.ts +28 -20
- package/lib/task/activity-intent.ts +3 -17
- package/lib/task/artifact-lifecycle.ts +39 -11
- package/lib/task/artifact-operations.ts +28 -35
- package/lib/task/capabilities.ts +2 -2
- package/lib/task/commands/ls.ts +4 -3
- package/lib/task/commands/status.ts +1 -1
- package/lib/task/commit-operation.ts +5 -6
- package/lib/task/control-authority.ts +29 -35
- package/lib/task/create-service.ts +2 -4
- package/lib/task/create.ts +10 -51
- package/lib/task/delegation-receipts.ts +1 -1
- package/lib/task/delivery.ts +2 -10
- package/lib/task/events.ts +5 -32
- package/lib/task/finalization.ts +27 -30
- package/lib/task/lifecycle.ts +9 -50
- package/lib/task/local-artifact-finalization.ts +1 -4
- package/lib/task/orchestration.ts +31 -80
- package/lib/task/platform-operation-recovery.ts +70 -82
- package/lib/task/resolve-ref.ts +18 -12
- package/lib/task/review-finalization.ts +2 -52
- package/lib/task/review-identity.ts +3 -1
- package/lib/task/short-id.ts +10 -8
- package/lib/task/verification-engine.ts +20 -62
- package/lib/task/workspace-root.ts +78 -0
- package/lib/task/workspace-snapshot.ts +4 -3
- package/package.json +1 -1
- package/templates/.agents/QUICKSTART.en.md +19 -8
- package/templates/.agents/QUICKSTART.zh-CN.md +19 -8
- package/templates/.agents/README.en.md +10 -15
- package/templates/.agents/README.zh-CN.md +9 -13
- package/templates/.agents/rules/issue-sync.en.md +1 -1
- package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +2 -2
- package/templates/.agents/rules/next-step-output.zh-CN.md +2 -2
- package/templates/.agents/rules/pr-sync.en.md +5 -3
- package/templates/.agents/rules/pr-sync.zh-CN.md +7 -10
- package/templates/.agents/rules/release-commands.en.md +3 -0
- package/templates/.agents/rules/release-commands.zh-CN.md +3 -0
- package/templates/.agents/rules/task-management.en.md +0 -2
- package/templates/.agents/rules/task-management.zh-CN.md +0 -2
- package/templates/.agents/rules/task-short-id.en.md +2 -2
- package/templates/.agents/rules/task-short-id.zh-CN.md +2 -2
- package/templates/.agents/scripts/task-short-id.js +2 -8
- package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/archive-tasks/SKILL.en.md +5 -5
- package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +10 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/block-task/config/verify.json +0 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/cancel-task/config/verify.json +0 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/code-task/reference/output-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/SKILL.en.md +1 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/config/verify.json +0 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +2 -2
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +2 -2
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +2 -2
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +0 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +15 -30
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +17 -32
- package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +3 -3
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/create-pr/config/verify.json +0 -1
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +10 -3
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +10 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +3 -3
- package/templates/.agents/skills/create-release-note/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +4 -22
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -22
- package/templates/.agents/skills/create-task/config/verify.json +0 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-codescan/config/verify.json +0 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +3 -3
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-dependabot/config/verify.json +0 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-issue/config/verify.json +0 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/plan-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/restore-task/config/verify.json +0 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +0 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +4 -4
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +4 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +0 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-pr/config/verify.en.json +0 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +1 -1
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +0 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +0 -3
- package/templates/.agents/skills/test/SKILL.zh-CN.md +0 -3
- package/templates/.agents/skills/test-integration/SKILL.en.md +0 -3
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +0 -3
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +13 -6
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +0 -3
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +0 -3
- package/templates/.agents/skills/watch-pr/SKILL.en.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +3 -3
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +3 -3
- package/templates/.agents/templates/handoff.en.md +1 -1
- package/templates/.agents/templates/handoff.zh-CN.md +1 -1
- package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +1 -1
- package/templates/.agents/workflows/refactoring.en.yaml +1 -1
- package/dist/lib/task/task-execution-lock.js +0 -250
- package/lib/task/task-execution-lock.ts +0 -320
|
@@ -7,19 +7,19 @@ import { validateCurrentTaskContract } from './current-contract.js';
|
|
|
7
7
|
import { parseReviewSummary } from './review-artifacts.js';
|
|
8
8
|
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
9
9
|
import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
|
|
10
|
+
import { resolveProjectTaskWorkspaceRoot } from './workspace-root.js';
|
|
10
11
|
import { activateDelegation, abortActivatedDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
11
12
|
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
12
13
|
import { getAgentClientAdapter, getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
13
14
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
14
15
|
import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
|
|
15
16
|
import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
|
|
16
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
17
17
|
import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.js';
|
|
18
18
|
import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.js';
|
|
19
19
|
import { buildLifecycleFacts, canStart, recommendNext } from './capabilities.js';
|
|
20
20
|
import { hasOpenLifecycleExecution } from './activity-log.js';
|
|
21
21
|
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
22
|
-
import {
|
|
22
|
+
import { resolveArtifactContextForResolvedTask } from './artifact-lifecycle.js';
|
|
23
23
|
function supportsLifecycleDelegation(client) {
|
|
24
24
|
return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
|
|
25
25
|
&& getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
|
|
@@ -274,15 +274,9 @@ function mutateDelegationAdapterEvidence(taskRef, receiptId, adapterId, mutate,
|
|
|
274
274
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
275
275
|
if (!resolved.ok)
|
|
276
276
|
throw new Error(`${resolved.code}: ${resolved.message}`);
|
|
277
|
-
return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options
|
|
277
|
+
return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options);
|
|
278
278
|
}
|
|
279
|
-
function
|
|
280
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
281
|
-
if (!resolved.ok)
|
|
282
|
-
throw new Error(`${resolved.code}: ${resolved.message}`);
|
|
283
|
-
return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options, true);
|
|
284
|
-
}
|
|
285
|
-
function mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options, taskLockHeld) {
|
|
279
|
+
function mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options) {
|
|
286
280
|
const update = () => {
|
|
287
281
|
try {
|
|
288
282
|
const run = readRun(resolved.taskDir, options);
|
|
@@ -316,9 +310,7 @@ function mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterI
|
|
|
316
310
|
}
|
|
317
311
|
};
|
|
318
312
|
let mutationError;
|
|
319
|
-
const result =
|
|
320
|
-
? update()
|
|
321
|
-
: withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `agent-adapter.${adapterId}.evidence`, update);
|
|
313
|
+
const result = update();
|
|
322
314
|
if (mutationError)
|
|
323
315
|
throw mutationError;
|
|
324
316
|
return result;
|
|
@@ -542,10 +534,7 @@ function routeFromFacts(facts) {
|
|
|
542
534
|
const role = action.startsWith('review-') ? 'reviewer' : 'executor';
|
|
543
535
|
return { action: command, role, stage: action };
|
|
544
536
|
}
|
|
545
|
-
function
|
|
546
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
547
|
-
if (!resolved.ok)
|
|
548
|
-
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
537
|
+
function routeResolvedTask(taskRef, resolved, options) {
|
|
549
538
|
let gitRoot;
|
|
550
539
|
try {
|
|
551
540
|
gitRoot = gitRootFor(resolved.repoRoot, options);
|
|
@@ -584,7 +573,7 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
584
573
|
catch (error) {
|
|
585
574
|
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
586
575
|
}
|
|
587
|
-
const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state, hasOpenLifecycleExecution(content) || Boolean(run?.pendingDelegation));
|
|
576
|
+
const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state, hasOpenLifecycleExecution(content) || Boolean(run?.pendingDelegation), resolved.repoRoot);
|
|
588
577
|
if (!facts.ok)
|
|
589
578
|
return failed('ORCHESTRATION_CAPABILITY_FACTS_INVALID', facts.message, resolved.taskId);
|
|
590
579
|
const routed = routeFromFacts(facts.facts);
|
|
@@ -674,7 +663,7 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
674
663
|
}
|
|
675
664
|
const action = routed.stage;
|
|
676
665
|
const family = action === 'analysis' ? 'analysis' : action;
|
|
677
|
-
const artifactContext =
|
|
666
|
+
const artifactContext = resolveArtifactContextForResolvedTask(resolved, family, {
|
|
678
667
|
repoRoot: resolved.repoRoot
|
|
679
668
|
});
|
|
680
669
|
if (artifactContext.status !== 'ready' || !artifactContext.selection) {
|
|
@@ -700,6 +689,12 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
700
689
|
};
|
|
701
690
|
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
702
691
|
}
|
|
692
|
+
function routeOrchestration(taskRef, options = {}) {
|
|
693
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
694
|
+
if (!resolved.ok)
|
|
695
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
696
|
+
return routeResolvedTask(taskRef, resolved, options);
|
|
697
|
+
}
|
|
703
698
|
function statusOrchestration(taskRef, options = {}) {
|
|
704
699
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
705
700
|
if (!resolved.ok)
|
|
@@ -743,7 +738,7 @@ function validateDelegationRun(taskRef, taskId, run, options) {
|
|
|
743
738
|
}
|
|
744
739
|
return null;
|
|
745
740
|
}
|
|
746
|
-
function
|
|
741
|
+
function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
747
742
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
748
743
|
if (!resolved.ok)
|
|
749
744
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
@@ -763,7 +758,7 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
763
758
|
if (runFailure)
|
|
764
759
|
return runFailure;
|
|
765
760
|
const activeRun = run;
|
|
766
|
-
const routed =
|
|
761
|
+
const routed = routeResolvedTask(taskRef, resolved, options);
|
|
767
762
|
if (!routed.next)
|
|
768
763
|
return routed;
|
|
769
764
|
const next = routed.next;
|
|
@@ -797,20 +792,7 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
797
792
|
saveRun(resolved.taskDir, updated);
|
|
798
793
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
|
|
799
794
|
}
|
|
800
|
-
function
|
|
801
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
802
|
-
if (!resolved.ok)
|
|
803
|
-
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
804
|
-
try {
|
|
805
|
-
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.prepare.task', () => prepareOrchestrationDelegationUnlocked(taskRef, input, options));
|
|
806
|
-
}
|
|
807
|
-
catch (error) {
|
|
808
|
-
if (error instanceof TaskExecutionLockError)
|
|
809
|
-
return failed(error.code, error.message, resolved.taskId);
|
|
810
|
-
throw error;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
function dispatchOrchestrationDelegationUnlocked(taskRef, options = {}) {
|
|
795
|
+
function dispatchOrchestrationDelegation(taskRef, options = {}) {
|
|
814
796
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
815
797
|
if (!resolved.ok)
|
|
816
798
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
@@ -830,19 +812,6 @@ function dispatchOrchestrationDelegationUnlocked(taskRef, options = {}) {
|
|
|
830
812
|
saveRun(resolved.taskDir, updated);
|
|
831
813
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
832
814
|
}
|
|
833
|
-
function dispatchOrchestrationDelegation(taskRef, options = {}) {
|
|
834
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
835
|
-
if (!resolved.ok)
|
|
836
|
-
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
837
|
-
try {
|
|
838
|
-
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.dispatch.task', () => dispatchOrchestrationDelegationUnlocked(taskRef, options));
|
|
839
|
-
}
|
|
840
|
-
catch (error) {
|
|
841
|
-
if (error instanceof TaskExecutionLockError)
|
|
842
|
-
return failed(error.code, error.message, resolved.taskId);
|
|
843
|
-
throw error;
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
815
|
function matchingDelegations(predicate, options) {
|
|
847
816
|
const taskId = options.taskId;
|
|
848
817
|
if (taskId === undefined || taskId === '') {
|
|
@@ -852,7 +821,7 @@ function matchingDelegations(predicate, options) {
|
|
|
852
821
|
return { ok: false, error: failed('ORCHESTRATION_TASK_ID_INVALID', 'taskId must be a canonical TASK-id') };
|
|
853
822
|
}
|
|
854
823
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
855
|
-
const taskDir = path.join(repoRoot, '
|
|
824
|
+
const taskDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId);
|
|
856
825
|
const run = readRun(taskDir, options);
|
|
857
826
|
const matches = run && run.status === 'running' && run.pendingDelegation && predicate(run.pendingDelegation)
|
|
858
827
|
? [{ taskId, run }]
|
|
@@ -1033,7 +1002,7 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
|
1033
1002
|
if (!resolved.ok)
|
|
1034
1003
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1035
1004
|
try {
|
|
1036
|
-
return
|
|
1005
|
+
return (() => {
|
|
1037
1006
|
const run = readRun(resolved.taskDir, options);
|
|
1038
1007
|
const receipt = run?.pendingDelegation;
|
|
1039
1008
|
if (!run || !receipt) {
|
|
@@ -1073,11 +1042,9 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
|
1073
1042
|
}, options.now);
|
|
1074
1043
|
saveRun(resolved.taskDir, updated);
|
|
1075
1044
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1076
|
-
});
|
|
1045
|
+
})();
|
|
1077
1046
|
}
|
|
1078
1047
|
catch (error) {
|
|
1079
|
-
if (error instanceof TaskExecutionLockError)
|
|
1080
|
-
return failed(error.code, error.message, resolved.taskId);
|
|
1081
1048
|
return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
1082
1049
|
}
|
|
1083
1050
|
}
|
|
@@ -1088,7 +1055,7 @@ function recoveryReceiptMatches(receipt, event) {
|
|
|
1088
1055
|
&& receipt.childId === event.childId
|
|
1089
1056
|
&& normalizeAgentToken(event.startedAgent) === normalizeAgentToken(receipt.client);
|
|
1090
1057
|
}
|
|
1091
|
-
function
|
|
1058
|
+
function recoverActivatedOrchestrationDelegation(taskRef, event, options = {}) {
|
|
1092
1059
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1093
1060
|
if (!resolved.ok)
|
|
1094
1061
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
@@ -1204,7 +1171,7 @@ function sealOrchestrationDelegation(taskRef, event, expected, options = {}, bef
|
|
|
1204
1171
|
const resolved = resolveTaskRef(taskRef, { repoRoot: orchestrationOptions.repoRoot });
|
|
1205
1172
|
if (!resolved.ok)
|
|
1206
1173
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1207
|
-
return
|
|
1174
|
+
return (() => {
|
|
1208
1175
|
const run = readRun(resolved.taskDir, orchestrationOptions);
|
|
1209
1176
|
if (!run?.pendingDelegation)
|
|
1210
1177
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
@@ -1226,7 +1193,7 @@ function sealOrchestrationDelegation(taskRef, event, expected, options = {}, bef
|
|
|
1226
1193
|
const updated = withUpdatedRun(current, { pendingDelegation: result.receipt });
|
|
1227
1194
|
saveRun(resolved.taskDir, updated);
|
|
1228
1195
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1229
|
-
});
|
|
1196
|
+
})();
|
|
1230
1197
|
}
|
|
1231
1198
|
function advanceOrchestration(taskRef, options = {}) {
|
|
1232
1199
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
@@ -1262,5 +1229,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
|
|
|
1262
1229
|
saveRun(resolved.taskDir, updated);
|
|
1263
1230
|
return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1264
1231
|
}
|
|
1265
|
-
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, orchestrationPath, readDelegationAdapterEvidence, mutateDelegationAdapterEvidence,
|
|
1232
|
+
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, orchestrationPath, readDelegationAdapterEvidence, mutateDelegationAdapterEvidence, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverPreparedOrchestrationDelegation, recoverActivatedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration, };
|
|
1266
1233
|
//# sourceMappingURL=orchestration.js.map
|
|
@@ -2,13 +2,15 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
|
-
import { inspectPlatformCommentOperation,
|
|
5
|
+
import { inspectPlatformCommentOperation, syncPlatformComment } from '../platform/issue-comments.js';
|
|
6
|
+
import { parseDeliverySummaryContent, renderDeliverySummaryContent, parseCancelSummaryContent, renderCancelSummaryContent } from '../platform/issue-summary-content.js';
|
|
7
|
+
import { enumerateArtifacts } from './artifacts.js';
|
|
6
8
|
import { inspectPlatformIssue, syncPlatformIssue } from '../platform/issues.js';
|
|
7
9
|
import { resolvePlatformProviderContext } from '../platform/context.js';
|
|
8
10
|
import { providerOperationContext, providerError, unsupportedProviderOperation } from '../platform/provider-bridge.js';
|
|
9
11
|
import { inLabelMappingDigest } from '../platform/in-label-sync.js';
|
|
10
|
-
import { bindPlatformPullRequest,
|
|
11
|
-
import {
|
|
12
|
+
import { bindPlatformPullRequest, recoverCreatedPullRequest, syncPlatformPullRequest } from '../platform/pull-requests.js';
|
|
13
|
+
import { replayPullRequestSummary } from '../platform/pr-summary.js';
|
|
12
14
|
import { publishPrReview, readReviewBodyFile } from '../platform/pr-review.js';
|
|
13
15
|
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
14
16
|
import { taskIssueIdentity } from '../platform/task-identities.js';
|
|
@@ -35,32 +37,70 @@ function summaryPayload(taskDir, taskId) {
|
|
|
35
37
|
if (!fs.existsSync(file))
|
|
36
38
|
return null;
|
|
37
39
|
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
let content;
|
|
41
|
+
try {
|
|
42
|
+
content = parseDeliverySummaryContent(data.content);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
content = null;
|
|
46
|
+
}
|
|
47
|
+
if (data.taskId !== taskId || !content || content.taskId !== taskId || typeof data.contentSha256 !== 'string'
|
|
48
|
+
|| createHash('sha256').update(JSON.stringify(content)).digest('hex') !== data.contentSha256) {
|
|
42
49
|
throw Object.assign(new Error('delivery summary staging record is invalid'), { code: 'SUMMARY_STAGING_INVALID' });
|
|
43
50
|
}
|
|
44
|
-
|
|
51
|
+
const canonical = canonicalizeSummaryBody(renderDeliverySummaryContent(content));
|
|
52
|
+
if (!canonical?.ok)
|
|
53
|
+
throw Object.assign(new Error('delivery summary staging body is not canonical'), { code: 'SUMMARY_STAGING_INVALID' });
|
|
54
|
+
return { body: canonical.value, sha256: createHash('sha256').update(canonical.value).digest('hex') };
|
|
55
|
+
}
|
|
56
|
+
function cancellationPayload(taskDir, taskId) {
|
|
57
|
+
const taskPath = path.join(taskDir, 'task.md');
|
|
58
|
+
if (!fs.existsSync(taskPath))
|
|
59
|
+
return null;
|
|
60
|
+
const taskContent = fs.readFileSync(taskPath, 'utf8');
|
|
61
|
+
const frontmatter = parseTaskFrontmatter(taskContent);
|
|
62
|
+
const title = /^#\s+(.+)$/mu.exec(taskContent)?.[1]?.trim() || taskId;
|
|
63
|
+
const artifacts = enumerateArtifacts(taskDir).filter((artifact) => artifact.name !== 'task.md').map((artifact) => artifact.name);
|
|
64
|
+
const content = parseCancelSummaryContent({
|
|
65
|
+
taskId,
|
|
66
|
+
title,
|
|
67
|
+
outcome: 'cancelled',
|
|
68
|
+
reason: frontmatter.cancel_reason,
|
|
69
|
+
cancelledAt: frontmatter.cancelled_at,
|
|
70
|
+
lastCompletedArtifact: artifacts.at(-1) ?? null
|
|
71
|
+
});
|
|
72
|
+
return renderCancelSummaryContent(content);
|
|
45
73
|
}
|
|
46
74
|
async function replayComment(taskId, taskDir, operation, agent, cwd, client) {
|
|
47
75
|
if (operation.kind === 'task-comment') {
|
|
48
|
-
return syncPlatformComment(taskId, { kind: 'task', agent, cwd, client, dependency: operation.dependency,
|
|
76
|
+
return syncPlatformComment(taskId, { kind: 'task', agent, cwd, client, dependency: operation.dependency, queuedOperation: operation });
|
|
49
77
|
}
|
|
50
78
|
if (operation.kind === 'artifact-comment') {
|
|
51
|
-
return syncPlatformComment(taskId, { kind: 'artifact', artifact: operation.target, agent, cwd, client, dependency: operation.dependency,
|
|
79
|
+
return syncPlatformComment(taskId, { kind: 'artifact', artifact: operation.target, agent, cwd, client, dependency: operation.dependency, queuedOperation: operation });
|
|
52
80
|
}
|
|
53
81
|
if (operation.kind === 'summary-comment') {
|
|
54
|
-
|
|
82
|
+
let summary;
|
|
83
|
+
try {
|
|
84
|
+
summary = summaryPayload(taskDir, taskId);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return platformResult('failed', { error: { code: 'SUMMARY_STAGING_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
|
|
88
|
+
}
|
|
55
89
|
if (!summary)
|
|
56
|
-
return platformResult('
|
|
57
|
-
return syncPlatformComment(taskId, {
|
|
58
|
-
kind: 'summary', body: summary.body, agent, cwd,
|
|
59
|
-
summaryAuthorization: { sha256: summary.sha256 }, dependency: operation.dependency, client, skipQueue: true
|
|
60
|
-
});
|
|
90
|
+
return platformResult('blocked', { error: { code: 'SUMMARY_STAGING_MISSING', message: 'delivery summary staging record is unavailable', retryable: true } });
|
|
91
|
+
return syncPlatformComment(taskId, { kind: 'summary', body: summary.body, agent, cwd, client, summaryAuthorization: { sha256: summary.sha256 }, dependency: operation.dependency, queuedOperation: operation });
|
|
61
92
|
}
|
|
62
93
|
if (operation.kind === 'cancel-comment') {
|
|
63
|
-
|
|
94
|
+
let body;
|
|
95
|
+
try {
|
|
96
|
+
body = cancellationPayload(taskDir, taskId);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return platformResult('failed', { error: { code: 'CANCEL_SUMMARY_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
|
|
100
|
+
}
|
|
101
|
+
if (!body)
|
|
102
|
+
return platformResult('blocked', { error: { code: 'CANCEL_SUMMARY_INVALID', message: 'Cancellation facts are unavailable for recovery', retryable: true } });
|
|
103
|
+
return syncPlatformComment(taskId, { kind: 'cancel', body, agent, cwd, client, dependency: operation.dependency, queuedOperation: operation });
|
|
64
104
|
}
|
|
65
105
|
return platformResult('failed', { error: { code: 'PLATFORM_OPERATION_UNSUPPORTED', message: `operation kind '${operation.kind}' has no registered recovery handler`, retryable: false } });
|
|
66
106
|
}
|
|
@@ -185,65 +225,6 @@ async function verifyIssueCreate(taskId, operation, cwd, client) {
|
|
|
185
225
|
} });
|
|
186
226
|
return platformResult('no-op');
|
|
187
227
|
}
|
|
188
|
-
async function replayPullRequestSummary(taskId, operation, agent, cwd, client) {
|
|
189
|
-
let identity;
|
|
190
|
-
try {
|
|
191
|
-
identity = parseResourceIdentity(JSON.parse(operation.target), 'queued pull-request summary identity');
|
|
192
|
-
}
|
|
193
|
-
catch {
|
|
194
|
-
return platformResult('failed', { error: { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID', message: 'Queued pull-request summary identity is invalid', retryable: false } });
|
|
195
|
-
}
|
|
196
|
-
const summary = operation.pullRequestSummary;
|
|
197
|
-
if (!summary)
|
|
198
|
-
return platformResult('blocked', { error: {
|
|
199
|
-
code: 'PLATFORM_OPERATION_PAYLOAD_INVALID', message: 'Pull-request summary recovery content is missing', retryable: false
|
|
200
|
-
} });
|
|
201
|
-
const manualValidationHeading = /^###\s+(?:⏳\s+(?:Manual Validation Pending|人工验证待收尾)|✅\s+(?:Manual Validation Passed|人工验证已通过))\s*$/mu.test(summary.body);
|
|
202
|
-
if (manualValidationHeading && !summary.manualValidation)
|
|
203
|
-
return platformResult('blocked', { error: {
|
|
204
|
-
code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'Queued manual-validation summary has no persisted transaction context', retryable: false
|
|
205
|
-
} });
|
|
206
|
-
const resolved = await resolvePlatformProviderContext({ cwd, client });
|
|
207
|
-
if (!resolved.ok)
|
|
208
|
-
return resolved.context;
|
|
209
|
-
const listed = resolved.value.provider.comments?.list
|
|
210
|
-
? await resolved.value.provider.comments.list({ context: providerOperationContext(resolved.value), parent: identity })
|
|
211
|
-
: unsupportedProviderOperation(resolved.value.provider, 'comments.list');
|
|
212
|
-
if (!listed.ok)
|
|
213
|
-
return platformResult(providerErrorRetryable(listed.error.code) ? 'blocked' : 'failed', { error: providerError(listed.error, 'PLATFORM_PROVIDER_OPERATION_FAILED') });
|
|
214
|
-
const marker = `<!-- sync-pr:${taskId}:summary -->`;
|
|
215
|
-
const summaries = listed.value.filter((comment) => normalizeCommentContent(comment.body).split('\n', 1)[0] === marker);
|
|
216
|
-
if (summaries.length > 1)
|
|
217
|
-
return platformResult('blocked', { error: {
|
|
218
|
-
code: 'PR_SUMMARY_MARKER_AMBIGUOUS', message: 'Multiple pull-request comments contain the queued summary marker', retryable: false
|
|
219
|
-
} });
|
|
220
|
-
if (!summary.manualValidation && summaries.length === 1 && createHash('sha256').update(normalizeCommentContent(summaries[0].body)).digest('hex') === operation.expectedDigest) {
|
|
221
|
-
return platformResult('no-op');
|
|
222
|
-
}
|
|
223
|
-
const bound = await inspectPlatformPullRequest(taskId, { cwd, client });
|
|
224
|
-
if (!bound.pullRequest || !resourceIdentityEquals(bound.pullRequest.identity, identity))
|
|
225
|
-
return platformResult('blocked', { error: bound.error ?? {
|
|
226
|
-
code: 'PLATFORM_OPERATION_IDENTITY_MISMATCH', message: 'Bound pull request differs from the queued summary target', retryable: false
|
|
227
|
-
} });
|
|
228
|
-
const replayed = await syncPullRequestSummary(taskId, {
|
|
229
|
-
agent,
|
|
230
|
-
cwd,
|
|
231
|
-
client,
|
|
232
|
-
body: summary.body,
|
|
233
|
-
changeReportFile: summary.changeReportFile,
|
|
234
|
-
...(summary.manualValidation ? { manualValidation: summary.manualValidation } : {}),
|
|
235
|
-
primaryResult: 'no_op',
|
|
236
|
-
strict: true,
|
|
237
|
-
skipQueue: true,
|
|
238
|
-
lockAlreadyHeld: true
|
|
239
|
-
});
|
|
240
|
-
if ((replayed.status === 'applied' || replayed.status === 'no-op') && !replayed.error && replayed.warnings.length === 0) {
|
|
241
|
-
return platformResult(replayed.status, { changed: replayed.changed });
|
|
242
|
-
}
|
|
243
|
-
return platformResult('blocked', { error: replayed.error ?? {
|
|
244
|
-
code: 'PR_SUMMARY_RECOVERY_UNCONFIRMED', message: 'Pull-request summary replay did not confirm the requested remote state', retryable: true
|
|
245
|
-
} });
|
|
246
|
-
}
|
|
247
228
|
async function replayPullRequestReview(operation, agent, cwd, client) {
|
|
248
229
|
const intent = operation.pullRequestReview;
|
|
249
230
|
if (!intent)
|
|
@@ -302,6 +283,14 @@ async function replayPullRequest(taskId, operation, agent, cwd) {
|
|
|
302
283
|
}
|
|
303
284
|
return recoverCreatedPullRequest(taskId, { agent, cwd, base: intent.baseRef || '', head: intent.headRef || '', skipQueue: true });
|
|
304
285
|
}
|
|
286
|
+
function isRecoveryEligible(operation, excludeId) {
|
|
287
|
+
if (operation.id === excludeId)
|
|
288
|
+
return false;
|
|
289
|
+
if (operation.state === 'queued' || operation.state === 'pending' || operation.state === 'unknown')
|
|
290
|
+
return true;
|
|
291
|
+
return operation.state === 'failed'
|
|
292
|
+
&& operation.lastCode !== 'PLATFORM_OPERATION_SUPERSEDED';
|
|
293
|
+
}
|
|
305
294
|
async function recoverPlatformOperationsOnce(taskRef, selection, options) {
|
|
306
295
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
307
296
|
if (!resolved.ok)
|
|
@@ -314,9 +303,7 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
|
|
|
314
303
|
const value = error;
|
|
315
304
|
return result('failed', [], [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false });
|
|
316
305
|
}
|
|
317
|
-
const candidates = journal.operations.filter((operation) => operation
|
|
318
|
-
&& (operation.state === 'queued' || operation.state === 'pending' || operation.state === 'unknown'
|
|
319
|
-
|| (operation.state === 'failed' && operation.lastCode !== 'PLATFORM_OPERATION_SUPERSEDED')));
|
|
306
|
+
const candidates = journal.operations.filter((operation) => isRecoveryEligible(operation, options.excludeId));
|
|
320
307
|
if (!candidates.length)
|
|
321
308
|
return result('no-op', [], []);
|
|
322
309
|
const head = candidates[0];
|
|
@@ -383,9 +370,22 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
|
|
|
383
370
|
: operation.kind === 'artifact-comment'
|
|
384
371
|
? { kind: 'artifact', artifact: operation.target, agent: options.agent, cwd: resolved.repoRoot, dependency: operation.dependency }
|
|
385
372
|
: operation.kind === 'summary-comment' ? (() => {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
373
|
+
try {
|
|
374
|
+
const summary = summaryPayload(resolved.taskDir, resolved.taskId);
|
|
375
|
+
return summary ? { kind: 'summary', body: summary.body, agent: options.agent, cwd: resolved.repoRoot, summaryAuthorization: { sha256: summary.sha256 }, dependency: operation.dependency } : null;
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
})() : (() => {
|
|
381
|
+
try {
|
|
382
|
+
const body = cancellationPayload(resolved.taskDir, resolved.taskId);
|
|
383
|
+
return body ? { kind: 'cancel', body, agent: options.agent, cwd: resolved.repoRoot, dependency: operation.dependency } : null;
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
})();
|
|
389
389
|
const current = commentOptions ? inspectPlatformCommentOperation(resolved.taskId, commentOptions) : null;
|
|
390
390
|
if (!current) {
|
|
391
391
|
pending.push(operation.id);
|
|
@@ -408,20 +408,21 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
|
|
|
408
408
|
remote = operation.kind === 'issue-create'
|
|
409
409
|
? await verifyIssueCreate(resolved.taskId, operation, resolved.repoRoot)
|
|
410
410
|
: operation.kind === 'pull-request-summary'
|
|
411
|
-
? await replayPullRequestSummary(resolved.taskId,
|
|
411
|
+
? await replayPullRequestSummary(resolved.taskId, targetOperation, { agent: options.agent, cwd: resolved.repoRoot, client: options.client })
|
|
412
412
|
: operation.kind === 'pull-request-review'
|
|
413
413
|
? await replayPullRequestReview(operation, options.agent, resolved.repoRoot, options.client)
|
|
414
414
|
: operation.kind === 'issue-metadata'
|
|
415
415
|
? await replayIssueMetadata(resolved.taskId, targetOperation, options.agent, resolved.repoRoot)
|
|
416
416
|
: operation.kind === 'pull-request'
|
|
417
417
|
? await replayPullRequest(resolved.taskId, targetOperation, options.agent, resolved.repoRoot)
|
|
418
|
-
: await replayComment(resolved.taskId, resolved.taskDir,
|
|
418
|
+
: await replayComment(resolved.taskId, resolved.taskDir, targetOperation, options.agent, resolved.repoRoot, options.client);
|
|
419
419
|
}
|
|
420
420
|
catch (error) {
|
|
421
421
|
const value = error;
|
|
422
422
|
remote = platformResult('blocked', { error: { code: value.code || 'PLATFORM_OPERATION_RECOVERY_FAILED', message: value.message || String(error), retryable: true } });
|
|
423
423
|
}
|
|
424
424
|
const succeeded = (remote.status === 'applied' || remote.status === 'no-op') && !remote.error;
|
|
425
|
+
const latestOperation = readPlatformOperationJournal(resolved.taskId, resolved.repoRoot).operations.find((item) => item.id === operation.id);
|
|
425
426
|
try {
|
|
426
427
|
recordPlatformOperation({
|
|
427
428
|
taskRef: resolved.taskId,
|
|
@@ -436,7 +437,7 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
|
|
|
436
437
|
...(operation.pullRequestReview ? { pullRequestReview: operation.pullRequestReview } : {}),
|
|
437
438
|
dependency: operation.dependency,
|
|
438
439
|
state: succeeded ? 'succeeded' : 'unknown',
|
|
439
|
-
lastCode: remote.error?.code ?? null
|
|
440
|
+
lastCode: remote.error?.code ?? latestOperation?.lastCode ?? null
|
|
440
441
|
});
|
|
441
442
|
}
|
|
442
443
|
catch (error) {
|
|
@@ -465,9 +466,7 @@ async function recoverPlatformOperations(taskRef, selection, options) {
|
|
|
465
466
|
const value = error;
|
|
466
467
|
return result('failed', [], [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false });
|
|
467
468
|
}
|
|
468
|
-
const eligible = (operation) => operation
|
|
469
|
-
&& (operation.state === 'queued' || operation.state === 'pending' || operation.state === 'unknown'
|
|
470
|
-
|| (operation.state === 'failed' && operation.lastCode !== 'PLATFORM_OPERATION_SUPERSEDED'));
|
|
469
|
+
const eligible = (operation) => isRecoveryEligible(operation, options.excludeId);
|
|
471
470
|
const first = initial.operations.filter(eligible);
|
|
472
471
|
if (!first.length)
|
|
473
472
|
return result('no-op', [], []);
|
|
@@ -3,10 +3,12 @@ import path from 'node:path';
|
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
4
|
import { normalizeShortIdInput, resolveShortIdReadOnly } from './short-id.js';
|
|
5
5
|
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
6
|
+
import { resolveProjectTaskWorkspaceRoot } from './workspace-root.js';
|
|
6
7
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
7
8
|
// Flat-structured workspace dirs that hold tasks under `{dir}/{taskId}/task.md`.
|
|
8
9
|
// Note: `archive` uses a three-level YYYY/MM/DD layout and is handled separately.
|
|
9
10
|
const FLAT_WORKSPACE_DIRS = ['active', 'blocked', 'completed'];
|
|
11
|
+
function taskRoot(repoRoot) { return resolveProjectTaskWorkspaceRoot(repoRoot); }
|
|
10
12
|
function detectRepoRoot() {
|
|
11
13
|
try {
|
|
12
14
|
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
@@ -40,12 +42,12 @@ function listSortedNumeric(dir, width) {
|
|
|
40
42
|
.sort()
|
|
41
43
|
.reverse();
|
|
42
44
|
}
|
|
43
|
-
function findInArchive(
|
|
44
|
-
// archive-tasks stores local material at
|
|
45
|
+
function findInArchive(workspaceRoot, taskId) {
|
|
46
|
+
// archive-tasks stores local material at the project task workspace archive/YYYY/MM/DD/{taskId}/local/task.md
|
|
45
47
|
// where YYYY/MM/DD comes from completed_at (or updated_at fallback) — NOT from
|
|
46
48
|
// the task id's creation date. So we cannot derive the path from taskId alone;
|
|
47
49
|
// walk the bounded YYYY/MM/DD tree instead. Newest-first to favor recent archives.
|
|
48
|
-
const archiveDir = path.join(
|
|
50
|
+
const archiveDir = path.join(workspaceRoot, 'archive');
|
|
49
51
|
for (const year of listSortedNumeric(archiveDir, 4)) {
|
|
50
52
|
const yearDir = path.join(archiveDir, year);
|
|
51
53
|
for (const month of listSortedNumeric(yearDir, 2)) {
|
|
@@ -59,13 +61,13 @@ function findInArchive(repoRoot, taskId) {
|
|
|
59
61
|
}
|
|
60
62
|
return null;
|
|
61
63
|
}
|
|
62
|
-
function findTaskMd(
|
|
64
|
+
function findTaskMd(workspaceRoot, taskId) {
|
|
63
65
|
for (const sub of FLAT_WORKSPACE_DIRS) {
|
|
64
|
-
const candidate = path.join(
|
|
66
|
+
const candidate = path.join(workspaceRoot, sub, taskId, 'task.md');
|
|
65
67
|
if (fs.existsSync(candidate))
|
|
66
68
|
return { taskMdPath: candidate, state: sub };
|
|
67
69
|
}
|
|
68
|
-
const archived = findInArchive(
|
|
70
|
+
const archived = findInArchive(workspaceRoot, taskId);
|
|
69
71
|
return archived ? { taskMdPath: archived, state: 'archive' } : null;
|
|
70
72
|
}
|
|
71
73
|
/**
|
|
@@ -76,8 +78,9 @@ function findTaskMd(repoRoot, taskId) {
|
|
|
76
78
|
*/
|
|
77
79
|
function enumerateTaskDirs(repoRoot) {
|
|
78
80
|
const out = [];
|
|
81
|
+
const workspaceRoot = taskRoot(repoRoot);
|
|
79
82
|
for (const sub of FLAT_WORKSPACE_DIRS) {
|
|
80
|
-
const base = path.join(
|
|
83
|
+
const base = path.join(workspaceRoot, sub);
|
|
81
84
|
if (!fs.existsSync(base))
|
|
82
85
|
continue;
|
|
83
86
|
for (const entry of fs.readdirSync(base).sort()) {
|
|
@@ -98,6 +101,7 @@ function enumerateTaskDirs(repoRoot) {
|
|
|
98
101
|
*/
|
|
99
102
|
function enumerateAllTaskDirs(repoRoot) {
|
|
100
103
|
const out = [];
|
|
104
|
+
const workspaceRoot = taskRoot(repoRoot);
|
|
101
105
|
const seen = new Map();
|
|
102
106
|
const add = (taskId, taskDir, state) => {
|
|
103
107
|
if (!TASK_ID_RE.test(taskId))
|
|
@@ -116,13 +120,13 @@ function enumerateAllTaskDirs(repoRoot) {
|
|
|
116
120
|
out.push({ taskId, taskDir, state });
|
|
117
121
|
};
|
|
118
122
|
for (const state of FLAT_WORKSPACE_DIRS) {
|
|
119
|
-
const base = path.join(
|
|
123
|
+
const base = path.join(workspaceRoot, state);
|
|
120
124
|
if (!fs.existsSync(base))
|
|
121
125
|
continue;
|
|
122
126
|
for (const taskId of fs.readdirSync(base).sort())
|
|
123
127
|
add(taskId, path.join(base, taskId), state);
|
|
124
128
|
}
|
|
125
|
-
const archive = path.join(
|
|
129
|
+
const archive = path.join(workspaceRoot, 'archive');
|
|
126
130
|
for (const year of listSortedNumeric(archive, 4).reverse()) {
|
|
127
131
|
const yearDir = path.join(archive, year);
|
|
128
132
|
for (const month of listSortedNumeric(yearDir, 2).reverse()) {
|
|
@@ -137,8 +141,9 @@ function enumerateAllTaskDirs(repoRoot) {
|
|
|
137
141
|
return out;
|
|
138
142
|
}
|
|
139
143
|
function locateHotTaskDirs(repoRoot, taskId) {
|
|
144
|
+
const workspaceRoot = taskRoot(repoRoot);
|
|
140
145
|
return FLAT_WORKSPACE_DIRS.flatMap((state) => {
|
|
141
|
-
const taskDir = path.join(
|
|
146
|
+
const taskDir = path.join(workspaceRoot, state, taskId);
|
|
142
147
|
const taskMdPath = path.join(taskDir, 'task.md');
|
|
143
148
|
return fs.existsSync(taskMdPath) ? [{ taskId, taskDir, taskMdPath, state }] : [];
|
|
144
149
|
});
|
|
@@ -203,7 +208,7 @@ function resolveTaskRef(arg, options = {}) {
|
|
|
203
208
|
}
|
|
204
209
|
taskId = shortResult.taskId;
|
|
205
210
|
}
|
|
206
|
-
const located = findTaskMd(repoRoot, taskId);
|
|
211
|
+
const located = findTaskMd(taskRoot(repoRoot), taskId);
|
|
207
212
|
if (!located) {
|
|
208
213
|
return {
|
|
209
214
|
ok: false,
|
|
@@ -255,7 +260,7 @@ function resolveTaskContext(taskRef, options = {}) {
|
|
|
255
260
|
taskId: null
|
|
256
261
|
};
|
|
257
262
|
}
|
|
258
|
-
const activeDir = path.join(repoRoot, '
|
|
263
|
+
const activeDir = path.join(taskRoot(repoRoot), 'active');
|
|
259
264
|
const matches = [];
|
|
260
265
|
if (fs.existsSync(activeDir)) {
|
|
261
266
|
for (const entry of fs.readdirSync(activeDir).sort()) {
|