@fitlab-ai/agent-infra 0.10.0 → 0.11.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/README.md +2 -6
- package/README.zh-CN.md +2 -6
- package/bin/internal-cli.sh +0 -13
- package/dist/bin/internal-cli.js +11 -49
- package/dist/lib/internal/cli-route-inventory.js +3 -4
- package/dist/lib/internal/manual-validation.js +6 -2
- package/dist/lib/internal/platform-comment.js +60 -2
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +22 -12
- package/dist/lib/internal/task-orchestration.js +2 -2
- package/dist/lib/internal/task-review.js +10 -5
- package/dist/lib/platform/activity-recovery.js +159 -0
- package/dist/lib/platform/issue-comments.js +84 -32
- package/dist/lib/platform/issues.js +3 -4
- package/dist/lib/platform/task-comment-projection.js +70 -0
- package/dist/lib/platform/task-recovery.js +55 -0
- package/dist/lib/platform/verification-sync.js +184 -162
- package/dist/lib/sandbox/control/executor.js +4 -2
- package/dist/lib/sandbox/control/task-workflow.js +1 -1
- package/dist/lib/sandbox/control/workflow-executor.js +90 -22
- package/dist/lib/server/process-state.js +17 -5
- package/dist/lib/task/artifact-command.js +6 -3
- package/dist/lib/task/artifact-lifecycle.js +15 -52
- package/dist/lib/task/artifact-operations.js +28 -2
- package/dist/lib/task/artifact-recovery.js +114 -68
- package/dist/lib/task/artifact-repair-intent.js +25 -8
- package/dist/lib/task/capabilities.js +0 -2
- package/dist/lib/task/control-authority.js +1 -1
- package/dist/lib/task/events.js +66 -49
- package/dist/lib/task/gate-policy.js +94 -0
- package/dist/lib/task/intermediate-cleanup.js +28 -3
- package/dist/lib/task/local-artifact-finalization.js +85 -18
- package/dist/lib/task/orchestration.js +8 -6
- package/dist/lib/task/review-artifacts.js +12 -6
- package/dist/lib/task/review-command.js +2 -2
- package/dist/lib/task/review-finalization.js +198 -11
- package/dist/lib/{host-control → task}/secure-fs.js +8 -19
- package/dist/lib/task/verification-engine.js +28 -17
- package/dist/lib/task/verification.js +25 -9
- package/dist/lib/task/workflow-command.js +2 -0
- package/dist/lib/task/workflow-dispatch.js +161 -0
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/internal/cli-route-inventory.ts +3 -4
- package/lib/internal/manual-validation.ts +6 -2
- package/lib/internal/platform-comment.ts +53 -2
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-finalization.ts +1 -1
- package/lib/internal/task-lifecycle.ts +1 -1
- package/lib/internal/task-operation-registry.ts +22 -12
- package/lib/internal/task-orchestration.ts +1 -1
- package/lib/internal/task-review.ts +9 -5
- package/lib/platform/activity-recovery.ts +179 -0
- package/lib/platform/issue-comments.ts +98 -31
- package/lib/platform/issues.ts +3 -4
- package/lib/platform/task-comment-projection.ts +79 -0
- package/lib/platform/task-recovery.ts +54 -0
- package/lib/platform/verification-sync.ts +203 -188
- package/lib/sandbox/control/executor.ts +4 -2
- package/lib/sandbox/control/protocol.ts +1 -1
- package/lib/sandbox/control/task-workflow.ts +1 -1
- package/lib/sandbox/control/workflow-executor.ts +113 -22
- package/lib/server/process-state.ts +26 -5
- package/lib/task/artifact-command.ts +6 -4
- package/lib/task/artifact-lifecycle.ts +12 -49
- package/lib/task/artifact-operations.ts +28 -2
- package/lib/task/artifact-recovery.ts +120 -67
- package/lib/task/artifact-repair-intent.ts +38 -11
- package/lib/task/capabilities.ts +0 -1
- package/lib/task/control-authority.ts +1 -1
- package/lib/task/events.ts +55 -38
- package/lib/task/gate-policy.ts +133 -0
- package/lib/task/intermediate-cleanup.ts +18 -2
- package/lib/task/local-artifact-finalization.ts +91 -15
- package/lib/task/orchestration.ts +12 -7
- package/lib/task/review-artifacts.ts +8 -2
- package/lib/task/review-command.ts +1 -1
- package/lib/task/review-finalization.ts +198 -9
- package/lib/{host-control → task}/secure-fs.ts +10 -32
- package/lib/task/verification-engine.ts +24 -14
- package/lib/task/verification-types.ts +7 -0
- package/lib/task/verification.ts +25 -9
- package/lib/task/workflow-command.ts +2 -0
- package/lib/task/workflow-dispatch.ts +171 -0
- package/package.json +1 -1
- package/templates/.agents/rules/issue-sync.en.md +11 -1
- package/templates/.agents/rules/issue-sync.zh-CN.md +6 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +18 -9
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +21 -11
- package/templates/.agents/skills/analyze-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -10
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/block-task/config/verify.json +1 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -12
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -0
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -0
- package/templates/.agents/skills/commit/config/verify.json +1 -5
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +6 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +6 -6
- package/templates/.agents/skills/complete-task/config/verify.en.json +3 -14
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +3 -14
- package/templates/.agents/skills/create-pr/config/verify.json +1 -10
- package/templates/.agents/skills/create-task/config/verify.json +1 -4
- package/templates/.agents/skills/import-issue/config/verify.json +1 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -10
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/restore-task/SKILL.en.md +9 -4
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +10 -5
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -10
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +3 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +3 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -10
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-code/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -10
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -8
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -8
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +38 -13
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +38 -13
- package/dist/lib/host-control/audit.js +0 -19
- package/dist/lib/host-control/client.js +0 -142
- package/dist/lib/host-control/command.js +0 -49
- package/dist/lib/host-control/path.js +0 -160
- package/dist/lib/host-control/server.js +0 -159
- package/dist/lib/host-control/service.js +0 -112
- package/dist/lib/internal/host-control.js +0 -28
- package/lib/host-control/audit.ts +0 -22
- package/lib/host-control/client.ts +0 -172
- package/lib/host-control/command.ts +0 -60
- package/lib/host-control/path.ts +0 -167
- package/lib/host-control/server.ts +0 -180
- package/lib/host-control/service.ts +0 -133
- package/lib/internal/host-control.ts +0 -28
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { reconcileTaskInvalidation } from './invalidation-command.js';
|
|
2
|
+
import { applyLedgerIntent } from './ledger-intents.js';
|
|
3
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
4
|
+
import { applyWorkflowWarningIntent } from './workflow-warning-intents.js';
|
|
5
|
+
function failed(code, message) {
|
|
6
|
+
return { status: 'failed', changed: false, error: { code, message } };
|
|
7
|
+
}
|
|
8
|
+
function isFailure(value) {
|
|
9
|
+
return Boolean(value) && typeof value === 'object' && value.status === 'failed';
|
|
10
|
+
}
|
|
11
|
+
function options(args, allowed) {
|
|
12
|
+
const values = {};
|
|
13
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
14
|
+
const flag = args[index];
|
|
15
|
+
if (flag === '--dry-run') {
|
|
16
|
+
if (!Object.hasOwn(allowed, flag) || values[flag] !== undefined)
|
|
17
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', `invalid option '${flag}'`);
|
|
18
|
+
values[flag] = true;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const key = allowed[flag];
|
|
22
|
+
if (!key || values[flag] !== undefined)
|
|
23
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', `invalid option '${flag}'`);
|
|
24
|
+
const value = args[++index];
|
|
25
|
+
if (value === undefined || value.startsWith('--'))
|
|
26
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', `option '${flag}' requires a value`);
|
|
27
|
+
if (flag === '--needs-implementation' && value !== 'true' && value !== 'false') {
|
|
28
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', "option '--needs-implementation' must be true or false");
|
|
29
|
+
}
|
|
30
|
+
values[flag] = value;
|
|
31
|
+
}
|
|
32
|
+
return values;
|
|
33
|
+
}
|
|
34
|
+
function value(values, flag) {
|
|
35
|
+
const candidate = values[flag];
|
|
36
|
+
return typeof candidate === 'string' ? candidate : undefined;
|
|
37
|
+
}
|
|
38
|
+
function required(values, flags) {
|
|
39
|
+
const missing = flags.find((flag) => value(values, flag) === undefined);
|
|
40
|
+
return missing ? failed('TASK_WORKFLOW_PAYLOAD_INVALID', `option '${missing}' is required`) : null;
|
|
41
|
+
}
|
|
42
|
+
function resultForLock(error) {
|
|
43
|
+
if (error instanceof TaskExecutionLockError)
|
|
44
|
+
return failed(error.code, error.message);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
function ledgerIntent(operation, args) {
|
|
48
|
+
const [taskRef, kind] = args;
|
|
49
|
+
const mappings = {
|
|
50
|
+
'ledger-finding-response': {
|
|
51
|
+
kind: 'finding-respond', required: ['--id', '--round', '--status', '--evidence'],
|
|
52
|
+
flags: { '--id': 'id', '--round': 'round', '--status': 'status', '--evidence': 'evidence', '--dry-run': 'dryRun' }
|
|
53
|
+
},
|
|
54
|
+
'ledger-finding-review': {
|
|
55
|
+
kind: 'finding-review', required: ['--id', '--status', '--evidence'],
|
|
56
|
+
flags: { '--id': 'id', '--status': 'status', '--evidence': 'evidence', '--needs-implementation': 'needsImplementation', '--dry-run': 'dryRun' }
|
|
57
|
+
},
|
|
58
|
+
'ledger-finding-upsert': {
|
|
59
|
+
kind: 'finding-upsert', required: ['--stage', '--review-artifact', '--ordinal', '--severity', '--evidence'],
|
|
60
|
+
flags: { '--stage': 'stage', '--review-artifact': 'reviewArtifact', '--ordinal': 'ordinal', '--severity': 'severity', '--evidence': 'evidence', '--dry-run': 'dryRun' }
|
|
61
|
+
},
|
|
62
|
+
'decision-next-id': { kind: 'decision-next-id', required: [], flags: {} },
|
|
63
|
+
'decision-upsert': {
|
|
64
|
+
kind: 'decision-upsert', required: ['--id', '--stage', '--artifact'],
|
|
65
|
+
flags: { '--id': 'id', '--stage': 'stage', '--artifact': 'artifact', '--needs-implementation': 'needsImplementation', '--dry-run': 'dryRun' }
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const mapping = mappings[operation];
|
|
69
|
+
if (!mapping || !taskRef || kind === undefined)
|
|
70
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', 'task workflow ledger request is invalid');
|
|
71
|
+
const values = options(args, mapping.flags);
|
|
72
|
+
if (isFailure(values))
|
|
73
|
+
return values;
|
|
74
|
+
const missing = required(values, mapping.required);
|
|
75
|
+
if (missing)
|
|
76
|
+
return missing;
|
|
77
|
+
const dryRun = values['--dry-run'] === true;
|
|
78
|
+
if (mapping.kind === 'decision-next-id')
|
|
79
|
+
return { kind: mapping.kind, taskRef };
|
|
80
|
+
if (mapping.kind === 'finding-respond')
|
|
81
|
+
return {
|
|
82
|
+
kind: mapping.kind, taskRef, id: value(values, '--id'), round: Number(value(values, '--round')),
|
|
83
|
+
status: value(values, '--status'), evidence: value(values, '--evidence'), ...(dryRun ? { dryRun } : {})
|
|
84
|
+
};
|
|
85
|
+
if (mapping.kind === 'finding-review')
|
|
86
|
+
return {
|
|
87
|
+
kind: mapping.kind, taskRef, id: value(values, '--id'), status: value(values, '--status'),
|
|
88
|
+
evidence: value(values, '--evidence'), ...(value(values, '--needs-implementation') === 'true' ? { needsImplementation: true } : {}),
|
|
89
|
+
...(dryRun ? { dryRun } : {})
|
|
90
|
+
};
|
|
91
|
+
if (mapping.kind === 'finding-upsert')
|
|
92
|
+
return {
|
|
93
|
+
kind: mapping.kind, taskRef, stage: value(values, '--stage'), reviewArtifact: value(values, '--review-artifact'),
|
|
94
|
+
ordinal: Number(value(values, '--ordinal')), severity: value(values, '--severity'), evidence: value(values, '--evidence'),
|
|
95
|
+
...(dryRun ? { dryRun } : {})
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
kind: mapping.kind, taskRef, id: value(values, '--id'), stage: value(values, '--stage'),
|
|
99
|
+
artifact: value(values, '--artifact'), ...(value(values, '--needs-implementation') === 'true' ? { needsImplementation: true } : {}),
|
|
100
|
+
...(dryRun ? { dryRun } : {})
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function warningIntent(args) {
|
|
104
|
+
const [taskRef, kind] = args;
|
|
105
|
+
if (!taskRef || kind !== 'add')
|
|
106
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', 'task workflow warning request is invalid');
|
|
107
|
+
const values = options(args, {
|
|
108
|
+
'--step': 'step', '--severity': 'severity', '--code': 'code', '--target': 'target',
|
|
109
|
+
'--message': 'message', '--action': 'action', '--dry-run': 'dryRun'
|
|
110
|
+
});
|
|
111
|
+
if (isFailure(values))
|
|
112
|
+
return values;
|
|
113
|
+
const missing = required(values, ['--step', '--severity', '--code', '--target', '--message', '--action']);
|
|
114
|
+
if (missing)
|
|
115
|
+
return missing;
|
|
116
|
+
return {
|
|
117
|
+
kind: 'add', taskRef, step: value(values, '--step'), severity: value(values, '--severity'),
|
|
118
|
+
code: value(values, '--code'), target: value(values, '--target'), message: value(values, '--message'),
|
|
119
|
+
action: value(values, '--action'), ...(values['--dry-run'] === true ? { dryRun: true } : {})
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The caller has already selected and authorized the execution boundary.
|
|
124
|
+
* This function deliberately invokes task domains in that process: starting a
|
|
125
|
+
* second CLI process would discard the broker binding and could misclassify a
|
|
126
|
+
* sandbox process with cleared environment variables as direct-host.
|
|
127
|
+
*/
|
|
128
|
+
export function dispatchWorkflowCommand(repoRoot, operation, args) {
|
|
129
|
+
const taskRef = args[0];
|
|
130
|
+
if (!taskRef)
|
|
131
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', 'task ref is required');
|
|
132
|
+
try {
|
|
133
|
+
if (operation === 'invalidation-reconcile') {
|
|
134
|
+
const values = options(args, { '--max-targets': 'maxTargets', '--dry-run': 'dryRun' });
|
|
135
|
+
if (isFailure(values))
|
|
136
|
+
return values;
|
|
137
|
+
const maxTargets = value(values, '--max-targets');
|
|
138
|
+
if (maxTargets !== undefined && (!Number.isSafeInteger(Number(maxTargets)) || Number(maxTargets) < 1)) {
|
|
139
|
+
return failed('TASK_WORKFLOW_PAYLOAD_INVALID', '--max-targets must be a positive integer');
|
|
140
|
+
}
|
|
141
|
+
return withTaskExecutionLock(repoRoot, taskRef, 'task-invalidation.reconcile', () => reconcileTaskInvalidation(taskRef, {
|
|
142
|
+
repoRoot, ...(maxTargets === undefined ? {} : { maxTargets: Number(maxTargets) }),
|
|
143
|
+
...(values['--dry-run'] === true ? { dryRun: true } : {})
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
if (operation === 'warning-add') {
|
|
147
|
+
const intent = warningIntent(args);
|
|
148
|
+
if (isFailure(intent))
|
|
149
|
+
return intent;
|
|
150
|
+
return withTaskExecutionLock(repoRoot, taskRef, 'task-warning.add', () => applyWorkflowWarningIntent(intent, { repoRoot }));
|
|
151
|
+
}
|
|
152
|
+
const intent = ledgerIntent(operation, args);
|
|
153
|
+
if (isFailure(intent))
|
|
154
|
+
return intent;
|
|
155
|
+
return withTaskExecutionLock(repoRoot, taskRef, `task-ledger.${intent.kind}`, () => applyLedgerIntent(intent, { repoRoot }));
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return resultForLock(error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=workflow-dispatch.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.11.1",
|
|
4
|
+
"sourceInputHash": "0d66fb32bbdc0982d38a04b86b0278768bb6091eb945ebd90a432f9bae363712",
|
|
5
|
+
"internalExecutableBuildHash": "4b1edd20ed87d3d742e5e7975fd49b1ac1fb0ff14f837d52ee9ba3ab11318365"
|
|
6
6
|
}
|
|
@@ -36,7 +36,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
36
36
|
'task-create': ['input'],
|
|
37
37
|
'task-qualification': ['proposal', 'confirm', 'supersede', 'revoke'],
|
|
38
38
|
'sandbox-control': ['serve', 'execute', 'recover', 'client'],
|
|
39
|
-
'host-control': ['serve', 'status', 'install', 'uninstall'],
|
|
40
39
|
'agent-client': ['next-steps', 'model-selection'],
|
|
41
40
|
'codex-lifecycle': ['preflight', 'capability-arm', 'hook-event', 'resolve-start', 'resolve-stop', 'consume'],
|
|
42
41
|
'codex-sandbox-controller': ['verify-context', 'run'],
|
|
@@ -47,7 +46,7 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
47
46
|
'platform-security': ['read', 'dismiss'],
|
|
48
47
|
'platform-metadata': ['init-labels', 'init-milestones'],
|
|
49
48
|
'platform-context': ['resolve'],
|
|
50
|
-
'platform-comment': ['list', 'owner', 'backfill', 'sync'],
|
|
49
|
+
'platform-comment': ['list', 'recover', 'owner', 'backfill', 'sync'],
|
|
51
50
|
'platform-issue': ['inspect', 'create', 'bind', 'sync'],
|
|
52
51
|
'platform-pr': ['inspect', 'summary-context', 'resolve-external', 'create', 'bind', 'skip', 'sync', 'change-report', 'summary-sync', 'sync-in-labels'],
|
|
53
52
|
'platform-pr-review': ['inspect', 'list', 'publish-pr', 'publish-task'],
|
|
@@ -57,9 +56,9 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
57
56
|
'task-ledger': ['decision-next-id', 'stage-status', 'finding-upsert', 'finding-respond', 'finding-review', 'decision-upsert', 'rework-intent-upsert'],
|
|
58
57
|
'task-warning': ['list', 'add', 'set-status'],
|
|
59
58
|
'task-activity': ['pr-review-inspect', 'pr-review-start', 'pr-review-complete', 'pr-review-terminate'],
|
|
60
|
-
'task-artifact': ['inspect', 'init', 'finalize-local'],
|
|
59
|
+
'task-artifact': ['inspect', 'init', 'preflight', 'finalize-local'],
|
|
61
60
|
'task-orchestration': ['status', 'progress'],
|
|
62
|
-
'task-review': ['finalize-summary'],
|
|
61
|
+
'task-review': ['preflight', 'finalize-summary'],
|
|
63
62
|
'task-event': ['event'],
|
|
64
63
|
'task-invalidation': ['reconcile'],
|
|
65
64
|
'task-override': ['diagnose', 'issue', 'consume'],
|
|
@@ -44,6 +44,7 @@ function printFailure(format: OutputFormat, error: { code: string; message: stri
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const MANUAL_VALIDATION_STATUS_SECTION = /^###\s+(?:⚠️\s+(?:需人工校验|Manual Validation Required)|✅\s+(?:人工验证已通过|无需人工校验|Manual Validation Passed|No Manual Validation Required)|⏳\s+(?:人工验证待收尾|Manual Validation Pending))\s*$[\s\S]*?(?=^#{1,3}\s|(?![\s\S]))/gmu;
|
|
47
|
+
const TRAILING_CANONICAL_REPORT_PLACEHOLDER = /\n*<!--\s*canonical-pr-change-report\s*-->\s*$/u;
|
|
47
48
|
|
|
48
49
|
function manualSummaryBody(body: string, phase: 'pending' | 'final', transactionId: string, receiptDigest = '', evidenceDigest = '', prHeadSha = ''): string {
|
|
49
50
|
const chinese = /###\s+(?:⚠️\s+需人工校验|✅\s+(?:人工验证已通过|无需人工校验)|⏳\s+人工验证待收尾)\s*$/mu.test(body);
|
|
@@ -55,10 +56,13 @@ function manualSummaryBody(body: string, phase: 'pending' | 'final', transaction
|
|
|
55
56
|
? `### ✅ 人工验证已通过\n\n人工验证已通过;transaction=${transactionId}; receipt=${receiptDigest}; evidence=${evidenceDigest}; head=${prHeadSha}.`
|
|
56
57
|
: `### ✅ Manual Validation Passed\n\nManual validation passed; transaction=${transactionId}; receipt=${receiptDigest}; evidence=${evidenceDigest}; head=${prHeadSha}.`;
|
|
57
58
|
let inserted = false;
|
|
58
|
-
const updated = body.replace(MANUAL_VALIDATION_STATUS_SECTION, () => {
|
|
59
|
+
const updated = body.replace(MANUAL_VALIDATION_STATUS_SECTION, (matched) => {
|
|
59
60
|
if (inserted) return '';
|
|
60
61
|
inserted = true;
|
|
61
|
-
|
|
62
|
+
const trailingPlaceholder = TRAILING_CANONICAL_REPORT_PLACEHOLDER.exec(matched)?.[0];
|
|
63
|
+
return trailingPlaceholder
|
|
64
|
+
? `${trailingPlaceholder.trim()}\n\n${section}\n\n`
|
|
65
|
+
: `${section}\n\n`;
|
|
62
66
|
});
|
|
63
67
|
return inserted ? `${updated.replace(/\s+$/u, '')}\n` : `${body.replace(/\s+$/u, '')}\n\n${section}\n`;
|
|
64
68
|
}
|
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
} from '../platform/issue-comments.ts';
|
|
10
10
|
import type { CommentKind } from '../platform/issue-comments.ts';
|
|
11
11
|
import type { PlatformResult } from '../platform/types.ts';
|
|
12
|
+
import { recoverTaskFromComments } from '../platform/task-recovery.ts';
|
|
12
13
|
import { backfillCompletionComments } from '../platform/completion-backfill.ts';
|
|
13
14
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
14
15
|
|
|
15
16
|
const USAGE = `Usage: agent-infra-internal platform-comment list --issue <token> [--cwd <path>]
|
|
17
|
+
agent-infra-internal platform-comment recover --issue <token> --task-id <TASK-id> --output <staging-task.md> [--cwd <path>]
|
|
16
18
|
agent-infra-internal platform-comment owner <task-ref> [--cwd <path>]
|
|
17
19
|
agent-infra-internal platform-comment backfill <task-ref> --agent <agent> [--cwd <path>]
|
|
18
20
|
agent-infra-internal platform-comment sync <task-ref> --kind <kind> --agent <agent> [--artifact <file>] [--body-file <path|->] [--status-label <label>] [--backfill] [--cwd <path>]
|
|
@@ -35,7 +37,7 @@ function parseFlags(args: string[], start: number): { values: Record<string, str
|
|
|
35
37
|
const seen = new Set<string>();
|
|
36
38
|
for (let index = start; index < args.length; index += 1) {
|
|
37
39
|
const flag = args[index]!;
|
|
38
|
-
if (!['--issue', '--cwd', '--kind', '--agent', '--artifact', '--body-file', '--status-label', '--backfill'].includes(flag)) {
|
|
40
|
+
if (!['--issue', '--task-id', '--output', '--cwd', '--kind', '--agent', '--artifact', '--body-file', '--status-label', '--backfill'].includes(flag)) {
|
|
39
41
|
return { values, error: `unknown option '${flag}'` };
|
|
40
42
|
}
|
|
41
43
|
if (seen.has(flag)) return { values, error: `duplicate option '${flag}'` };
|
|
@@ -59,11 +61,12 @@ async function platformComment(args: string[] = []): Promise<void> {
|
|
|
59
61
|
const operation = args[0];
|
|
60
62
|
if (!operation || ![
|
|
61
63
|
internalHandlerRoute('platform-comment', 'list', operation),
|
|
64
|
+
internalHandlerRoute('platform-comment', 'recover', operation),
|
|
62
65
|
internalHandlerRoute('platform-comment', 'owner', operation),
|
|
63
66
|
internalHandlerRoute('platform-comment', 'backfill', operation),
|
|
64
67
|
internalHandlerRoute('platform-comment', 'sync', operation)
|
|
65
68
|
].some(Boolean)) { fail('a valid operation is required'); return; }
|
|
66
|
-
const hasTaskRef = operation !== 'list';
|
|
69
|
+
const hasTaskRef = operation !== 'list' && operation !== 'recover';
|
|
67
70
|
const taskRef = hasTaskRef ? args[1] : undefined;
|
|
68
71
|
if (hasTaskRef && (!taskRef || taskRef.startsWith('--'))) { fail(`${operation} requires a task ref`); return; }
|
|
69
72
|
const parsed = parseFlags(args, hasTaskRef ? 2 : 1);
|
|
@@ -77,6 +80,54 @@ async function platformComment(args: string[] = []): Promise<void> {
|
|
|
77
80
|
finish(await listPlatformComments(issue, cwd));
|
|
78
81
|
return;
|
|
79
82
|
}
|
|
83
|
+
if (internalHandlerRoute('platform-comment', 'recover', operation)) {
|
|
84
|
+
const issue = parsed.values.issue;
|
|
85
|
+
const taskId = parsed.values.taskId;
|
|
86
|
+
const output = parsed.values.output;
|
|
87
|
+
if (typeof issue !== 'string' || !issue || typeof taskId !== 'string' || !/^TASK-\d{8}-\d{6}$/.test(taskId)
|
|
88
|
+
|| typeof output !== 'string' || !output) {
|
|
89
|
+
fail('recover requires --issue <token> --task-id <TASK-id> --output <staging-task.md>');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const unexpected = Object.keys(parsed.values).find((key) => !['issue', 'taskId', 'output', 'cwd'].includes(key));
|
|
93
|
+
if (unexpected) { fail(`recover does not accept '--${unexpected}'`); return; }
|
|
94
|
+
const outputPath = path.resolve(cwd, output);
|
|
95
|
+
const workspaceDir = path.resolve(cwd, '.agents', 'workspace');
|
|
96
|
+
const stagingDir = path.dirname(outputPath);
|
|
97
|
+
const stagingParent = fs.existsSync(stagingDir) ? path.dirname(fs.realpathSync(stagingDir)) : '';
|
|
98
|
+
const realWorkspace = fs.existsSync(workspaceDir) ? fs.realpathSync(workspaceDir) : '';
|
|
99
|
+
if (path.basename(outputPath) !== 'task.md' || stagingParent !== realWorkspace
|
|
100
|
+
|| !path.basename(stagingDir).startsWith('.restore-staging-') || !fs.existsSync(stagingDir)
|
|
101
|
+
|| !fs.lstatSync(stagingDir).isDirectory() || fs.existsSync(outputPath)) {
|
|
102
|
+
fail('recover output must be a new task.md inside an existing .agents/workspace/.restore-staging-* directory');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const listed = await listPlatformComments(issue, cwd);
|
|
106
|
+
if (listed.status === 'failed' || listed.status === 'blocked' || !listed.comments) { finish(listed); return; }
|
|
107
|
+
try {
|
|
108
|
+
const taskContent = recoverTaskFromComments({ taskId, comments: listed.comments });
|
|
109
|
+
fs.writeFileSync(outputPath, taskContent, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
|
|
110
|
+
finish({
|
|
111
|
+
...listed,
|
|
112
|
+
status: 'applied',
|
|
113
|
+
changed: true,
|
|
114
|
+
operations: [{ name: `recovery:task:${taskId}`, status: 'applied', reasonCode: null }],
|
|
115
|
+
error: null
|
|
116
|
+
});
|
|
117
|
+
} catch (error) {
|
|
118
|
+
finish({
|
|
119
|
+
...listed,
|
|
120
|
+
status: 'failed',
|
|
121
|
+
changed: false,
|
|
122
|
+
error: {
|
|
123
|
+
code: 'RECOVERY_EVIDENCE_MISSING',
|
|
124
|
+
message: error instanceof Error ? error.message : String(error),
|
|
125
|
+
retryable: false
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
80
131
|
if (internalHandlerRoute('platform-comment', 'owner', operation)) {
|
|
81
132
|
const unexpected = Object.keys(parsed.values).find((key) => key !== 'cwd');
|
|
82
133
|
if (unexpected) { fail(`owner does not accept '--${unexpected}'`); return; }
|
|
@@ -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>\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> finalize-local --family <analysis|plan|code> --artifact <artifact> [--recovery-id <id>]\n\nInspect, initialize, or finalize a workflow artifact without changing task state.\n`;
|
|
4
|
+
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family>\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> [--recovery-id <id>]\n\nInspect, initialize, preflight, or finalize a workflow artifact without changing task state.\n`;
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
function taskArtifact(args: string[] = []): void {
|
|
@@ -38,8 +38,8 @@ function parseFailure(error: unknown): string {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
async function taskFinalization(args: string[] = []): Promise<void> {
|
|
41
|
-
if (!ensureInternalHandlerRoute('task-finalization', args)) return;
|
|
42
41
|
if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
|
|
42
|
+
if (!ensureInternalHandlerRoute('task-finalization', args)) return;
|
|
43
43
|
|
|
44
44
|
let operation;
|
|
45
45
|
try {
|
|
@@ -24,8 +24,8 @@ function parseFailure(error: unknown): string {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async function taskLifecycle(args: string[] = []): Promise<void> {
|
|
27
|
-
if (!ensureInternalHandlerRoute('task-lifecycle', args)) return;
|
|
28
27
|
if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
|
|
28
|
+
if (!ensureInternalHandlerRoute('task-lifecycle', args)) return;
|
|
29
29
|
|
|
30
30
|
let operation;
|
|
31
31
|
try {
|
|
@@ -56,10 +56,6 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
|
|
|
56
56
|
descriptor('internal', 'sandbox-control', 'execute', 'non-task', 'progress', 'none'),
|
|
57
57
|
descriptor('internal', 'sandbox-control', 'recover', 'conditional', 'recovery', 'delegated'),
|
|
58
58
|
descriptor('internal', 'sandbox-control', 'client', 'conditional', 'progress', 'delegated'),
|
|
59
|
-
descriptor('internal', 'host-control', 'serve', 'non-task', 'progress', 'none'),
|
|
60
|
-
descriptor('internal', 'host-control', 'status', 'non-task', 'diagnostic', 'none'),
|
|
61
|
-
descriptor('internal', 'host-control', 'install', 'non-task', 'progress', 'none'),
|
|
62
|
-
descriptor('internal', 'host-control', 'uninstall', 'non-task', 'progress', 'none'),
|
|
63
59
|
descriptor('internal', 'agent-client', 'next-steps', 'non-task', 'diagnostic', 'none'),
|
|
64
60
|
descriptor('internal', 'agent-client', 'model-selection', 'non-task', 'diagnostic', 'none'),
|
|
65
61
|
descriptor('internal', 'codex-lifecycle', 'preflight', 'non-task', 'diagnostic', 'none'),
|
|
@@ -91,6 +87,7 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
|
|
|
91
87
|
descriptor('internal', 'platform-metadata', 'init-milestones', 'non-task', 'remote-write', 'none'),
|
|
92
88
|
descriptor('internal', 'platform-context', 'resolve', 'non-task', 'diagnostic', 'none'),
|
|
93
89
|
descriptor('internal', 'platform-comment', 'list', 'non-task', 'diagnostic', 'none'),
|
|
90
|
+
descriptor('internal', 'platform-comment', 'recover', 'non-task', 'recovery', 'none'),
|
|
94
91
|
descriptor('internal', 'platform-comment', 'owner', 'task-bound', 'diagnostic'),
|
|
95
92
|
descriptor('internal', 'platform-comment', 'backfill', 'task-bound', 'remote-write'),
|
|
96
93
|
descriptor('internal', 'platform-comment', 'sync', 'task-bound', 'remote-write'),
|
|
@@ -136,9 +133,11 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
|
|
|
136
133
|
descriptor('internal', 'task-activity', 'pr-review-terminate', 'task-bound', 'progress'),
|
|
137
134
|
descriptor('internal', 'task-artifact', 'inspect', 'task-bound', 'diagnostic'),
|
|
138
135
|
descriptor('internal', 'task-artifact', 'init', 'task-bound', 'artifact-write'),
|
|
136
|
+
descriptor('internal', 'task-artifact', 'preflight', 'task-bound', 'artifact-write'),
|
|
139
137
|
descriptor('internal', 'task-artifact', 'finalize-local', 'task-bound', 'artifact-write'),
|
|
140
138
|
descriptor('internal', 'task-orchestration', 'status', 'task-bound', 'diagnostic'),
|
|
141
139
|
descriptor('internal', 'task-orchestration', 'progress', 'task-bound', 'progress'),
|
|
140
|
+
descriptor('internal', 'task-review', 'preflight', 'task-bound', 'artifact-write'),
|
|
142
141
|
descriptor('internal', 'task-review', 'finalize-summary', 'task-bound', 'progress'),
|
|
143
142
|
descriptor('internal', 'task-event', 'event', 'task-bound', 'progress'),
|
|
144
143
|
descriptor('internal', 'task-invalidation', 'reconcile', 'task-bound', 'progress'),
|
|
@@ -218,7 +217,7 @@ export const TASK_OPERATION_DESCRIPTORS = Object.freeze([
|
|
|
218
217
|
]);
|
|
219
218
|
|
|
220
219
|
export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
|
|
221
|
-
'task-create', 'task-qualification', 'sandbox-control', '
|
|
220
|
+
'task-create', 'task-qualification', 'sandbox-control', 'agent-client', 'codex-lifecycle', 'codex-sandbox-controller',
|
|
222
221
|
'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
|
|
223
222
|
'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
|
|
224
223
|
'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
|
|
@@ -342,28 +341,39 @@ export function resolveSandboxControlTransport(
|
|
|
342
341
|
env: NodeJS.ProcessEnv = process.env,
|
|
343
342
|
options: Readonly<{ statusMountPath?: string }> = {}
|
|
344
343
|
): SandboxControlTransportDecision {
|
|
345
|
-
const configuredStatusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
346
|
-
const fixedStatusDir = options.statusMountPath
|
|
347
|
-
?? SANDBOX_CONTROL_STATUS_MOUNT;
|
|
348
344
|
const hasAnyMarker = TASK_MARKER_KEYS.some((key) => Boolean(env[key]))
|
|
349
345
|
|| Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING)
|
|
350
346
|
|| Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
|
|
351
|
-
const
|
|
347
|
+
const fixedStatusDir = options.statusMountPath
|
|
348
|
+
?? SANDBOX_CONTROL_STATUS_MOUNT;
|
|
349
|
+
const fixedStatusProbe = path.isAbsolute(fixedStatusDir)
|
|
352
350
|
? nativeDirectoryProbe(fixedStatusDir) : 'absent';
|
|
353
351
|
if (fixedStatusProbe === 'unknown') {
|
|
354
352
|
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
355
353
|
}
|
|
356
354
|
const fixedStatusMounted = fixedStatusProbe === 'present';
|
|
355
|
+
// A fixed status mount is installed only in a task-bound sandbox. Its
|
|
356
|
+
// presence is not mutable by the sandbox process, so marker removal must
|
|
357
|
+
// never make that process look like a direct host invocation.
|
|
358
|
+
if (!hasAnyMarker) {
|
|
359
|
+
return fixedStatusMounted
|
|
360
|
+
? { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' }
|
|
361
|
+
: { kind: 'direct-host', reasonCode: null };
|
|
362
|
+
}
|
|
363
|
+
const hasCompleteConfig = TASK_CONTROL_CONFIG_KEYS.every((key) => Boolean(env[key]));
|
|
364
|
+
if (!hasCompleteConfig) {
|
|
365
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
366
|
+
}
|
|
367
|
+
const configuredStatusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
357
368
|
const configuredStatusProbe = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
358
369
|
? nativeDirectoryProbe(configuredStatusDir) : 'absent';
|
|
359
370
|
if (configuredStatusProbe === 'unknown') {
|
|
360
371
|
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
361
372
|
}
|
|
362
|
-
const statusDir = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
363
|
-
? configuredStatusDir
|
|
373
|
+
const statusDir = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
374
|
+
? configuredStatusProbe === 'present' ? configuredStatusDir : null
|
|
364
375
|
: fixedStatusMounted ? fixedStatusDir : null;
|
|
365
376
|
const statusMounted = statusDir !== null;
|
|
366
|
-
const hasCompleteConfig = TASK_CONTROL_CONFIG_KEYS.every((key) => Boolean(env[key]));
|
|
367
377
|
if (env.AGENT_INFRA_EXECUTOR_MANIFEST || env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING) {
|
|
368
378
|
return { kind: 'fail-closed', reasonCode: 'TASK_CONTROL_TRANSPORT_INVALID' };
|
|
369
379
|
}
|
|
@@ -20,11 +20,11 @@ function parseFailure(error: unknown): string {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async function taskOrchestration(args: string[] = []): Promise<void> {
|
|
23
|
-
if (!ensureInternalHandlerRoute('task-orchestration', args)) return;
|
|
24
23
|
if (args[0] === '--help' || args[0] === '-h') {
|
|
25
24
|
process.stdout.write(USAGE);
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
27
|
+
if (!ensureInternalHandlerRoute('task-orchestration', args)) return;
|
|
28
28
|
|
|
29
29
|
const route = internalHandlerRoute('task-orchestration', 'status', args[1] ?? '')
|
|
30
30
|
? 'status'
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { parseReviewCommand } from '../task/review-command.ts';
|
|
2
|
-
import { finalizeReviewSummary } from '../task/review-finalization.ts';
|
|
2
|
+
import { finalizeReviewSummary, preflightReviewSummary } from '../task/review-finalization.ts';
|
|
3
3
|
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.ts';
|
|
4
4
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
5
5
|
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
|
|
6
6
|
import { ensureInternalHandlerRoute } from './cli-route-inventory.ts';
|
|
7
7
|
|
|
8
|
-
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> finalize-summary --stage <analysis|plan|code> --artifact <review-*.md> [--recovery-id <id>] [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
8
|
+
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> <preflight|finalize-summary> --stage <analysis|plan|code> --artifact <review-*.md> [--recovery-id <id>] [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
9
9
|
|
|
10
10
|
function failUsage(message: string): void {
|
|
11
11
|
process.stdout.write(`${JSON.stringify({
|
|
12
12
|
status: 'failed',
|
|
13
13
|
changed: false,
|
|
14
|
-
intent: '
|
|
14
|
+
intent: 'review-operation',
|
|
15
15
|
error: { code: 'REVIEW_PAYLOAD_INVALID', message }
|
|
16
16
|
})}\n`);
|
|
17
17
|
process.stderr.write(USAGE);
|
|
@@ -28,6 +28,7 @@ async function taskReview(args: string[] = []): Promise<void> {
|
|
|
28
28
|
try { request = parseReviewCommand(args); }
|
|
29
29
|
catch (error) { failUsage(error instanceof Error ? error.message : String(error)); return; }
|
|
30
30
|
const { stage, artifact, recoveryId, dryRun, orchestrated, overrideTicket, overrideTarget, overrideScope } = request;
|
|
31
|
+
const operation = args[1]!;
|
|
31
32
|
const dryRunConflict = overrideDryRunConflict({ dryRun, overrideTicket, overrideTarget, overrideScope });
|
|
32
33
|
if (dryRunConflict) { failUsage(dryRunConflict.message); return; }
|
|
33
34
|
const resolved = resolveTaskRef(args[0]!);
|
|
@@ -35,8 +36,11 @@ async function taskReview(args: string[] = []): Promise<void> {
|
|
|
35
36
|
let result;
|
|
36
37
|
let humanOverride: unknown = null;
|
|
37
38
|
try {
|
|
38
|
-
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId,
|
|
39
|
-
let current =
|
|
39
|
+
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-review.${operation}`, async () => {
|
|
40
|
+
let current = operation === 'preflight'
|
|
41
|
+
? preflightReviewSummary({ taskRef: args[0]!, stage, artifact, recoveryId, orchestrated, dryRun }, { lockAlreadyHeld: true })
|
|
42
|
+
: finalizeReviewSummary({ taskRef: args[0]!, stage, artifact, recoveryId, orchestrated, dryRun }, { lockAlreadyHeld: true });
|
|
43
|
+
if (operation === 'preflight') return current;
|
|
40
44
|
if (current.status !== 'failed' || !overrideTicket) return current;
|
|
41
45
|
if (!overrideTarget || !overrideScope) { failUsage('override ticket requires target and scope'); return current; }
|
|
42
46
|
const consumed = await consumeHumanOverride({
|