@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.
@@ -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":"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"}
1
+ {"version":3,"file":"task-handler.d.ts","sourceRoot":"","sources":["../src/task-handler.ts"],"names":[],"mappings":"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"}
@@ -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
- const REVIEW_REPORT_PARSE_FAILURE_SUMMARY = 'Structured review report could not be parsed. Findings may be incomplete.';
63
- const REVIEW_REPORT_PARSE_FAILURE_ISSUE = {
64
- severity: 'warning',
65
- file: 'report_output',
66
- description: 'Review agent did not emit a valid structured report for the report stage.',
67
- suggestion: 'Emit a report RESULT marker or a single valid JSON object between report START and DONE.',
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
- // 5. Wait for agent to be ready (detect prompt character)
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
- return autoConfirmStartupPrompt(taskId, ptySession, ctx.handledStartupPromptSignatures, 'Auto-accepting workspace trust prompt (checkReady)');
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 startupPromptResult = handleStartupPrompt();
943
- if (startupPromptResult === 'handled') {
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 cleared duplicate startup-dialog remnants from
952
- // the shared buffer, the current PTY chunk may still contain the real
953
- // prompt. Check the latest chunk tail as a fallback.
954
- if (startupPromptResult === 'duplicate' || detectPromptFromLatestChunk(_data)) {
955
- if (detectPromptFromLatestChunk(_data)) {
956
- resolvePromptDetected();
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' && this.onReviewReport) {
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
- this.onReviewReport(taskId, report);
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
- if (/rate|limit|quota|429|auth|login|exceeded|exhausted|hit your/i.test(stripped)) {
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
- ctx.stage = 'completed';
1236
- this.reportCompletionWithCommitSha(taskId, 'Task completed successfully', ctx.workspaceInfo?.path, settle, resolve);
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`)));