@fitlab-ai/agent-infra 0.9.7 → 0.9.9
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +11 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
- package/dist/lib/decide.js +68 -14
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +19 -5
- package/dist/lib/internal/codex-sandbox-controller.js +6 -13
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +3 -4
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-ledger.js +59 -5
- package/dist/lib/internal/task-lifecycle.js +59 -8
- package/dist/lib/internal/task-orchestration.js +161 -288
- package/dist/lib/internal/task-override.js +131 -0
- package/dist/lib/internal/task-review.js +64 -6
- package/dist/lib/internal/task-verify.js +65 -3
- package/dist/lib/internal/task-warning.js +57 -4
- package/dist/lib/platform/pr-summary.js +53 -13
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +389 -311
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/container-identity.js +1 -1
- package/dist/lib/sandbox/control/controller-registration.js +273 -0
- package/dist/lib/sandbox/control/executor.js +145 -5
- package/dist/lib/sandbox/control/lifecycle.js +395 -73
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/control/state.js +27 -11
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/mounts.js +20 -5
- package/dist/lib/sandbox/recovery.js +78 -10
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/workspace-view.js +62 -40
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/server/process-state.js +51 -1
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/codex-orchestration.js +158 -33
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +279 -17
- package/dist/lib/task/events.js +9 -5
- package/dist/lib/task/finalization.js +567 -0
- package/dist/lib/task/guard-override.js +31 -0
- package/dist/lib/task/human-override.js +1240 -0
- package/dist/lib/task/ledger-intents.js +6 -3
- package/dist/lib/task/lifecycle.js +69 -14
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +199 -719
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +138 -59
- package/dist/lib/task/verification.js +15 -2
- package/dist/lib/task/workflow-warning-intents.js +54 -3
- package/dist/lib/task/write.js +2 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +63 -20
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
- package/lib/decide.ts +59 -14
- package/lib/git/workflow.ts +11 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +18 -5
- package/lib/internal/codex-sandbox-controller.ts +6 -12
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +3 -4
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +32 -107
- package/lib/internal/task-override.ts +138 -0
- package/lib/internal/task-review.ts +51 -6
- package/lib/internal/task-verify.ts +52 -3
- package/lib/internal/task-warning.ts +44 -3
- package/lib/platform/pr-summary.ts +65 -14
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +88 -4
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/container-identity.ts +3 -3
- package/lib/sandbox/control/controller-registration.ts +347 -0
- package/lib/sandbox/control/executor.ts +172 -6
- package/lib/sandbox/control/lifecycle.ts +465 -77
- package/lib/sandbox/control/protocol.ts +85 -9
- package/lib/sandbox/control/state.ts +35 -18
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/mounts.ts +23 -5
- package/lib/sandbox/recovery.ts +90 -9
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +73 -43
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/server/process-state.ts +48 -1
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/codex-orchestration.ts +167 -33
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +304 -19
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +719 -0
- package/lib/task/guard-override.ts +50 -0
- package/lib/task/human-override.ts +1503 -0
- package/lib/task/ledger-intents.ts +6 -3
- package/lib/task/lifecycle.ts +71 -14
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +239 -922
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +145 -64
- package/lib/task/verification.ts +18 -3
- package/lib/task/workflow-warning-intents.ts +61 -4
- package/lib/task/write.ts +4 -1
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { finalizeReviewSummary } from '../task/review-finalization.js';
|
|
2
|
-
|
|
2
|
+
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
|
|
3
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> finalize-summary --stage <analysis|plan|code> --artifact <review-*.md> [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
3
6
|
function failUsage(message) {
|
|
4
7
|
process.stdout.write(`${JSON.stringify({
|
|
5
8
|
status: 'failed',
|
|
@@ -27,10 +30,13 @@ function taskReview(args = []) {
|
|
|
27
30
|
let artifact = '';
|
|
28
31
|
let dryRun = false;
|
|
29
32
|
let orchestrated = false;
|
|
33
|
+
let overrideTicket = '';
|
|
34
|
+
let overrideTarget = '';
|
|
35
|
+
let overrideScope = '';
|
|
30
36
|
const seen = new Set();
|
|
31
37
|
for (let index = 2; index < args.length; index += 1) {
|
|
32
38
|
const flag = args[index];
|
|
33
|
-
if (!['--stage', '--artifact', '--orchestrated', '--dry-run'].includes(flag)) {
|
|
39
|
+
if (!['--stage', '--artifact', '--orchestrated', '--dry-run', '--override-ticket', '--override-target', '--override-scope'].includes(flag)) {
|
|
34
40
|
failUsage(`unknown option '${flag}'`);
|
|
35
41
|
return;
|
|
36
42
|
}
|
|
@@ -54,16 +60,68 @@ function taskReview(args = []) {
|
|
|
54
60
|
}
|
|
55
61
|
if (flag === '--stage')
|
|
56
62
|
stage = value;
|
|
57
|
-
else
|
|
63
|
+
else if (flag === '--artifact')
|
|
58
64
|
artifact = value;
|
|
65
|
+
else if (flag === '--override-ticket')
|
|
66
|
+
overrideTicket = value;
|
|
67
|
+
else if (flag === '--override-target')
|
|
68
|
+
overrideTarget = value;
|
|
69
|
+
else if (flag === '--override-scope')
|
|
70
|
+
overrideScope = value;
|
|
59
71
|
}
|
|
60
72
|
if (!stage || !artifact) {
|
|
61
73
|
failUsage("options '--stage' and '--artifact' are required");
|
|
62
74
|
return;
|
|
63
75
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
const dryRunConflict = overrideDryRunConflict({ dryRun, overrideTicket, overrideTarget, overrideScope });
|
|
77
|
+
if (dryRunConflict) {
|
|
78
|
+
failUsage(dryRunConflict.message);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const resolved = resolveTaskRef(args[0]);
|
|
82
|
+
if (!resolved.ok) {
|
|
83
|
+
failUsage(resolved.message);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let result;
|
|
87
|
+
let humanOverride = null;
|
|
88
|
+
try {
|
|
89
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-review.finalize-summary', () => {
|
|
90
|
+
let current = finalizeReviewSummary({ taskRef: args[0], stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true });
|
|
91
|
+
if (current.status !== 'failed' || !overrideTicket)
|
|
92
|
+
return current;
|
|
93
|
+
if (!overrideTarget || !overrideScope) {
|
|
94
|
+
failUsage('override ticket requires target and scope');
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
const consumed = consumeHumanOverride({
|
|
98
|
+
taskRef: args[0], ticketId: overrideTicket,
|
|
99
|
+
failureId: failureId('review-finalization', current.error?.code ?? 'TASK_STATE_MISMATCH'),
|
|
100
|
+
target: overrideTarget, scope: overrideScope
|
|
101
|
+
}, {
|
|
102
|
+
effectExecutor: (capability) => {
|
|
103
|
+
const retried = finalizeReviewSummary({ taskRef: args[0], stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true, manualOverride: capability });
|
|
104
|
+
current = retried;
|
|
105
|
+
return retried.status === 'failed' || retried.status === 'planned'
|
|
106
|
+
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no review effect was committed' : `${retried.error?.code ?? 'REVIEW_FAILED'}: ${retried.error?.message ?? 'manual review effect failed'}` }
|
|
107
|
+
: null;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
humanOverride = consumed;
|
|
111
|
+
return current;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
116
|
+
throw error;
|
|
117
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
|
|
122
|
+
const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
|
|
123
|
+
humanOverride.status === 'failed');
|
|
124
|
+
if (result.status === 'failed' || overrideFailed)
|
|
67
125
|
process.exitCode = 1;
|
|
68
126
|
}
|
|
69
127
|
export { taskReview };
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { renderTaskVerification, verifyTaskEvent } from '../task/verification.js';
|
|
2
|
-
|
|
2
|
+
import { consumeHumanOverride, failureId } from '../task/human-override.js';
|
|
3
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-verify <N | TASK-id> <verification-event> [--artifact <canonical.md>] [--format json|text] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
3
6
|
function fail(message) {
|
|
4
7
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'VERIFY_PAYLOAD_INVALID', message } })}\n`);
|
|
5
8
|
process.stderr.write(USAGE);
|
|
@@ -14,12 +17,17 @@ function taskVerify(args = []) {
|
|
|
14
17
|
fail('task ref and verification event are required');
|
|
15
18
|
return;
|
|
16
19
|
}
|
|
20
|
+
const taskRef = args[0];
|
|
21
|
+
const event = args[1];
|
|
17
22
|
let artifact;
|
|
18
23
|
let format = 'json';
|
|
24
|
+
let overrideTicket;
|
|
25
|
+
let overrideTarget;
|
|
26
|
+
let overrideScope;
|
|
19
27
|
const seen = new Set();
|
|
20
28
|
for (let index = 2; index < args.length; index += 1) {
|
|
21
29
|
const flag = args[index];
|
|
22
|
-
if (
|
|
30
|
+
if (!['--artifact', '--format', '--override-ticket', '--override-target', '--override-scope'].includes(flag)) {
|
|
23
31
|
fail(`unknown option '${flag}'`);
|
|
24
32
|
return;
|
|
25
33
|
}
|
|
@@ -35,6 +43,12 @@ function taskVerify(args = []) {
|
|
|
35
43
|
seen.add(flag);
|
|
36
44
|
if (flag === '--artifact')
|
|
37
45
|
artifact = value;
|
|
46
|
+
else if (flag === '--override-ticket')
|
|
47
|
+
overrideTicket = value;
|
|
48
|
+
else if (flag === '--override-target')
|
|
49
|
+
overrideTarget = value;
|
|
50
|
+
else if (flag === '--override-scope')
|
|
51
|
+
overrideScope = value;
|
|
38
52
|
else if (value === 'json' || value === 'text')
|
|
39
53
|
format = value;
|
|
40
54
|
else {
|
|
@@ -42,7 +56,55 @@ function taskVerify(args = []) {
|
|
|
42
56
|
return;
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
|
-
const
|
|
59
|
+
const resolved = resolveTaskRef(taskRef);
|
|
60
|
+
if (!resolved.ok) {
|
|
61
|
+
fail(resolved.message);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let result;
|
|
65
|
+
let humanOverride = null;
|
|
66
|
+
try {
|
|
67
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-verify.${event}`, () => {
|
|
68
|
+
let current = verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) });
|
|
69
|
+
if (current.status === 'pass' || !overrideTicket)
|
|
70
|
+
return current;
|
|
71
|
+
if (!overrideTarget || !overrideScope) {
|
|
72
|
+
fail('override ticket requires target and scope');
|
|
73
|
+
return current;
|
|
74
|
+
}
|
|
75
|
+
const failureProducer = current.status === 'blocked' || current.status === 'fail'
|
|
76
|
+
? 'verification-engine'
|
|
77
|
+
: 'task-verify';
|
|
78
|
+
const failureCode = current.status === 'blocked'
|
|
79
|
+
? 'CHECK_BLOCKED'
|
|
80
|
+
: current.status === 'fail'
|
|
81
|
+
? 'CHECK_FAILED'
|
|
82
|
+
: current.error?.code ?? 'VERIFY_TASK_STATE_MISMATCH';
|
|
83
|
+
const consumed = consumeHumanOverride({
|
|
84
|
+
taskRef, ticketId: overrideTicket,
|
|
85
|
+
failureId: failureId(failureProducer, failureCode),
|
|
86
|
+
target: overrideTarget, scope: overrideScope
|
|
87
|
+
}, {
|
|
88
|
+
effectExecutor: (capability) => {
|
|
89
|
+
const retried = verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) }, { manualOverride: capability });
|
|
90
|
+
current = retried;
|
|
91
|
+
return retried.status !== 'pass'
|
|
92
|
+
? { code: 'OVERRIDE_EFFECT_FAILED', message: `${retried.error?.code ?? 'VERIFY_FAILED'}: ${retried.error?.message ?? 'manual verification effect failed'}` }
|
|
93
|
+
: null;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
humanOverride = consumed;
|
|
97
|
+
return current;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
102
|
+
throw error;
|
|
103
|
+
fail(`${error.code}: ${error.message}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (humanOverride)
|
|
107
|
+
result = { ...result, humanOverride };
|
|
46
108
|
process.stdout.write(format === 'text' ? renderTaskVerification(result) : `${JSON.stringify(result)}\n`);
|
|
47
109
|
process.exitCode = result.status === 'pass' ? 0 : result.status === 'blocked' ? 2 : 1;
|
|
48
110
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.js';
|
|
2
|
+
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
|
|
3
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
2
5
|
const USAGE = `Usage: agent-infra-internal task-warning <task-ref> <add|set-status|list> [intent flags] [--dry-run]\n`;
|
|
3
6
|
const FLAGS = {
|
|
4
7
|
'--step': 'step', '--severity': 'severity', '--code': 'code', '--target': 'target',
|
|
5
|
-
'--message': 'message', '--action': 'action', '--id': 'id', '--status': 'status', '--resolution': 'resolution'
|
|
8
|
+
'--message': 'message', '--action': 'action', '--id': 'id', '--status': 'status', '--resolution': 'resolution',
|
|
9
|
+
'--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
|
|
6
10
|
};
|
|
7
11
|
function usageFailure(message) {
|
|
8
12
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'WARNING_PAYLOAD_INVALID', message } })}\n`);
|
|
@@ -55,6 +59,9 @@ function taskWarning(args = []) {
|
|
|
55
59
|
};
|
|
56
60
|
const optional = kind === 'list' ? ['status'] : [];
|
|
57
61
|
const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...optional]);
|
|
62
|
+
allowed.add('overrideTicket');
|
|
63
|
+
allowed.add('overrideTarget');
|
|
64
|
+
allowed.add('overrideScope');
|
|
58
65
|
const unexpected = Object.keys(values).find((key) => !allowed.has(key));
|
|
59
66
|
const missing = required[kind].find((key) => values[key] === undefined);
|
|
60
67
|
if (unexpected) {
|
|
@@ -65,9 +72,55 @@ function taskWarning(args = []) {
|
|
|
65
72
|
usageFailure(`${kind} requires '${missing}'`);
|
|
66
73
|
return;
|
|
67
74
|
}
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
const dryRunConflict = overrideDryRunConflict(values);
|
|
76
|
+
if (dryRunConflict) {
|
|
77
|
+
usageFailure(dryRunConflict.message);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const resolved = resolveTaskRef(taskRef);
|
|
81
|
+
if (!resolved.ok) {
|
|
82
|
+
usageFailure(resolved.message);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let result;
|
|
86
|
+
let humanOverride = null;
|
|
87
|
+
try {
|
|
88
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-warning.${kind}`, () => {
|
|
89
|
+
let current = applyWorkflowWarningIntent(values);
|
|
90
|
+
if (current.status !== 'failed' || !values.overrideTicket)
|
|
91
|
+
return current;
|
|
92
|
+
if (!values.overrideTarget || !values.overrideScope) {
|
|
93
|
+
usageFailure('override ticket requires target and scope');
|
|
94
|
+
return current;
|
|
95
|
+
}
|
|
96
|
+
const consumed = consumeHumanOverride({
|
|
97
|
+
taskRef, ticketId: String(values.overrideTicket),
|
|
98
|
+
failureId: failureId('workflow-warning', current.error?.code ?? 'TASK_STATE_MISMATCH'),
|
|
99
|
+
target: String(values.overrideTarget), scope: String(values.overrideScope)
|
|
100
|
+
}, {
|
|
101
|
+
effectExecutor: (capability) => {
|
|
102
|
+
const retried = applyWorkflowWarningIntent(values, { manualOverride: capability });
|
|
103
|
+
current = retried;
|
|
104
|
+
return retried.status === 'failed' || retried.status === 'planned'
|
|
105
|
+
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no workflow warning effect was committed' : `${retried.error?.code ?? 'WARNING_FAILED'}: ${retried.error?.message ?? 'manual warning effect failed'}` }
|
|
106
|
+
: null;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
humanOverride = consumed;
|
|
110
|
+
return current;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
115
|
+
throw error;
|
|
116
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
|
|
121
|
+
const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
|
|
122
|
+
humanOverride.status === 'failed');
|
|
123
|
+
if (result.status === 'failed' || overrideFailed)
|
|
71
124
|
process.exitCode = 1;
|
|
72
125
|
}
|
|
73
126
|
export { taskWarning };
|
|
@@ -2,16 +2,25 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { execFileSync } from 'node:child_process';
|
|
3
3
|
import { enumerateArtifacts } from '../task/artifacts.js';
|
|
4
4
|
import { parseTaskFrontmatter } from '../task/frontmatter.js';
|
|
5
|
+
import { renderHumanOverrideAudit } from '../task/human-override.js';
|
|
5
6
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
6
7
|
import { resolvePlatformContext } from './context.js';
|
|
7
8
|
import { createGitHubClient } from './github-client.js';
|
|
8
9
|
import { listRemoteComments, normalizeCommentContent, writeComment } from './issue-comments.js';
|
|
9
10
|
import { platformResult } from './types.js';
|
|
11
|
+
function warningResultForPrimary(primaryResult) {
|
|
12
|
+
if (primaryResult === 'pr_created')
|
|
13
|
+
return 'pr_created_with_warnings';
|
|
14
|
+
if (primaryResult === 'pr_reused')
|
|
15
|
+
return 'pr_reused_with_warnings';
|
|
16
|
+
return 'no_op_with_warnings';
|
|
17
|
+
}
|
|
10
18
|
function summaryMarker(taskId) {
|
|
11
19
|
return `<!-- sync-pr:${taskId}:summary -->`;
|
|
12
20
|
}
|
|
13
|
-
function buildPullRequestSummary(taskId, body, headSha) {
|
|
14
|
-
|
|
21
|
+
function buildPullRequestSummary(taskId, body, headSha, humanOverrideAudit = '') {
|
|
22
|
+
const sections = [body.replace(/\s+$/, ''), humanOverrideAudit.trim()].filter(Boolean).join('\n\n');
|
|
23
|
+
return normalizeCommentContent(`${summaryMarker(taskId)}\n<!-- last-commit: ${headSha} -->\n\n${sections}\n`);
|
|
15
24
|
}
|
|
16
25
|
function reconcileSummaryComment(comments, taskId, desired) {
|
|
17
26
|
const marker = summaryMarker(taskId);
|
|
@@ -49,40 +58,71 @@ function summaryContext(taskRef, options = {}) {
|
|
|
49
58
|
return { ...platformResult('no-op'), task: { id: resolved.taskId, prNumber }, artifacts: canonicalArtifacts(resolved.taskDir) };
|
|
50
59
|
}
|
|
51
60
|
function syncPullRequestSummary(taskRef, options) {
|
|
61
|
+
const warningResult = warningResultForPrimary(options.primaryResult);
|
|
62
|
+
let knownPrNumber = null;
|
|
63
|
+
const softenFailure = (output) => {
|
|
64
|
+
const prNumber = output.resource.kind === 'pull-request' && output.resource.number
|
|
65
|
+
? output.resource.number
|
|
66
|
+
: knownPrNumber;
|
|
67
|
+
const warning = output.error && prNumber
|
|
68
|
+
&& output.error.code !== 'PR_NOT_LINKED'
|
|
69
|
+
? {
|
|
70
|
+
code: output.error.code,
|
|
71
|
+
message: output.error.message,
|
|
72
|
+
retryable: output.error.retryable,
|
|
73
|
+
step: 'pr-summary',
|
|
74
|
+
target: `pull-request:${prNumber}`,
|
|
75
|
+
severity: 'ACTION_REQUIRED'
|
|
76
|
+
}
|
|
77
|
+
: null;
|
|
78
|
+
return warning
|
|
79
|
+
? {
|
|
80
|
+
...output,
|
|
81
|
+
status: 'applied',
|
|
82
|
+
changed: false,
|
|
83
|
+
resource: { kind: 'pull-request', number: prNumber },
|
|
84
|
+
error: null,
|
|
85
|
+
result: warningResult,
|
|
86
|
+
warnings: [warning]
|
|
87
|
+
}
|
|
88
|
+
: { ...output, result: null, warnings: [] };
|
|
89
|
+
};
|
|
52
90
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
53
91
|
if (!resolved.ok)
|
|
54
|
-
return platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } });
|
|
92
|
+
return softenFailure(platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }));
|
|
55
93
|
const frontmatter = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
|
|
56
94
|
const prNumber = Number(frontmatter.pr_number);
|
|
57
95
|
if (!Number.isInteger(prNumber) || prNumber <= 0)
|
|
58
|
-
return platformResult('failed', { error: { code: 'PR_NOT_LINKED', message: 'Task has no valid pr_number', retryable: false } });
|
|
96
|
+
return softenFailure(platformResult('failed', { error: { code: 'PR_NOT_LINKED', message: 'Task has no valid pr_number', retryable: false } }));
|
|
97
|
+
knownPrNumber = prNumber;
|
|
59
98
|
const client = options.client || createGitHubClient();
|
|
60
99
|
const context = resolvePlatformContext({ cwd: resolved.repoRoot, client });
|
|
61
100
|
if (!context.platform.repository || !['no-op', 'degraded'].includes(context.status))
|
|
62
|
-
return context;
|
|
101
|
+
return softenFailure(context);
|
|
63
102
|
let headSha;
|
|
64
103
|
try {
|
|
65
104
|
headSha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: resolved.repoRoot, encoding: 'utf8' }).trim();
|
|
66
105
|
}
|
|
67
106
|
catch (error) {
|
|
68
|
-
return platformResult('failed', { platform: context.platform, capabilities: context.capabilities, error: { code: 'GIT_HEAD_UNRESOLVED', message: error instanceof Error ? error.message : String(error), retryable: false } });
|
|
107
|
+
return softenFailure(platformResult('failed', { platform: context.platform, capabilities: context.capabilities, error: { code: 'GIT_HEAD_UNRESOLVED', message: error instanceof Error ? error.message : String(error), retryable: false } }));
|
|
69
108
|
}
|
|
70
|
-
const desired = buildPullRequestSummary(resolved.taskId, options.body, headSha);
|
|
109
|
+
const desired = buildPullRequestSummary(resolved.taskId, options.body, headSha, renderHumanOverrideAudit(fs.readFileSync(resolved.taskMdPath, 'utf8')));
|
|
71
110
|
const listed = listRemoteComments(client, context.platform.repository, prNumber, resolved.repoRoot);
|
|
72
111
|
if (!listed.ok)
|
|
73
|
-
return platformResult(listed.error.retryable ? 'blocked' : 'failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: listed.error });
|
|
112
|
+
return softenFailure(platformResult(listed.error.retryable ? 'blocked' : 'failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: listed.error }));
|
|
74
113
|
const reconciliation = reconcileSummaryComment(listed.value, resolved.taskId, desired);
|
|
75
114
|
if (reconciliation.action === 'conflict')
|
|
76
|
-
return platformResult('failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: { code: 'PR_SUMMARY_MARKER_AMBIGUOUS', message: 'Multiple PR comments contain the summary marker', retryable: false } });
|
|
115
|
+
return softenFailure(platformResult('failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: { code: 'PR_SUMMARY_MARKER_AMBIGUOUS', message: 'Multiple PR comments contain the summary marker', retryable: false } }));
|
|
77
116
|
if (reconciliation.action === 'no-op')
|
|
78
|
-
return platformResult('no-op', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: [reconciliation.commentId], parts: 1 }, error: null });
|
|
117
|
+
return softenFailure(platformResult('no-op', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: [reconciliation.commentId], parts: 1 }, error: null }));
|
|
79
118
|
if (options.dryRun)
|
|
80
|
-
return platformResult('planned', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, operations: [{ name: `summary:${reconciliation.action}`, status: 'planned', reasonCode: null }], error: null });
|
|
119
|
+
return softenFailure(platformResult('planned', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, operations: [{ name: `summary:${reconciliation.action}`, status: 'planned', reasonCode: null }], error: null }));
|
|
81
120
|
const written = writeComment(client, context.platform.repository, prNumber, resolved.repoRoot, desired, reconciliation.commentId || undefined);
|
|
82
121
|
if (!written.ok)
|
|
83
|
-
return platformResult(written.error.retryable ? 'blocked' : 'failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: written.error });
|
|
122
|
+
return softenFailure(platformResult(written.error.retryable ? 'blocked' : 'failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: written.error }));
|
|
84
123
|
const id = Number(written.value.id || reconciliation.commentId);
|
|
85
|
-
return platformResult('applied', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: Number.isInteger(id) ? [id] : [], parts: 1 }, operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }], error: null });
|
|
124
|
+
return softenFailure(platformResult('applied', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: Number.isInteger(id) ? [id] : [], parts: 1 }, operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }], error: null }));
|
|
86
125
|
}
|
|
87
126
|
export { buildPullRequestSummary, reconcileSummaryComment, summaryContext, summaryMarker, syncPullRequestSummary };
|
|
127
|
+
export { warningResultForPrimary };
|
|
88
128
|
//# sourceMappingURL=pr-summary.js.map
|