You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(github-code-review): resume opencode session on leaked tool-call instead of restarting
The MiniMax/SGLang tool-parse leak happens at the END of a run — after the review
is done — on a bookkeeping tool call (e.g. todowrite). Restarting from scratch
threw the finished review away and, since the agent already edited files on disk,
the fresh pass could no longer re-find the now-fixed bug (dropping it from the
report). On a detected leak the worker now RESUMES the same opencode session
(`opencode run -s <id>` / `-c`) with a nudge to call no more tools and just emit
the final report, salvaging the work and avoiding a re-trigger.
- extractOpencodeSessionId(): scrape the session id from the --format json stream
- getResumeNudge(): "no tools; output only the final report in the required
format; report already-applied fixes as FIXED" (OPENCODE_RESUME_NUDGE override)
- buildOpencodeResumeCommand()/selectOpencodeAttemptCommand(): -s <id> else -c;
attempt 1 = full prompt, later attempts = resume
- runOpencodeAnalysis(): leak -> resume; empty resume -> one fresh full-prompt
fallback (bounded by OPENCODE_MAX_ATTEMPTS, no infinite loop); temp files
cleaned in finally. Parsing/posting/baselines unchanged.
- New OpencodeResumeTest (11 tests, network-free).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@@ -2662,16 +2774,46 @@ function runOpencodeAnalysis(string $dir, string $jobId, string $cmdTemplate, bo
2662
2774
if ($run['stderr'] !== '') {
2663
2775
verbose_raw("opencode_stderr", $run['stderr']);
2664
2776
}
2777
+
2778
+
// A resume that produced nothing means the session couldn't be
2779
+
// continued (not found / resume failed). Rather than overwrite the
2780
+
// salvageable partial with '' or loop resuming an unresumable
2781
+
// session forever, fall back to ONE fresh full-prompt run.
2782
+
if ($mode === 'resume' && trim($run['output']) === '') {
2783
+
if ($attempt < $maxAttempts) {
2784
+
verbose_log("runOpencodeAnalysis: resume produced no output (session not found?); falling back to a fresh full-prompt run (" . ($attempt + 1) . "/{$maxAttempts})", 1);
2785
+
$forceFresh = true;
2786
+
usleep(500000); // brief backoff before retry
2787
+
if (function_exists('pcntl_signal_dispatch')) {
2788
+
pcntl_signal_dispatch();
2789
+
}
2790
+
// @phpstan-ignore-next-line — $running is mutated by the async signal handler
2791
+
if (!$running) {
2792
+
return'';
2793
+
}
2794
+
continue;
2795
+
}
2796
+
verbose_log("runOpencodeAnalysis: resume produced no output on the final attempt; using best-effort partial", 1);
2797
+
break;
2798
+
}
2799
+
2665
2800
$result = $run['output'];
2666
2801
2802
+
// Capture the session id from the first attempt that exposes one so
2803
+
// a later leak resumes the SAME session (falls back to -c otherwise).
2804
+
if ($sessionId === '') {
2805
+
$sessionId = extractOpencodeSessionId($result);
2806
+
}
2807
+
2667
2808
if ($run['timedOut']) {
2668
2809
// A timeout is not a parse leak — keep whatever partial we have
2669
2810
verbose_log("runOpencodeAnalysis: analysis timed out after {$timeout}s, using partial output", 1);
2670
2811
break;
2671
2812
}
2672
2813
2673
2814
if (looksLikeLeakedToolCall($result) && $attempt < $maxAttempts) {
0 commit comments