@overlordai/worker 1.0.173 → 1.0.175
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/dist/capability-detector.d.ts.map +1 -1
- package/dist/capability-detector.js +1 -0
- package/dist/capability-detector.js.map +1 -1
- package/dist/executor/claude.executor.d.ts +1 -1
- package/dist/executor/claude.executor.d.ts.map +1 -1
- package/dist/executor/claude.executor.js +11 -11
- package/dist/executor/claude.executor.js.map +1 -1
- package/dist/executor/executor.factory.d.ts.map +1 -1
- package/dist/executor/executor.factory.js +3 -0
- package/dist/executor/executor.factory.js.map +1 -1
- package/dist/executor/pi.executor.d.ts +24 -0
- package/dist/executor/pi.executor.d.ts.map +1 -0
- package/dist/executor/pi.executor.js +119 -0
- package/dist/executor/pi.executor.js.map +1 -0
- package/dist/pipeline-compiler.d.ts.map +1 -1
- package/dist/pipeline-compiler.js +11 -1
- package/dist/pipeline-compiler.js.map +1 -1
- package/dist/pty-stdin-writer.d.ts +1 -1
- package/dist/pty-stdin-writer.js +2 -2
- package/dist/pty-stdin-writer.js.map +1 -1
- package/dist/review-report-parser.d.ts +33 -0
- package/dist/review-report-parser.d.ts.map +1 -0
- package/dist/review-report-parser.js +212 -0
- package/dist/review-report-parser.js.map +1 -0
- package/dist/task-handler.d.ts.map +1 -1
- package/dist/task-handler.js +93 -127
- package/dist/task-handler.js.map +1 -1
- package/dist/workspace-manager.js +1 -1
- package/dist/workspace-manager.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.REVIEW_REPORT_PARSE_FAILURE_SUMMARY = void 0;
|
|
4
|
+
exports.tryParseStructuredReviewReport = tryParseStructuredReviewReport;
|
|
5
|
+
exports.extractJsonObjectCandidate = extractJsonObjectCandidate;
|
|
6
|
+
exports.parseReviewReportOutput = parseReviewReportOutput;
|
|
7
|
+
exports.extractFallbackReviewSummary = extractFallbackReviewSummary;
|
|
8
|
+
exports.extractReviewReport = extractReviewReport;
|
|
9
|
+
const OVERLORD_MARKER_LINE_RE = /\[\[OVERLORD:[^\n]*\]\]/g;
|
|
10
|
+
const REVIEW_REPORT_PARSE_FAILURE_SUMMARY = 'Structured review report could not be parsed. Findings may be incomplete.';
|
|
11
|
+
exports.REVIEW_REPORT_PARSE_FAILURE_SUMMARY = REVIEW_REPORT_PARSE_FAILURE_SUMMARY;
|
|
12
|
+
const REVIEW_REPORT_PARSE_FAILURE_ISSUE = {
|
|
13
|
+
severity: 'warning',
|
|
14
|
+
file: 'report_output',
|
|
15
|
+
description: 'Review agent did not emit a valid structured report for the report stage.',
|
|
16
|
+
suggestion: 'Emit a report RESULT marker or a single valid JSON object between report START and DONE.',
|
|
17
|
+
};
|
|
18
|
+
// Pre-compiled regexes for noise filtering (avoid re-creation per line)
|
|
19
|
+
const THINKING_LINE_RE = /^[*✢·✶✻✽]?\s*(thinking|pondering|sautéing|reasoning)\b/i;
|
|
20
|
+
const EMPTY_PROMPT_RE = /^[❯›>]\s*$/;
|
|
21
|
+
const BYPASS_PERMS_RE = /^[⏵❯›>]+\s*bypass\s*permissions\s*(on|off|mode)\b/i;
|
|
22
|
+
function normalizeReviewSummary(summary) {
|
|
23
|
+
if (typeof summary === 'string') {
|
|
24
|
+
return summary;
|
|
25
|
+
}
|
|
26
|
+
if (summary == null) {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(summary);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return String(summary);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function tryParseStructuredReviewReport(raw) {
|
|
37
|
+
const trimmed = raw.trim();
|
|
38
|
+
if (!trimmed) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(trimmed);
|
|
43
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const parsedObj = parsed;
|
|
47
|
+
// Require at least one of the expected fields to avoid matching
|
|
48
|
+
// random JSON objects (e.g. {} from code snippets).
|
|
49
|
+
if (!('summary' in parsedObj) && !('issues' in parsedObj)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
summary: normalizeReviewSummary(parsedObj.summary),
|
|
54
|
+
issues: Array.isArray(parsedObj.issues) ? parsedObj.issues : [],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function extractJsonObjectCandidate(raw) {
|
|
62
|
+
const firstBrace = raw.indexOf('{');
|
|
63
|
+
const lastBrace = raw.lastIndexOf('}');
|
|
64
|
+
if (firstBrace === -1 || lastBrace === -1 || firstBrace >= lastBrace) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return raw.slice(firstBrace, lastBrace + 1).trim();
|
|
68
|
+
}
|
|
69
|
+
const EXAMPLE_CONTEXT_RE = /\b(?:example|schema|shape|expected\s+(?:format|json|output)|template|placeholder|sample)\b/i;
|
|
70
|
+
function isLikelyExampleContext(textBeforeFence) {
|
|
71
|
+
// Check the last ~200 chars before a fence for example/schema indicators
|
|
72
|
+
return EXAMPLE_CONTEXT_RE.test(textBeforeFence);
|
|
73
|
+
}
|
|
74
|
+
function parseReviewReportOutput(raw, stripMarkers = false) {
|
|
75
|
+
if (!raw?.trim()) {
|
|
76
|
+
return { kind: 'empty' };
|
|
77
|
+
}
|
|
78
|
+
const base = stripMarkers ? raw.replace(OVERLORD_MARKER_LINE_RE, ' ') : raw;
|
|
79
|
+
const trimmed = base.trim();
|
|
80
|
+
if (!trimmed) {
|
|
81
|
+
return { kind: 'empty' };
|
|
82
|
+
}
|
|
83
|
+
// Detect code fences — both complete (```...```) and unterminated (```json\n{...).
|
|
84
|
+
// An unterminated fence indicates truncated output and should be treated as malformed.
|
|
85
|
+
const backtickCount = (trimmed.match(/```/g) ?? []).length;
|
|
86
|
+
const hasAnyFenceOpener = backtickCount > 0;
|
|
87
|
+
const hasUnterminatedFence = hasAnyFenceOpener && backtickCount % 2 !== 0;
|
|
88
|
+
// Try ALL complete code fences. Only accept the LAST fence that parses as a
|
|
89
|
+
// valid structured report AND is not preceded by example/schema context.
|
|
90
|
+
// Earlier fences are more likely to be diffs or schema illustrations.
|
|
91
|
+
const fenceRegex = /```(?:json)?\s*([\s\S]*?)```/gi;
|
|
92
|
+
let fenceMatch;
|
|
93
|
+
let hasCompleteFence = false;
|
|
94
|
+
let lastStructuredFromFence = null;
|
|
95
|
+
let hasAnyStructuredFence = false;
|
|
96
|
+
let previousFenceEnd = 0;
|
|
97
|
+
while ((fenceMatch = fenceRegex.exec(trimmed)) !== null) {
|
|
98
|
+
hasCompleteFence = true;
|
|
99
|
+
const fenceContent = fenceMatch[1]?.trim();
|
|
100
|
+
const currentFenceStart = fenceMatch.index;
|
|
101
|
+
if (!fenceContent) {
|
|
102
|
+
previousFenceEnd = currentFenceStart + fenceMatch[0].length;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
let structured = null;
|
|
106
|
+
structured = tryParseStructuredReviewReport(fenceContent);
|
|
107
|
+
if (!structured) {
|
|
108
|
+
const extracted = extractJsonObjectCandidate(fenceContent);
|
|
109
|
+
if (extracted) {
|
|
110
|
+
structured = tryParseStructuredReviewReport(extracted);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (structured) {
|
|
114
|
+
hasAnyStructuredFence = true;
|
|
115
|
+
// Check only the text BETWEEN the previous fence end and this fence
|
|
116
|
+
// start for example/schema indicators — avoids matching context from
|
|
117
|
+
// earlier unrelated fences.
|
|
118
|
+
const gapText = trimmed.slice(previousFenceEnd, currentFenceStart);
|
|
119
|
+
if (!isLikelyExampleContext(gapText)) {
|
|
120
|
+
lastStructuredFromFence = structured;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
previousFenceEnd = currentFenceStart + fenceMatch[0].length;
|
|
124
|
+
}
|
|
125
|
+
if (lastStructuredFromFence) {
|
|
126
|
+
return { kind: 'structured', report: lastStructuredFromFence };
|
|
127
|
+
}
|
|
128
|
+
// All structured fences had example/schema context — treat as malformed
|
|
129
|
+
if (hasAnyStructuredFence) {
|
|
130
|
+
return { kind: 'malformed' };
|
|
131
|
+
}
|
|
132
|
+
// Try the full text directly (handles JSON not wrapped in a code fence)
|
|
133
|
+
const directStructured = tryParseStructuredReviewReport(trimmed);
|
|
134
|
+
if (directStructured) {
|
|
135
|
+
return { kind: 'structured', report: directStructured };
|
|
136
|
+
}
|
|
137
|
+
// Try extracting a JSON object from anywhere in the full text
|
|
138
|
+
const extractedJson = extractJsonObjectCandidate(trimmed);
|
|
139
|
+
if (extractedJson) {
|
|
140
|
+
const extractedStructured = tryParseStructuredReviewReport(extractedJson);
|
|
141
|
+
if (extractedStructured) {
|
|
142
|
+
return { kind: 'structured', report: extractedStructured };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Detect failed JSON attempts — the text looks like it tried to be structured
|
|
146
|
+
// but couldn't be parsed
|
|
147
|
+
const looksStructured = hasCompleteFence || hasUnterminatedFence
|
|
148
|
+
|| /"issues"\s*:|"summary"\s*:/.test(trimmed)
|
|
149
|
+
|| trimmed.startsWith('{');
|
|
150
|
+
if (looksStructured) {
|
|
151
|
+
return { kind: 'malformed' };
|
|
152
|
+
}
|
|
153
|
+
return { kind: 'summary', summary: trimmed };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Strip agent terminal noise (thinking indicators, status lines, prompt markers)
|
|
157
|
+
* and return useful review text. Returns null if nothing useful remains.
|
|
158
|
+
*/
|
|
159
|
+
function extractFallbackReviewSummary(text) {
|
|
160
|
+
const lines = text.split('\n').filter(line => {
|
|
161
|
+
const t = line.trim();
|
|
162
|
+
if (!t)
|
|
163
|
+
return false;
|
|
164
|
+
if (THINKING_LINE_RE.test(t))
|
|
165
|
+
return false;
|
|
166
|
+
if (EMPTY_PROMPT_RE.test(t))
|
|
167
|
+
return false;
|
|
168
|
+
if (BYPASS_PERMS_RE.test(t))
|
|
169
|
+
return false;
|
|
170
|
+
return true;
|
|
171
|
+
});
|
|
172
|
+
const useful = lines.join('\n').trim();
|
|
173
|
+
if (useful.length < 20)
|
|
174
|
+
return null;
|
|
175
|
+
return useful.length > 2000 ? useful.slice(-2000) : useful;
|
|
176
|
+
}
|
|
177
|
+
function buildMalformedReviewReport() {
|
|
178
|
+
return {
|
|
179
|
+
summary: REVIEW_REPORT_PARSE_FAILURE_SUMMARY,
|
|
180
|
+
issues: [{ ...REVIEW_REPORT_PARSE_FAILURE_ISSUE }],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function extractReviewReport(resultPayload, cleanOutput) {
|
|
184
|
+
const rawCleanOutput = cleanOutput?.trim() ?? '';
|
|
185
|
+
// Pre-strip markers from cleanOutput once for prose fallback. Keep the raw
|
|
186
|
+
// output too so structured RESULT marker payloads remain recoverable when
|
|
187
|
+
// event.result is unavailable upstream.
|
|
188
|
+
const strippedCleanOutput = cleanOutput
|
|
189
|
+
? cleanOutput.replace(OVERLORD_MARKER_LINE_RE, ' ').trim()
|
|
190
|
+
: '';
|
|
191
|
+
const candidates = [
|
|
192
|
+
parseReviewReportOutput(resultPayload),
|
|
193
|
+
parseReviewReportOutput(rawCleanOutput),
|
|
194
|
+
parseReviewReportOutput(strippedCleanOutput),
|
|
195
|
+
];
|
|
196
|
+
for (const candidate of candidates) {
|
|
197
|
+
if (candidate.kind === 'structured') {
|
|
198
|
+
return candidate.report;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const candidate of [
|
|
202
|
+
candidates[0],
|
|
203
|
+
candidates[2],
|
|
204
|
+
]) {
|
|
205
|
+
if (candidate.kind === 'summary') {
|
|
206
|
+
return { summary: candidate.summary, issues: [] };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Both candidates were either empty or malformed — surface parse failure.
|
|
210
|
+
return buildMalformedReviewReport();
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=review-report-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-report-parser.js","sourceRoot":"","sources":["../src/review-report-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-handler.d.ts","sourceRoot":"","sources":["../src/task-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAE,cAAc,EAAkB,mBAAmB,EAAE,qBAAqB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAiB5N,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"task-handler.d.ts","sourceRoot":"","sources":["../src/task-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/task-handler.js
CHANGED
|
@@ -52,20 +52,21 @@ const stage_monitor_js_1 = require("./stage-monitor.js");
|
|
|
52
52
|
const pty_stdin_writer_js_1 = require("./pty-stdin-writer.js");
|
|
53
53
|
const git_token_helper_js_1 = require("./git-token-helper.js");
|
|
54
54
|
const ansi_stripper_js_1 = require("./ansi-stripper.js");
|
|
55
|
+
const review_report_parser_js_1 = require("./review-report-parser.js");
|
|
55
56
|
const log = (0, pino_1.default)({ name: 'task-handler' });
|
|
56
57
|
const REDACTED = '***REDACTED***';
|
|
57
58
|
const PROMPT_LOG_TAIL_CHARS = 600;
|
|
58
59
|
const AUTH_ERROR_SCAN_TAIL_CHARS = 2000;
|
|
59
|
-
const OVERLORD_MARKER_LINE_RE = /\[\[OVERLORD:[^\n]*\]\]/g;
|
|
60
60
|
const MAX_FINDING_DETAIL = 4000;
|
|
61
61
|
const MIN_AUTOMATION_OUTPUT_LENGTH = 20;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
62
|
+
function waitForQueuedPtyOutput() {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
// A nested setImmediate yields through the current check phase and the
|
|
65
|
+
// next event-loop iteration, giving already-queued PTY onData callbacks a
|
|
66
|
+
// chance to run before we finalize task completion from onExit.
|
|
67
|
+
setImmediate(() => setImmediate(resolve));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
69
70
|
/**
|
|
70
71
|
* Detect startup prompts from agent CLIs that block execution and return
|
|
71
72
|
* the keystroke to dismiss them. Returns null if no blocking prompt detected.
|
|
@@ -174,114 +175,19 @@ function buildPromptWaitDiagnostics(ptySession) {
|
|
|
174
175
|
function autoConfirmStartupPrompt(taskId, ptySession, handledSignatures, logMessage) {
|
|
175
176
|
const promptAction = detectStartupPromptAction(ptySession.cleanOutput);
|
|
176
177
|
if (!promptAction) {
|
|
177
|
-
return 'none';
|
|
178
|
+
return { action: 'none' };
|
|
178
179
|
}
|
|
179
180
|
if (handledSignatures.has(promptAction.signature)) {
|
|
180
181
|
// Repeated dialog redraws can leave stale trust/bypass text in the buffer.
|
|
181
182
|
// Clear them so a later real prompt is detectable.
|
|
182
183
|
ptySession.resetCleanOutput();
|
|
183
|
-
return 'duplicate';
|
|
184
|
+
return { action: 'duplicate', signature: promptAction.signature };
|
|
184
185
|
}
|
|
185
186
|
handledSignatures.add(promptAction.signature);
|
|
186
187
|
log.info({ taskId, trustResponse: promptAction.response }, logMessage);
|
|
187
188
|
ptySession.pty.write(promptAction.response);
|
|
188
189
|
ptySession.resetCleanOutput();
|
|
189
|
-
return 'handled';
|
|
190
|
-
}
|
|
191
|
-
function normalizeReviewSummary(summary) {
|
|
192
|
-
if (typeof summary === 'string') {
|
|
193
|
-
return summary;
|
|
194
|
-
}
|
|
195
|
-
if (summary == null) {
|
|
196
|
-
return '';
|
|
197
|
-
}
|
|
198
|
-
try {
|
|
199
|
-
return JSON.stringify(summary);
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
return String(summary);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
function tryParseStructuredReviewReport(raw) {
|
|
206
|
-
const trimmed = raw.trim();
|
|
207
|
-
if (!trimmed) {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
try {
|
|
211
|
-
const parsed = JSON.parse(trimmed);
|
|
212
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
const parsedObj = parsed;
|
|
216
|
-
return {
|
|
217
|
-
summary: normalizeReviewSummary(parsedObj.summary),
|
|
218
|
-
issues: Array.isArray(parsedObj.issues) ? parsedObj.issues : [],
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
function extractJsonObjectCandidate(raw) {
|
|
226
|
-
const firstBrace = raw.indexOf('{');
|
|
227
|
-
const lastBrace = raw.lastIndexOf('}');
|
|
228
|
-
if (firstBrace === -1 || lastBrace === -1 || firstBrace >= lastBrace) {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
return raw.slice(firstBrace, lastBrace + 1).trim();
|
|
232
|
-
}
|
|
233
|
-
function parseReviewReportOutput(raw, stripMarkers = false) {
|
|
234
|
-
if (!raw?.trim()) {
|
|
235
|
-
return { kind: 'empty' };
|
|
236
|
-
}
|
|
237
|
-
const base = stripMarkers ? raw.replace(OVERLORD_MARKER_LINE_RE, ' ') : raw;
|
|
238
|
-
const trimmed = base.trim();
|
|
239
|
-
if (!trimmed) {
|
|
240
|
-
return { kind: 'empty' };
|
|
241
|
-
}
|
|
242
|
-
const fencedMatch = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
243
|
-
const candidate = (fencedMatch?.[1] ?? trimmed).trim();
|
|
244
|
-
const directStructured = tryParseStructuredReviewReport(candidate);
|
|
245
|
-
if (directStructured) {
|
|
246
|
-
return { kind: 'structured', report: directStructured };
|
|
247
|
-
}
|
|
248
|
-
const extractedJson = extractJsonObjectCandidate(candidate);
|
|
249
|
-
if (extractedJson) {
|
|
250
|
-
const extractedStructured = tryParseStructuredReviewReport(extractedJson);
|
|
251
|
-
if (extractedStructured) {
|
|
252
|
-
return { kind: 'structured', report: extractedStructured };
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
const looksStructured = /```(?:json)?/i.test(trimmed)
|
|
256
|
-
|| /"issues"\s*:|"summary"\s*:/.test(candidate)
|
|
257
|
-
|| candidate.startsWith('{');
|
|
258
|
-
if (looksStructured) {
|
|
259
|
-
return { kind: 'malformed' };
|
|
260
|
-
}
|
|
261
|
-
return { kind: 'summary', summary: candidate };
|
|
262
|
-
}
|
|
263
|
-
function buildMalformedReviewReport() {
|
|
264
|
-
return {
|
|
265
|
-
summary: REVIEW_REPORT_PARSE_FAILURE_SUMMARY,
|
|
266
|
-
issues: [{ ...REVIEW_REPORT_PARSE_FAILURE_ISSUE }],
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
function extractReviewReport(resultPayload, cleanOutput) {
|
|
270
|
-
const candidates = [
|
|
271
|
-
parseReviewReportOutput(resultPayload),
|
|
272
|
-
parseReviewReportOutput(cleanOutput, true),
|
|
273
|
-
];
|
|
274
|
-
for (const candidate of candidates) {
|
|
275
|
-
if (candidate.kind === 'structured') {
|
|
276
|
-
return candidate.report;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
for (const candidate of candidates) {
|
|
280
|
-
if (candidate.kind === 'summary') {
|
|
281
|
-
return { summary: candidate.summary, issues: [] };
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return buildMalformedReviewReport();
|
|
190
|
+
return { action: 'handled', signature: promptAction.signature };
|
|
285
191
|
}
|
|
286
192
|
class TaskHandler {
|
|
287
193
|
activeTasks = new Map();
|
|
@@ -541,7 +447,10 @@ class TaskHandler {
|
|
|
541
447
|
// Auto-confirm startup prompts (trust dialogs, bypass permissions, etc.)
|
|
542
448
|
// that appear before waitForPrompt registers its own checkReady handler.
|
|
543
449
|
if (ctx.startupPromptHandlingEnabled) {
|
|
544
|
-
autoConfirmStartupPrompt(taskId, ptySession, ctx.handledStartupPromptSignatures, 'Detected startup prompt (early), auto-confirming');
|
|
450
|
+
const result = autoConfirmStartupPrompt(taskId, ptySession, ctx.handledStartupPromptSignatures, 'Detected startup prompt (early), auto-confirming');
|
|
451
|
+
if (result.action === 'handled' && result.signature === 'claude-mcp-server-decline') {
|
|
452
|
+
this.onProgress(taskId, ctx.stage, 'running', 'Declined repo MCP server declared in .mcp.json — task will continue without it');
|
|
453
|
+
}
|
|
545
454
|
}
|
|
546
455
|
};
|
|
547
456
|
ctx.ptyDataHandler = ptyDataHandler;
|
|
@@ -847,7 +756,7 @@ class TaskHandler {
|
|
|
847
756
|
// 5. Create StageMonitor
|
|
848
757
|
const monitor = new stage_monitor_js_1.StageMonitor(effectivePipeline, nonce);
|
|
849
758
|
ctx.stageMonitor = monitor;
|
|
850
|
-
//
|
|
759
|
+
// 6. Wait for agent to be ready (detect prompt character)
|
|
851
760
|
await new Promise((resolve, reject) => {
|
|
852
761
|
const PROMPT_TIMEOUT_MS = 180_000;
|
|
853
762
|
let settled = false;
|
|
@@ -881,9 +790,13 @@ class TaskHandler {
|
|
|
881
790
|
abortController.signal.addEventListener('abort', onAbort, { once: true });
|
|
882
791
|
const handleStartupPrompt = () => {
|
|
883
792
|
if (!ctx.startupPromptHandlingEnabled) {
|
|
884
|
-
return 'none';
|
|
793
|
+
return { action: 'none' };
|
|
885
794
|
}
|
|
886
|
-
|
|
795
|
+
const result = autoConfirmStartupPrompt(taskId, ptySession, ctx.handledStartupPromptSignatures, 'Auto-accepting workspace trust prompt (checkReady)');
|
|
796
|
+
if (result.action === 'handled' && result.signature === 'claude-mcp-server-decline') {
|
|
797
|
+
this.onProgress(taskId, ctx.stage, 'running', 'Declined repo MCP server declared in .mcp.json — task will continue without it');
|
|
798
|
+
}
|
|
799
|
+
return result;
|
|
887
800
|
};
|
|
888
801
|
const detectPromptFromLatestChunk = (data) => {
|
|
889
802
|
const cleanData = (0, ansi_stripper_js_1.stripAnsi)(data);
|
|
@@ -939,8 +852,8 @@ class TaskHandler {
|
|
|
939
852
|
// Auto-confirm startup prompts exactly once per prompt type. While a
|
|
940
853
|
// prompt is visible, skip ready detection — the dialog's menu cursor
|
|
941
854
|
// is not the agent's interactive prompt.
|
|
942
|
-
const
|
|
943
|
-
if (
|
|
855
|
+
const { action: startupAction } = handleStartupPrompt();
|
|
856
|
+
if (startupAction === 'handled') {
|
|
944
857
|
return;
|
|
945
858
|
}
|
|
946
859
|
// Check accumulated output for agent prompt
|
|
@@ -948,13 +861,12 @@ class TaskHandler {
|
|
|
948
861
|
resolvePromptDetected();
|
|
949
862
|
return;
|
|
950
863
|
}
|
|
951
|
-
// If an earlier handler
|
|
952
|
-
// the shared buffer, the
|
|
953
|
-
//
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
}
|
|
864
|
+
// If an earlier handler (ptyDataHandler or a previous checkReady call)
|
|
865
|
+
// cleared startup-dialog text from the shared buffer, the accumulated
|
|
866
|
+
// cleanOutput may be empty while the current PTY chunk still contains
|
|
867
|
+
// the real prompt. Check the latest chunk tail as a fallback.
|
|
868
|
+
if (detectPromptFromLatestChunk(_data)) {
|
|
869
|
+
resolvePromptDetected();
|
|
958
870
|
}
|
|
959
871
|
};
|
|
960
872
|
ptySession.onData(checkReady);
|
|
@@ -975,6 +887,43 @@ class TaskHandler {
|
|
|
975
887
|
let planSpecPushCompleted = false;
|
|
976
888
|
let planSpecPushFailed = false;
|
|
977
889
|
let automationFindingsReported = false;
|
|
890
|
+
let reviewReportSent = false;
|
|
891
|
+
const emitReviewReport = (report) => {
|
|
892
|
+
if (taskMode !== 'review' || !this.onReviewReport || reviewReportSent)
|
|
893
|
+
return;
|
|
894
|
+
reviewReportSent = true;
|
|
895
|
+
this.onReviewReport(taskId, report);
|
|
896
|
+
};
|
|
897
|
+
// Ensure the review report is extracted and sent before a review task
|
|
898
|
+
// reaches terminal state. This is the single place that guarantees the
|
|
899
|
+
// report is delivered regardless of *how* the task completes (normal
|
|
900
|
+
// pipeline_completed, process exit, idle timeout, etc.).
|
|
901
|
+
const ensureReviewReportSent = () => {
|
|
902
|
+
if (taskMode !== 'review' || !this.onReviewReport || reviewReportSent)
|
|
903
|
+
return;
|
|
904
|
+
// Prefer the RESULT payload captured by StageMonitor for the report stage
|
|
905
|
+
const state = monitor.getState();
|
|
906
|
+
const reportStage = state.stages.find(s => s.name === 'report');
|
|
907
|
+
const resultPayload = reportStage?.result ?? null;
|
|
908
|
+
// Only attempt extraction when the report stage was actually reached.
|
|
909
|
+
// Without this guard, completion paths like "idle at prompt with no
|
|
910
|
+
// markers" would feed terminal noise (shell prompt, bypass status)
|
|
911
|
+
// into extractReviewReport, which treats any non-empty text as a
|
|
912
|
+
// valid zero-issue summary — creating a false-success report.
|
|
913
|
+
if (!resultPayload && (!reportStage || reportStage.status === 'pending')) {
|
|
914
|
+
log.warn({ taskId }, 'Review report stage was never reached — sending parse-failure placeholder');
|
|
915
|
+
emitReviewReport({
|
|
916
|
+
summary: review_report_parser_js_1.REVIEW_REPORT_PARSE_FAILURE_SUMMARY,
|
|
917
|
+
issues: [{ severity: 'warning', file: 'report_output', description: 'Review agent did not produce a report.', suggestion: 'The report stage was never reached.' }],
|
|
918
|
+
});
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const report = (0, review_report_parser_js_1.extractReviewReport)(resultPayload, ptySession.cleanOutput);
|
|
922
|
+
if (report.summary === review_report_parser_js_1.REVIEW_REPORT_PARSE_FAILURE_SUMMARY) {
|
|
923
|
+
log.warn({ taskId }, 'Review report was missing or malformed at task completion — recorded a warning issue');
|
|
924
|
+
}
|
|
925
|
+
emitReviewReport(report);
|
|
926
|
+
};
|
|
978
927
|
const settle = (fn) => {
|
|
979
928
|
if (!settled) {
|
|
980
929
|
settled = true;
|
|
@@ -1008,6 +957,7 @@ class TaskHandler {
|
|
|
1008
957
|
if (!monitor.hasReceivedAnyMarker()) {
|
|
1009
958
|
log.info({ taskId, idleMs }, 'Agent idle at prompt with no pipeline markers — treating as completed');
|
|
1010
959
|
ctx.stage = 'completed';
|
|
960
|
+
ensureReviewReportSent();
|
|
1011
961
|
this.reportCompletionWithCommitSha(taskId, 'Task completed (agent idle, no markers)', ctx.workspaceInfo?.path, settle, resolve);
|
|
1012
962
|
return;
|
|
1013
963
|
}
|
|
@@ -1016,6 +966,7 @@ class TaskHandler {
|
|
|
1016
966
|
if (monitor.hasAttemptedReinjection()) {
|
|
1017
967
|
log.info({ taskId, idleMs }, 'Agent idle at prompt after reinjection — treating as completed');
|
|
1018
968
|
ctx.stage = 'completed';
|
|
969
|
+
ensureReviewReportSent();
|
|
1019
970
|
this.reportCompletionWithCommitSha(taskId, 'Task completed (agent idle after reinjection)', ctx.workspaceInfo?.path, settle, resolve);
|
|
1020
971
|
return;
|
|
1021
972
|
}
|
|
@@ -1044,15 +995,15 @@ class TaskHandler {
|
|
|
1044
995
|
}
|
|
1045
996
|
this.onProgress(taskId, event.stage, 'stage_done', `Stage completed: ${event.stage}`, extra);
|
|
1046
997
|
// When the report stage completes in review mode, fire the review report callback.
|
|
1047
|
-
if (taskMode === 'review' && event.stage === 'report' &&
|
|
1048
|
-
const report = extractReviewReport(event.result, ptySession.cleanOutput);
|
|
1049
|
-
if (report.summary === REVIEW_REPORT_PARSE_FAILURE_SUMMARY) {
|
|
998
|
+
if (taskMode === 'review' && event.stage === 'report' && !reviewReportSent) {
|
|
999
|
+
const report = (0, review_report_parser_js_1.extractReviewReport)(event.result, ptySession.cleanOutput);
|
|
1000
|
+
if (report.summary === review_report_parser_js_1.REVIEW_REPORT_PARSE_FAILURE_SUMMARY) {
|
|
1050
1001
|
log.warn({ taskId }, 'Review report was missing or malformed — recorded a warning issue instead of an empty report');
|
|
1051
1002
|
}
|
|
1052
1003
|
else if (!report.summary && report.issues.length === 0) {
|
|
1053
1004
|
log.warn({ taskId }, 'Review report has no issues — agent may have failed to produce structured output');
|
|
1054
1005
|
}
|
|
1055
|
-
|
|
1006
|
+
emitReviewReport(report);
|
|
1056
1007
|
}
|
|
1057
1008
|
// Plan mode: publish the spec before the confirm dialog is shown.
|
|
1058
1009
|
if (taskMode === 'plan' && event.stage === 'document') {
|
|
@@ -1181,6 +1132,8 @@ class TaskHandler {
|
|
|
1181
1132
|
}
|
|
1182
1133
|
case 'pipeline_completed':
|
|
1183
1134
|
ctx.stage = 'completed';
|
|
1135
|
+
// Ensure review report is sent before the task is marked complete
|
|
1136
|
+
ensureReviewReportSent();
|
|
1184
1137
|
// Capture final commit SHA for all task modes — develop tasks use it to pin
|
|
1185
1138
|
// review tasks; review tasks (fix stage) use it to update the source task's SHA
|
|
1186
1139
|
// so the next review round pins to post-fix code.
|
|
@@ -1212,8 +1165,9 @@ class TaskHandler {
|
|
|
1212
1165
|
ptySession.onData((data) => {
|
|
1213
1166
|
lastPtyOutputTime = Date.now();
|
|
1214
1167
|
const stripped = (0, ansi_stripper_js_1.stripAnsi)(data);
|
|
1215
|
-
// Keyword pre-filter: only run full regex detection when output looks relevant
|
|
1216
|
-
|
|
1168
|
+
// Keyword pre-filter: only run full regex detection when output looks relevant.
|
|
1169
|
+
// Includes Chinese patterns for Zhipu API errors (令牌/余额/验证).
|
|
1170
|
+
if (/rate|limit|quota|429|auth|login|exceeded|exhausted|hit your|令牌|余额|验证|insufficient|expired/i.test(stripped)) {
|
|
1217
1171
|
const authError = detectRecentAuthError(ptySession, false);
|
|
1218
1172
|
if (authError) {
|
|
1219
1173
|
log.warn({
|
|
@@ -1232,8 +1186,20 @@ class TaskHandler {
|
|
|
1232
1186
|
ptySession.onExit((exitCode) => {
|
|
1233
1187
|
const status = monitor.handleProcessExit(exitCode);
|
|
1234
1188
|
if (status === 'completed') {
|
|
1235
|
-
|
|
1236
|
-
|
|
1189
|
+
const finalizeCompletedExit = () => {
|
|
1190
|
+
ctx.stage = 'completed';
|
|
1191
|
+
ensureReviewReportSent();
|
|
1192
|
+
this.reportCompletionWithCommitSha(taskId, 'Task completed successfully', ctx.workspaceInfo?.path, settle, resolve);
|
|
1193
|
+
};
|
|
1194
|
+
if (taskMode === 'review' && !reviewReportSent) {
|
|
1195
|
+
void waitForQueuedPtyOutput().then(() => {
|
|
1196
|
+
if (!settled) {
|
|
1197
|
+
finalizeCompletedExit();
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
finalizeCompletedExit();
|
|
1237
1203
|
}
|
|
1238
1204
|
else {
|
|
1239
1205
|
settle(() => reject(new Error(`Agent exited with code ${exitCode}, pipeline incomplete`)));
|