@fitlab-ai/agent-infra 0.11.3 → 0.11.5
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 +17 -8
- package/README.zh-CN.md +17 -8
- package/dist/bin/internal-cli.js +1 -6
- package/dist/lib/decide.js +4 -41
- package/dist/lib/git/branch-worktree.js +45 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -4
- package/dist/lib/internal/task-activity.js +7 -40
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-event.js +4 -37
- package/dist/lib/internal/task-finalization.js +48 -1
- package/dist/lib/internal/task-ledger.js +14 -44
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +2 -4
- package/dist/lib/internal/task-review.js +5 -37
- package/dist/lib/internal/task-verify.js +3 -47
- package/dist/lib/internal/task-warning.js +4 -40
- package/dist/lib/platform/completion-backfill.js +63 -17
- package/dist/lib/platform/pr-summary.js +4 -23
- package/dist/lib/platform/verification-sync.js +12 -49
- package/dist/lib/sandbox/control/client.js +4 -1
- package/dist/lib/sandbox/control/executor.js +12 -4
- package/dist/lib/sandbox/control/protocol.js +2 -1
- package/dist/lib/sandbox/control/server.js +26 -143
- package/dist/lib/sandbox/removal.js +15 -8
- package/dist/lib/sandbox/task-cleanup.js +13 -20
- package/dist/lib/task/activity-intent.js +1 -2
- package/dist/lib/task/artifact-command.js +11 -4
- package/dist/lib/task/artifact-lifecycle.js +217 -85
- package/dist/lib/task/artifact-operations.js +37 -28
- package/dist/lib/task/artifact-receipts.js +4 -1
- package/dist/lib/task/artifact-schema.js +8 -7
- package/dist/lib/task/artifact-selection.js +25 -0
- package/dist/lib/task/capabilities.js +158 -39
- package/dist/lib/task/commands/status.js +9 -2
- package/dist/lib/task/control-authority.js +12 -168
- package/dist/lib/task/decision-intents.js +14 -4
- package/dist/lib/task/delivery.js +1 -43
- package/dist/lib/task/events.js +68 -73
- package/dist/lib/task/finalization-handoff.js +92 -0
- package/dist/lib/task/finalization.js +423 -154
- package/dist/lib/task/intermediate-cleanup.js +9 -148
- package/dist/lib/task/ledger-intents.js +159 -20
- package/dist/lib/task/lifecycle-path.js +75 -0
- package/dist/lib/task/lifecycle.js +60 -11
- package/dist/lib/task/local-artifact-finalization.js +13 -47
- package/dist/lib/task/orchestration.js +17 -7
- package/dist/lib/task/review-command.js +1 -2
- package/dist/lib/task/review-finalization.js +4 -18
- package/dist/lib/task/rework-intent.js +70 -4
- package/dist/lib/task/snapshot.js +6 -6
- package/dist/lib/task/verification-engine.js +25 -30
- package/dist/lib/task/verification.js +2 -13
- package/dist/lib/task/workflow-warning-intents.js +23 -10
- package/dist/lib/task/workspace-snapshot.js +7 -2
- package/dist/lib/task/write.js +1 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/decide.ts +5 -35
- package/lib/git/branch-worktree.ts +41 -0
- package/lib/internal/cli-route-inventory.ts +1 -3
- package/lib/internal/task-activity.ts +6 -34
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-event.ts +4 -37
- package/lib/internal/task-finalization.ts +47 -1
- package/lib/internal/task-ledger.ts +15 -41
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +2 -4
- package/lib/internal/task-review.ts +5 -31
- package/lib/internal/task-verify.ts +3 -39
- package/lib/internal/task-warning.ts +3 -32
- package/lib/platform/completion-backfill.ts +78 -16
- package/lib/platform/pr-summary.ts +4 -25
- package/lib/platform/verification-sync.ts +13 -53
- package/lib/sandbox/control/client.ts +5 -1
- package/lib/sandbox/control/executor.ts +13 -4
- package/lib/sandbox/control/protocol.ts +3 -2
- package/lib/sandbox/control/server.ts +21 -146
- package/lib/sandbox/removal.ts +23 -7
- package/lib/sandbox/task-cleanup.ts +12 -20
- package/lib/task/activity-intent.ts +1 -2
- package/lib/task/artifact-command.ts +14 -4
- package/lib/task/artifact-lifecycle.ts +236 -85
- package/lib/task/artifact-operations.ts +59 -28
- package/lib/task/artifact-receipts.ts +4 -1
- package/lib/task/artifact-schema.ts +10 -7
- package/lib/task/artifact-selection.ts +53 -0
- package/lib/task/capabilities.ts +154 -40
- package/lib/task/commands/status.ts +11 -2
- package/lib/task/control-authority.ts +17 -189
- package/lib/task/decision-intents.ts +11 -4
- package/lib/task/delivery.ts +1 -40
- package/lib/task/events.ts +73 -74
- package/lib/task/finalization-handoff.ts +104 -0
- package/lib/task/finalization.ts +388 -122
- package/lib/task/intermediate-cleanup.ts +9 -125
- package/lib/task/ledger-intents.ts +157 -20
- package/lib/task/lifecycle-path.ts +98 -0
- package/lib/task/lifecycle.ts +58 -13
- package/lib/task/local-artifact-finalization.ts +14 -56
- package/lib/task/orchestration.ts +26 -8
- package/lib/task/review-command.ts +2 -5
- package/lib/task/review-finalization.ts +4 -20
- package/lib/task/rework-intent.ts +75 -5
- package/lib/task/snapshot.ts +6 -6
- package/lib/task/verification-engine.ts +29 -30
- package/lib/task/verification.ts +3 -16
- package/lib/task/workflow-warning-intents.ts +20 -10
- package/lib/task/workspace-snapshot.ts +3 -2
- package/lib/task/write.ts +1 -4
- package/package.json +1 -1
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/skills/analyze-task/SKILL.en.md +21 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +21 -5
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +1 -3
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +1 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-pr/config/verify.en.json +3 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +3 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +2 -2
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/feature-development.en.yaml +11 -6
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/refactoring.en.yaml +11 -6
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
- package/dist/lib/internal/task-override.js +0 -138
- package/dist/lib/sandbox/control/finalization-response.js +0 -33
- package/dist/lib/task/artifact-recovery.js +0 -476
- package/dist/lib/task/artifact-repair-intent.js +0 -181
- package/dist/lib/task/guard-override.js +0 -31
- package/dist/lib/task/human-override.js +0 -1259
- package/lib/internal/task-override.ts +0 -144
- package/lib/sandbox/control/finalization-response.ts +0 -35
- package/lib/task/artifact-recovery.ts +0 -572
- package/lib/task/artifact-repair-intent.ts +0 -227
- package/lib/task/guard-override.ts +0 -50
- package/lib/task/human-override.ts +0 -1522
|
@@ -11,6 +11,7 @@ import { inspectPlatformPullRequest } from "../platform/pull-requests.js";
|
|
|
11
11
|
import { resolveMaterializedReviewedHeadRelation } from "../platform/change-request-git-evidence.js";
|
|
12
12
|
import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.js";
|
|
13
13
|
import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.js";
|
|
14
|
+
import { resolveBranchWorktree } from "../git/branch-worktree.js";
|
|
14
15
|
import { parseTypedTaskFrontmatter } from "./frontmatter.js";
|
|
15
16
|
import { LEDGER_TERMINAL, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
|
|
16
17
|
import { isValidAgentInfraVersion } from "../version.js";
|
|
@@ -26,7 +27,6 @@ import { OrchestrationStateError, readRun } from "./orchestration.js";
|
|
|
26
27
|
import { resolveDeliveryTarget } from "./delivery-target.js";
|
|
27
28
|
import { readPrDeliveryFact } from "./pr-delivery-fact.js";
|
|
28
29
|
import { validateLocalArtifact } from "./local-artifact-finalization.js";
|
|
29
|
-
import { validateQualificationAudit } from "./qualification-audit.js";
|
|
30
30
|
import { getArtifactSchema } from "./artifact-schema.js";
|
|
31
31
|
import { inspectArtifactContract } from "./artifact-operations.js";
|
|
32
32
|
import { normalizeVerificationRecord } from './gate-policy.js';
|
|
@@ -59,7 +59,6 @@ const ACTIVITY_LOG_STARTED_RE = /\s*\[started\]\s*$/;
|
|
|
59
59
|
const BRANCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
60
60
|
// Review disagreement ledger (see .agents/rules/review-handshake.md).
|
|
61
61
|
const LEDGER_SECTION_NAMES = ["审查分歧账本", "Review Disagreement Ledger"];
|
|
62
|
-
const DEFAULT_MAX_HANDSHAKE_ROUNDS = 3;
|
|
63
62
|
const POST_REVIEW_COMMIT_STAGE = "post-review-commit";
|
|
64
63
|
const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
65
64
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
@@ -441,21 +440,6 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
|
|
|
441
440
|
return failResult("artifact", `Artifact is empty: ${path.basename(artifactPath)}`);
|
|
442
441
|
}
|
|
443
442
|
const content = fs.readFileSync(artifactPath, "utf8");
|
|
444
|
-
let taskContent = "";
|
|
445
|
-
const taskPath = path.join(taskDir, "task.md");
|
|
446
|
-
if (fs.existsSync(taskPath)) {
|
|
447
|
-
try {
|
|
448
|
-
taskContent = fs.readFileSync(taskPath, "utf8");
|
|
449
|
-
}
|
|
450
|
-
catch (error) {
|
|
451
|
-
return failResult("artifact", `Cannot read task qualification contract: ${error instanceof Error ? error.message : String(error)}`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (taskContent && /^review-(?:analysis|plan|code)$/.test(skillName)) {
|
|
455
|
-
const qualification = validateQualificationAudit(taskContent, content, { artifact: path.basename(artifactPath) });
|
|
456
|
-
if (!qualification.ok)
|
|
457
|
-
return failResult("artifact", `${path.basename(artifactPath)} has qualification audit errors: ${qualification.code}: ${qualification.message}`);
|
|
458
|
-
}
|
|
459
443
|
const schemaFamily = skillName === "analyze-task"
|
|
460
444
|
? "analysis"
|
|
461
445
|
: skillName === "review-analysis"
|
|
@@ -479,9 +463,7 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
|
|
|
479
463
|
}
|
|
480
464
|
if (localFamily) {
|
|
481
465
|
const local = validateLocalArtifact(content, {
|
|
482
|
-
family: localFamily
|
|
483
|
-
...(taskContent ? { taskContent } : {}),
|
|
484
|
-
artifact: path.basename(artifactPath)
|
|
466
|
+
family: localFamily
|
|
485
467
|
});
|
|
486
468
|
if (!local.ok) {
|
|
487
469
|
return failResult("artifact", `${path.basename(artifactPath)} has local structural errors: ${local.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}`);
|
|
@@ -502,6 +484,18 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
|
|
|
502
484
|
return failResult("artifact", `${path.basename(artifactPath)} is missing required pattern: ${pattern}`);
|
|
503
485
|
}
|
|
504
486
|
}
|
|
487
|
+
const stateCheckPatterns = config.state_check_patterns || [];
|
|
488
|
+
if (stateCheckPatterns.length > 0) {
|
|
489
|
+
const scanned = scanVisibleMarkdown(content);
|
|
490
|
+
const stateCheckHeading = scanned.headings.find((heading) => (heading.level === 2 && (heading.text === "状态核对" || heading.text === "State Check")));
|
|
491
|
+
const nextHeading = stateCheckHeading && scanned.headings.find((heading) => (heading.start > stateCheckHeading.start && heading.level <= 2));
|
|
492
|
+
const stateCheckBody = stateCheckHeading ? content.slice(stateCheckHeading.end, nextHeading?.start ?? content.length) : "";
|
|
493
|
+
for (const pattern of stateCheckPatterns) {
|
|
494
|
+
if (!new RegExp(pattern, "m").test(stateCheckBody)) {
|
|
495
|
+
return failResult("artifact", `${path.basename(artifactPath)} state-check section is missing required command: ${pattern}`);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
505
499
|
return passResult("artifact", `${path.basename(artifactPath)} passed (${requiredSections.length} sections)`);
|
|
506
500
|
}
|
|
507
501
|
function checkDecisionDetails({ taskDir, artifactFile }) {
|
|
@@ -715,11 +709,10 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }) {
|
|
|
715
709
|
return passResult("review-ledger", "Disagreement ledger has no entries");
|
|
716
710
|
}
|
|
717
711
|
const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
|
|
718
|
-
const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS, repositoryRoot));
|
|
719
712
|
const problems = [];
|
|
720
713
|
let inScopeCount = 0;
|
|
721
714
|
for (const row of rows) {
|
|
722
|
-
const { id, stage,
|
|
715
|
+
const { id, stage, status, evidence } = row;
|
|
723
716
|
const stageScoped = stageScope ? stageScope.includes(stage) : true;
|
|
724
717
|
// post-review-commit exemption rows are consumed by the post-review-commit
|
|
725
718
|
// check, not enforced here.
|
|
@@ -738,13 +731,6 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }) {
|
|
|
738
731
|
if (status !== "open" && evidence === "") {
|
|
739
732
|
problems.push(`${id}: status '${status}' requires evidence`);
|
|
740
733
|
}
|
|
741
|
-
const round = Number.parseInt(roundRaw, 10);
|
|
742
|
-
if (Number.isFinite(round) &&
|
|
743
|
-
round >= maxRounds &&
|
|
744
|
-
!LEDGER_TERMINAL.has(status) &&
|
|
745
|
-
status !== "needs-human-decision") {
|
|
746
|
-
problems.push(`${id}: round ${round} reached limit ${maxRounds} without convergence; escalate to needs-human-decision`);
|
|
747
|
-
}
|
|
748
734
|
if (!LEDGER_TERMINAL.has(status)) {
|
|
749
735
|
problems.push(`${id}: unresolved (status '${status}')`);
|
|
750
736
|
}
|
|
@@ -1051,7 +1037,8 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }) {
|
|
|
1051
1037
|
let targetHead;
|
|
1052
1038
|
let diffBase;
|
|
1053
1039
|
try {
|
|
1054
|
-
|
|
1040
|
+
const taskRepositoryRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
1041
|
+
gitRoot = reviewWorktreeForTask(taskRepositoryRoot, String(task.metadata.branch || "").trim()) ?? taskRepositoryRoot;
|
|
1055
1042
|
head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
1056
1043
|
const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
|
|
1057
1044
|
if (!target.ok)
|
|
@@ -1101,6 +1088,14 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }) {
|
|
|
1101
1088
|
}
|
|
1102
1089
|
return passResult("review-fact", `Review fact valid for ${path.basename(resolvedArtifact.path)} at ${baseline.slice(0, 8)}`);
|
|
1103
1090
|
}
|
|
1091
|
+
function reviewWorktreeForTask(repositoryRoot, branch) {
|
|
1092
|
+
if (!branch)
|
|
1093
|
+
return null;
|
|
1094
|
+
const worktree = resolveBranchWorktree(repositoryRoot, branch);
|
|
1095
|
+
if (worktree)
|
|
1096
|
+
return worktree;
|
|
1097
|
+
throw new Error(`Task branch '${branch}' is not checked out in a registered worktree`);
|
|
1098
|
+
}
|
|
1104
1099
|
function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit, reviewArtifact }) {
|
|
1105
1100
|
if (SHA_PATTERN.test(lastReviewedCommit) && gitCommitExists(gitRoot, lastReviewedCommit)) {
|
|
1106
1101
|
return { ok: true, sha: lastReviewedCommit };
|
|
@@ -4,7 +4,6 @@ import path from 'node:path';
|
|
|
4
4
|
import { verifyInProcess } from './verification-engine.js';
|
|
5
5
|
import { parseArtifactName } from './artifact-name.js';
|
|
6
6
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
7
|
-
import { allowsManualOverride } from './guard-override.js';
|
|
8
7
|
const gate = (skill, expectedState, artifactFamily) => ({
|
|
9
8
|
skill, expectedState, mode: 'gate', ...(artifactFamily ? { artifactFamily } : {})
|
|
10
9
|
});
|
|
@@ -23,6 +22,7 @@ const VERIFICATION_CATALOG = {
|
|
|
23
22
|
'commit.completed': gate('commit', 'active'),
|
|
24
23
|
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
25
24
|
'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
25
|
+
'complete-task.prepared': gate('complete-task', 'active'),
|
|
26
26
|
'complete-task.completed': gate('complete-task', 'completed'),
|
|
27
27
|
'create-pr.completed': gate('create-pr', 'active'),
|
|
28
28
|
'create-task.completed': gate('create-task', 'active'),
|
|
@@ -49,7 +49,7 @@ async function verifyTaskEvent(request, options = {}) {
|
|
|
49
49
|
if (!resolved.ok)
|
|
50
50
|
return failure(request, resolved.code, resolved.message, { taskId: resolved.taskId, skill: spec.skill, mode: spec.mode });
|
|
51
51
|
const identity = { taskId: resolved.taskId, taskDir: resolved.taskDir, taskState: resolved.state, skill: spec.skill, mode: spec.mode };
|
|
52
|
-
if (resolved.state !== spec.expectedState
|
|
52
|
+
if (resolved.state !== spec.expectedState) {
|
|
53
53
|
return failure(request, 'VERIFY_TASK_STATE_MISMATCH', `event '${request.event}' requires workspace '${spec.expectedState}', received '${resolved.state}'`, identity);
|
|
54
54
|
}
|
|
55
55
|
if (spec.artifactFamily) {
|
|
@@ -68,23 +68,12 @@ async function verifyTaskEvent(request, options = {}) {
|
|
|
68
68
|
if (spec.mode === 'gate') {
|
|
69
69
|
const payload = await engine({ mode: 'gate', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [], repositoryRoot: resolved.repoRoot });
|
|
70
70
|
const status = payload.gate;
|
|
71
|
-
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
72
|
-
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, gate: 'pass', humanOverride: 'human-approved', originalGate: status } });
|
|
73
|
-
return {
|
|
74
|
-
status: 'pass', changed: false, event: request.event, requestRef: request.taskRef,
|
|
75
|
-
...identity, artifact: request.artifact ?? null, invocations, error: null
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
71
|
invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
|
|
79
72
|
}
|
|
80
73
|
else {
|
|
81
74
|
for (const check of spec.checks ?? []) {
|
|
82
75
|
const payload = await engine({ mode: 'checks', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [check], repositoryRoot: resolved.repoRoot });
|
|
83
76
|
const status = (payload.effectiveStatus ?? payload.status);
|
|
84
|
-
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
85
|
-
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, status: 'pass', humanOverride: 'human-approved', originalStatus: status } });
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
77
|
invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
|
|
89
78
|
if (status !== 'pass')
|
|
90
79
|
break;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
3
|
+
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
3
4
|
import { WORKFLOW_WARNING_COLUMNS, WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_SEVERITIES, WORKFLOW_WARNING_STATUSES, parseWorkflowWarnings } from './workflow-warnings.js';
|
|
4
5
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
5
|
-
import { allowsManualOverride } from './guard-override.js';
|
|
6
6
|
import { retryHintForWarning } from './operation-outcome.js';
|
|
7
7
|
function failed(intent, code, message, taskId = null, entityId = null) {
|
|
8
8
|
return { status: 'failed', changed: false, intent: intent.kind, taskId, entityId, before: null, after: null, warnings: [], operations: [], error: { code, message } };
|
|
@@ -43,8 +43,7 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
43
43
|
const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
|
|
44
44
|
if (!resolved.ok)
|
|
45
45
|
return failed(intent, resolved.code, resolved.message, resolved.taskId);
|
|
46
|
-
|
|
47
|
-
if (resolved.state !== 'active' && !stateOverride)
|
|
46
|
+
if (resolved.state !== 'active')
|
|
48
47
|
return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
49
48
|
let content;
|
|
50
49
|
let rows;
|
|
@@ -101,7 +100,7 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
101
100
|
after = { ...before, status: intent.status, resolvedAt: metadata.timestamp, resolution };
|
|
102
101
|
}
|
|
103
102
|
const writeResult = writeTask({
|
|
104
|
-
taskRef: intent.taskRef, expectedState:
|
|
103
|
+
taskRef: intent.taskRef, expectedState: 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
|
|
105
104
|
}, {
|
|
106
105
|
...options,
|
|
107
106
|
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
|
@@ -118,8 +117,18 @@ function projectFinalizationWarning(taskRef, warning, options = {}) {
|
|
|
118
117
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
119
118
|
if (!resolved.ok)
|
|
120
119
|
return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
let prepared = resolved.state === 'completed';
|
|
121
|
+
if (!prepared && resolved.state === 'active') {
|
|
122
|
+
try {
|
|
123
|
+
prepared = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8')).status === 'completed';
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
prepared = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (!prepared) {
|
|
130
|
+
return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected a prepared or completed task` } };
|
|
131
|
+
}
|
|
123
132
|
let content;
|
|
124
133
|
let rows;
|
|
125
134
|
try {
|
|
@@ -139,23 +148,27 @@ function projectFinalizationWarning(taskRef, warning, options = {}) {
|
|
|
139
148
|
message: warning.message,
|
|
140
149
|
action: retryHintForWarning(warning),
|
|
141
150
|
resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
|
|
142
|
-
resolution: warning.status === 'resolved'
|
|
151
|
+
resolution: warning.status === 'resolved'
|
|
152
|
+
? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
|
|
153
|
+
: ''
|
|
143
154
|
}
|
|
144
155
|
: {
|
|
145
156
|
id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
|
|
146
157
|
severity: warning.severity, code: warning.code, status: warning.status,
|
|
147
158
|
target: warning.target, message: warning.message, action: retryHintForWarning(warning),
|
|
148
|
-
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved'
|
|
159
|
+
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved'
|
|
160
|
+
? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
|
|
161
|
+
: ''
|
|
149
162
|
};
|
|
150
163
|
if (existing && JSON.stringify(existing) === JSON.stringify(after))
|
|
151
164
|
return { status: 'no-op', changed: false, error: null };
|
|
152
165
|
const written = writeTask({
|
|
153
166
|
taskRef: resolved.taskId,
|
|
154
|
-
expectedState:
|
|
167
|
+
expectedState: resolved.state,
|
|
155
168
|
mutations: [...sectionMutation(content), rowMutation(after)]
|
|
156
169
|
}, {
|
|
157
170
|
...options,
|
|
158
|
-
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state:
|
|
171
|
+
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
|
159
172
|
metadataProvider: () => metadata
|
|
160
173
|
});
|
|
161
174
|
if (written.status === 'failed')
|
|
@@ -15,7 +15,12 @@ function captureWorktreeTree(repoRoot, forcedPath) {
|
|
|
15
15
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-orchestration-index-'));
|
|
16
16
|
const indexFile = path.join(tempDir, 'index');
|
|
17
17
|
try {
|
|
18
|
-
|
|
18
|
+
try {
|
|
19
|
+
git(repoRoot, indexFile, ['read-tree', 'HEAD']);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
git(repoRoot, indexFile, ['read-tree', '--empty']);
|
|
23
|
+
}
|
|
19
24
|
git(repoRoot, indexFile, ['add', '-A', '--', ':/']);
|
|
20
25
|
if (forcedPath && fs.existsSync(path.join(repoRoot, forcedPath))) {
|
|
21
26
|
git(repoRoot, indexFile, ['add', '-f', '--', forcedPath]);
|
|
@@ -111,5 +116,5 @@ function diffWorkspaceSnapshots(repoRoot, before, after) {
|
|
|
111
116
|
}
|
|
112
117
|
return [...changed].sort();
|
|
113
118
|
}
|
|
114
|
-
export { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots };
|
|
119
|
+
export { captureRepositorySnapshot, captureWorkspaceSnapshot, captureWorktreeTree, diffWorkspaceSnapshots };
|
|
115
120
|
//# sourceMappingURL=workspace-snapshot.js.map
|
package/dist/lib/task/write.js
CHANGED
|
@@ -3,7 +3,6 @@ import path from 'node:path';
|
|
|
3
3
|
import { isValidAgentInfraVersion, VERSION } from '../version.js';
|
|
4
4
|
import { parseTypedTaskFrontmatter, updateTaskFrontmatter } from './frontmatter.js';
|
|
5
5
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
|
-
import { allowsManualOverride } from './guard-override.js';
|
|
7
6
|
import { mutateTableRow, upsertSection } from './sections.js';
|
|
8
7
|
import { validateCurrentTaskContract } from './current-contract.js';
|
|
9
8
|
import { invalidationBlocks, parseInvalidationDocument } from './invalidation.js';
|
|
@@ -73,7 +72,7 @@ function writeTask(request, options = {}) {
|
|
|
73
72
|
taskMdPath: resolved.taskMdPath,
|
|
74
73
|
actualState: resolved.state
|
|
75
74
|
};
|
|
76
|
-
if (resolved.state !== request.expectedState
|
|
75
|
+
if (resolved.state !== request.expectedState) {
|
|
77
76
|
return failure(request, identity, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected ${request.expectedState}`);
|
|
78
77
|
}
|
|
79
78
|
const io = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.11.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.11.5",
|
|
4
|
+
"sourceInputHash": "2f29b7f1f134733c3bc1bb7c92ea6c11e57462ed51a5a96b0198b8824a3f455d",
|
|
5
|
+
"internalExecutableBuildHash": "cb9d00aa3fa974cf936a8aad310e1fa5f28842351f52a53fed43345fe9c46439"
|
|
6
6
|
}
|
package/lib/decide.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { VERSION } from './version.ts';
|
|
|
2
2
|
import { parseTaskScope } from './task/command-options.ts';
|
|
3
3
|
import { applyHumanDecision } from './task/decision-intents.ts';
|
|
4
4
|
import { canonicalTimestamp } from './task/write.ts';
|
|
5
|
-
import { consumeHumanOverride, failureId } from './task/human-override.ts';
|
|
6
5
|
import { resolveTaskContext } from './task/resolve-ref.ts';
|
|
7
6
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task/task-execution-lock.ts';
|
|
8
7
|
|
|
@@ -12,12 +11,9 @@ type DecideOptions = {
|
|
|
12
11
|
version?: string;
|
|
13
12
|
};
|
|
14
13
|
|
|
15
|
-
function parseDecisionParts(parts: string[]): { decision: string; needsImplementation: boolean | undefined
|
|
14
|
+
function parseDecisionParts(parts: string[]): { decision: string; needsImplementation: boolean | undefined } {
|
|
16
15
|
const decision: string[] = [];
|
|
17
16
|
let needsImplementation: boolean | undefined;
|
|
18
|
-
let overrideTicket: string | undefined;
|
|
19
|
-
let overrideTarget: string | undefined;
|
|
20
|
-
let overrideScope: string | undefined;
|
|
21
17
|
for (let index = 0; index < parts.length; index += 1) {
|
|
22
18
|
const part = parts[index];
|
|
23
19
|
if (part === '--needs-implementation') {
|
|
@@ -27,18 +23,14 @@ function parseDecisionParts(parts: string[]): { decision: string; needsImplement
|
|
|
27
23
|
throw new Error("--needs-implementation must be 'true' or 'false'");
|
|
28
24
|
}
|
|
29
25
|
needsImplementation = value === 'true';
|
|
30
|
-
} else if (part
|
|
31
|
-
|
|
32
|
-
if (!value) throw new Error(`${part} requires a value`);
|
|
33
|
-
if (part === '--override-ticket') overrideTicket = value;
|
|
34
|
-
else if (part === '--override-target') overrideTarget = value;
|
|
35
|
-
else overrideScope = value;
|
|
26
|
+
} else if (part?.startsWith('--')) {
|
|
27
|
+
throw new Error(`unknown option '${part}'`);
|
|
36
28
|
} else {
|
|
37
29
|
decision.push(parts[index]!);
|
|
38
30
|
}
|
|
39
31
|
}
|
|
40
32
|
if (decision.length === 0) throw new Error('decision content is required');
|
|
41
|
-
return { decision: decision.join(' '), needsImplementation
|
|
33
|
+
return { decision: decision.join(' '), needsImplementation };
|
|
42
34
|
}
|
|
43
35
|
|
|
44
36
|
export async function decide(args: string[], options: DecideOptions = {}): Promise<number> {
|
|
@@ -79,29 +71,7 @@ export async function decide(args: string[], options: DecideOptions = {}): Promi
|
|
|
79
71
|
metadataProvider: () => ({ timestamp: now, agentInfraVersion: options.version ?? VERSION })
|
|
80
72
|
};
|
|
81
73
|
let result;
|
|
82
|
-
const execute = async () =>
|
|
83
|
-
let current = applyHumanDecision(request, writeOptions);
|
|
84
|
-
if (current.status !== 'failed' || !parsedDecision.overrideTicket) return current;
|
|
85
|
-
if (!parsedDecision.overrideTarget || !parsedDecision.overrideScope) throw new Error('override ticket requires --override-target and --override-scope');
|
|
86
|
-
const consumed = await consumeHumanOverride({
|
|
87
|
-
taskRef: resolved.taskId,
|
|
88
|
-
ticketId: parsedDecision.overrideTicket,
|
|
89
|
-
failureId: failureId('decision-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
|
|
90
|
-
target: parsedDecision.overrideTarget,
|
|
91
|
-
scope: parsedDecision.overrideScope
|
|
92
|
-
}, {
|
|
93
|
-
...writeOptions,
|
|
94
|
-
effectExecutor: (capability) => {
|
|
95
|
-
const retried = applyHumanDecision(request, { ...writeOptions, manualOverride: capability });
|
|
96
|
-
current = retried;
|
|
97
|
-
return retried.status === 'failed' || retried.status === 'planned'
|
|
98
|
-
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no decision effect was committed' : `${retried.error?.code ?? 'DECISION_FAILED'}: ${retried.error?.message ?? 'manual decision effect failed'}` }
|
|
99
|
-
: null;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
if (consumed.status === 'failed') throw new Error(consumed.error.message);
|
|
103
|
-
return current;
|
|
104
|
-
};
|
|
74
|
+
const execute = async () => applyHumanDecision(request, writeOptions);
|
|
105
75
|
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-decision', execute);
|
|
106
76
|
if (result.error) throw new Error(result.error.message);
|
|
107
77
|
return 0;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
function gitText(repositoryRoot: string, args: string[]): string | null {
|
|
4
|
+
try {
|
|
5
|
+
return execFileSync('git', ['-C', repositoryRoot, ...args], {
|
|
6
|
+
encoding: 'utf8',
|
|
7
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
8
|
+
}).trim();
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function verifiedWorktreeRoot(candidate: string, expectedBranch: string, expectedCommonDirectory: string): string | null {
|
|
15
|
+
const commonDirectory = gitText(candidate, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
|
|
16
|
+
if (commonDirectory !== expectedCommonDirectory) return null;
|
|
17
|
+
const root = gitText(candidate, ['rev-parse', '--show-toplevel']);
|
|
18
|
+
if (!root) return null;
|
|
19
|
+
const branch = gitText(root, ['symbolic-ref', '--short', 'HEAD']);
|
|
20
|
+
return branch === expectedBranch ? root : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resolveBranchWorktree(repositoryRoot: string, expectedBranch: string): string | null {
|
|
24
|
+
const commonDirectory = gitText(repositoryRoot, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
|
|
25
|
+
if (!commonDirectory) return null;
|
|
26
|
+
const currentRoot = verifiedWorktreeRoot(repositoryRoot, expectedBranch, commonDirectory);
|
|
27
|
+
if (currentRoot) return currentRoot;
|
|
28
|
+
|
|
29
|
+
const records = gitText(repositoryRoot, ['worktree', 'list', '--porcelain']);
|
|
30
|
+
if (!records) return null;
|
|
31
|
+
const expectedRef = `refs/heads/${expectedBranch}`;
|
|
32
|
+
for (const record of records.split(/\r?\n\r?\n/)) {
|
|
33
|
+
const candidate = /^worktree (.+)$/m.exec(record)?.[1];
|
|
34
|
+
const branch = /^branch (.+)$/m.exec(record)?.[1];
|
|
35
|
+
if (candidate && branch === expectedRef) {
|
|
36
|
+
const verifiedRoot = verifiedWorktreeRoot(candidate, expectedBranch, commonDirectory);
|
|
37
|
+
if (verifiedRoot) return verifiedRoot;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
@@ -53,7 +53,7 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
53
53
|
'pr-review-grade': ['decide', 'resolve-host', 'verify-artifact'],
|
|
54
54
|
'platform-checks': ['inspect', 'watch', 'resolve-run', 'logs'],
|
|
55
55
|
'task-context': ['resolve'],
|
|
56
|
-
'task-ledger': ['decision-next-id', 'stage-status', 'finding-upsert', 'finding-respond', 'finding-review', 'decision-upsert', 'rework-intent-upsert'],
|
|
56
|
+
'task-ledger': ['decision-next-id', 'stage-status', 'finding-upsert', 'finding-respond', 'finding-review', 'decision-upsert', 'rework-intent-upsert', 'rework-intent-rebuild'],
|
|
57
57
|
'task-warning': ['list', 'add', 'set-status'],
|
|
58
58
|
'task-activity': ['pr-review-inspect', 'pr-review-start', 'pr-review-complete', 'pr-review-terminate'],
|
|
59
59
|
'task-artifact': ['inspect', 'init', 'preflight', 'finalize-local'],
|
|
@@ -61,7 +61,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
61
61
|
'task-review': ['preflight', 'finalize-summary'],
|
|
62
62
|
'task-event': ['event'],
|
|
63
63
|
'task-invalidation': ['reconcile'],
|
|
64
|
-
'task-override': ['diagnose', 'issue', 'consume'],
|
|
65
64
|
'task-lifecycle': ['intent'],
|
|
66
65
|
'task-finalization': ['complete'],
|
|
67
66
|
'task-short-id': ['list', 'list-verify', 'alloc', 'release', 'resolve'],
|
|
@@ -98,7 +97,6 @@ export function internalRouteSelector(command: string, args: readonly string[]):
|
|
|
98
97
|
if (command === 'task-orchestration') return args[1] === 'status' ? 'status' : args[1] ? 'progress' : '';
|
|
99
98
|
if (command === 'task-review') return args[1] ?? '';
|
|
100
99
|
if (command === 'task-invalidation') return args[1] ?? '';
|
|
101
|
-
if (command === 'task-override') return args[1] ?? '';
|
|
102
100
|
if (command === 'task-artifact') return args[1] ?? '';
|
|
103
101
|
if (command === 'platform-pr-review' && first(args) === 'publish') {
|
|
104
102
|
const scope = optionValue(args, '--scope') ?? '';
|
|
@@ -6,7 +6,6 @@ import type {
|
|
|
6
6
|
PrReviewActivityIntent,
|
|
7
7
|
PrReviewInspectIntent
|
|
8
8
|
} from '../task/activity-intent.ts';
|
|
9
|
-
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.ts';
|
|
10
9
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
11
10
|
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
|
|
12
11
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
@@ -28,14 +27,13 @@ const FLAGS: Record<string, string> = {
|
|
|
28
27
|
'--minor': 'minor',
|
|
29
28
|
'--outcome': 'outcome',
|
|
30
29
|
'--reason': 'reason'
|
|
31
|
-
, '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
|
|
32
30
|
};
|
|
33
31
|
const COMMON = ['agent', 'artifact', 'head'] as const;
|
|
34
32
|
const ALLOWED: Record<string, ReadonlySet<string>> = {
|
|
35
33
|
'pr-review-inspect': new Set(['kind', 'taskRef']),
|
|
36
|
-
'pr-review-start': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'dryRun'
|
|
37
|
-
'pr-review-complete': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'verdict', 'blockers', 'major', 'minor', 'dryRun'
|
|
38
|
-
'pr-review-terminate': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'outcome', 'reason', 'dryRun'
|
|
34
|
+
'pr-review-start': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'dryRun']),
|
|
35
|
+
'pr-review-complete': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'verdict', 'blockers', 'major', 'minor', 'dryRun']),
|
|
36
|
+
'pr-review-terminate': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'outcome', 'reason', 'dryRun'])
|
|
39
37
|
};
|
|
40
38
|
|
|
41
39
|
function usageFailure(message: string): void {
|
|
@@ -95,45 +93,19 @@ async function taskActivity(args: string[] = []): Promise<void> {
|
|
|
95
93
|
for (const key of operationRequired) {
|
|
96
94
|
if (values[key] === undefined) { usageFailure(`${kind} requires '${key}'`); return; }
|
|
97
95
|
}
|
|
98
|
-
const dryRunConflict = overrideDryRunConflict(values);
|
|
99
|
-
if (dryRunConflict) { usageFailure(dryRunConflict.message); return; }
|
|
100
96
|
const resolved = resolveTaskRef(taskRef);
|
|
101
97
|
if (!resolved.ok) { usageFailure(resolved.message); return; }
|
|
102
98
|
let result;
|
|
103
|
-
let humanOverride: unknown = null;
|
|
104
99
|
try {
|
|
105
|
-
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-activity.${kind}`, async () => {
|
|
106
|
-
let current = applyPrReviewActivityIntent(values as PrReviewActivityIntent, { lockAlreadyHeld: true });
|
|
107
|
-
if (current.status !== 'failed' || !values.overrideTicket) return current;
|
|
108
|
-
if (!values.overrideTarget || !values.overrideScope) { usageFailure('override ticket requires target and scope'); return current; }
|
|
109
|
-
const consumed = await consumeHumanOverride({
|
|
110
|
-
taskRef, ticketId: String(values.overrideTicket),
|
|
111
|
-
failureId: failureId('activity-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
|
|
112
|
-
target: String(values.overrideTarget), scope: String(values.overrideScope)
|
|
113
|
-
}, {
|
|
114
|
-
effectExecutor: (capability) => {
|
|
115
|
-
const retried = applyPrReviewActivityIntent(values as PrReviewActivityIntent, { lockAlreadyHeld: true, manualOverride: capability });
|
|
116
|
-
current = retried;
|
|
117
|
-
return retried.status === 'failed' || retried.status === 'planned'
|
|
118
|
-
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no activity effect was committed' : `${retried.error?.code ?? 'ACTIVITY_FAILED'}: ${retried.error?.message ?? 'manual activity effect failed'}` }
|
|
119
|
-
: null;
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
humanOverride = consumed;
|
|
123
|
-
return current;
|
|
124
|
-
});
|
|
100
|
+
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-activity.${kind}`, async () => applyPrReviewActivityIntent(values as PrReviewActivityIntent, { lockAlreadyHeld: true }));
|
|
125
101
|
} catch (error) {
|
|
126
102
|
if (!(error instanceof TaskExecutionLockError)) throw error;
|
|
127
103
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
|
|
128
104
|
process.exitCode = 1;
|
|
129
105
|
return;
|
|
130
106
|
}
|
|
131
|
-
process.stdout.write(`${JSON.stringify(
|
|
132
|
-
|
|
133
|
-
humanOverride && typeof humanOverride === 'object' &&
|
|
134
|
-
(humanOverride as { status?: unknown }).status === 'failed'
|
|
135
|
-
);
|
|
136
|
-
if (result.status === 'failed' || overrideFailed) process.exitCode = 1;
|
|
107
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
108
|
+
if (result.status === 'failed') process.exitCode = 1;
|
|
137
109
|
}
|
|
138
110
|
|
|
139
111
|
export { taskActivity };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseArtifactCommand, executeArtifactCommand } from '../task/artifact-command.ts';
|
|
2
2
|
import { ensureInternalHandlerRoute } from './cli-route-inventory.ts';
|
|
3
3
|
|
|
4
|
-
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family
|
|
4
|
+
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family> [--source-finding <id> --source-artifact <name> --source-sha256 <digest>]\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact>\n\nInspect, initialize, preflight, or finalize workflow artifact facts.\n`;
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
function taskArtifact(args: string[] = []): void {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
|
|
2
2
|
import { applyTaskEvent, eventCatalog } from '../task/events.ts';
|
|
3
3
|
import type { TaskEventRequest, Verdict } from '../task/events.ts';
|
|
4
|
-
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.ts';
|
|
5
4
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
6
5
|
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
|
|
7
6
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
@@ -21,8 +20,7 @@ const FLAGS: Record<string, keyof TaskEventRequest> = {
|
|
|
21
20
|
'--verdict': 'verdict', '--blockers': 'blockers', '--major': 'major',
|
|
22
21
|
'--minor': 'minor', '--manual-validation': 'manualValidation', '--files-modified': 'filesModified',
|
|
23
22
|
'--tests-passed': 'testsPassed', '--summary-result': 'summaryResult',
|
|
24
|
-
'--transaction-id': 'transactionId', '--receipt-digest': 'receiptDigest', '--pr-head-sha': 'prHeadSha'
|
|
25
|
-
'--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
|
|
23
|
+
'--transaction-id': 'transactionId', '--receipt-digest': 'receiptDigest', '--pr-head-sha': 'prHeadSha'
|
|
26
24
|
};
|
|
27
25
|
const NUMERIC = new Set(['round', 'question', 'blockers', 'major', 'minor', 'manualValidation', 'filesModified', 'testsPassed']);
|
|
28
26
|
|
|
@@ -59,8 +57,6 @@ export function parseTaskEventRequest(args: readonly string[]): TaskEventRequest
|
|
|
59
57
|
const agent = normalizeAgentToken(String(request.agent ?? ''));
|
|
60
58
|
if (!agent) throw new Error(`invalid --agent '${request.agent}': ${AGENT_USAGE_HINT}`);
|
|
61
59
|
request.agent = agent;
|
|
62
|
-
const dryRunConflict = overrideDryRunConflict(request as unknown as Record<string, unknown>);
|
|
63
|
-
if (dryRunConflict) throw new Error(dryRunConflict.message);
|
|
64
60
|
return request;
|
|
65
61
|
}
|
|
66
62
|
|
|
@@ -78,45 +74,16 @@ async function taskEvent(args: string[] = []): Promise<void> {
|
|
|
78
74
|
return;
|
|
79
75
|
}
|
|
80
76
|
let result;
|
|
81
|
-
let humanOverride: unknown = null;
|
|
82
77
|
try {
|
|
83
|
-
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, async () => {
|
|
84
|
-
let current = applyTaskEvent(request, { lockAlreadyHeld: true });
|
|
85
|
-
const values = request as Record<string, unknown>;
|
|
86
|
-
if (current.status !== 'failed' || !values.overrideTicket) return current;
|
|
87
|
-
if (!values.overrideTarget || !values.overrideScope) {
|
|
88
|
-
return { ...current, humanOverride: { status: 'failed', error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires target and scope' } } } as typeof current & { humanOverride: unknown };
|
|
89
|
-
}
|
|
90
|
-
const consumed = await consumeHumanOverride({
|
|
91
|
-
taskRef: request.taskRef,
|
|
92
|
-
ticketId: String(values.overrideTicket),
|
|
93
|
-
failureId: failureId('task-event', current.error?.code ?? 'EVENT_TRANSITION_INVALID'),
|
|
94
|
-
target: String(values.overrideTarget),
|
|
95
|
-
scope: String(values.overrideScope)
|
|
96
|
-
}, {
|
|
97
|
-
effectExecutor: (capability) => {
|
|
98
|
-
const retried = applyTaskEvent(request, { lockAlreadyHeld: true, manualOverride: capability });
|
|
99
|
-
current = retried;
|
|
100
|
-
return retried.status === 'failed' || retried.status === 'planned'
|
|
101
|
-
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no task event was committed' : `${retried.error?.code ?? 'EVENT_FAILED'}: ${retried.error?.message ?? 'manual task event effect failed'}` }
|
|
102
|
-
: null;
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
humanOverride = consumed;
|
|
106
|
-
return current;
|
|
107
|
-
});
|
|
78
|
+
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, async () => applyTaskEvent(request, { lockAlreadyHeld: true }));
|
|
108
79
|
} catch (error) {
|
|
109
80
|
if (!(error instanceof TaskExecutionLockError)) throw error;
|
|
110
81
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
|
|
111
82
|
process.exitCode = 1;
|
|
112
83
|
return;
|
|
113
84
|
}
|
|
114
|
-
process.stdout.write(`${JSON.stringify(
|
|
115
|
-
|
|
116
|
-
humanOverride && typeof humanOverride === 'object' &&
|
|
117
|
-
(humanOverride as { status?: unknown }).status === 'failed'
|
|
118
|
-
);
|
|
119
|
-
if (result.status === 'failed' || overrideFailed) process.exitCode = 1;
|
|
85
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
86
|
+
if (result.status === 'failed') process.exitCode = 1;
|
|
120
87
|
}
|
|
121
88
|
|
|
122
89
|
export { taskEvent };
|