fix(coding-agent): suppress bug hints for expected failures

This commit is contained in:
Armin Ronacher
2026-09-20 12:06:14 +02:00
parent 60740991c0
commit d875512cc4
3 changed files with 70 additions and 3 deletions
+1
View File
@@ -5,6 +5,7 @@
### Fixed
- Fixed `/bug` descriptions dropping line breaks from pasted diagnostics.
- Fixed `/bug` hints appearing for user cancellations and retryable provider failures such as service unavailability.
## [0.86.0] - 2026-09-19
@@ -9,7 +9,14 @@ import * as os from "node:os";
import * as path from "node:path";
import type { AgentMessage, ThinkingLevel } from "@earendil-works/pi-agent-core";
import type { AuthEvent, AuthPrompt } from "@earendil-works/pi-ai";
import type { AssistantMessage, ImageContent, Message, Model, Usage } from "@earendil-works/pi-ai/compat";
import {
type AssistantMessage,
type ImageContent,
isRetryableAssistantError,
type Message,
type Model,
type Usage,
} from "@earendil-works/pi-ai/compat";
import type {
AutocompleteItem,
AutocompleteProvider,
@@ -2064,6 +2071,12 @@ export class InteractiveMode {
this.ui.requestRender();
}
private maybeSuggestBugReport(message: AssistantMessage): void {
if (message.stopReason !== "error" || isRetryableAssistantError(message)) return;
if (/\b(?:abort(?:ed)?|cancel(?:l?ed)?)\b/i.test(message.errorMessage ?? "")) return;
this.suggestBugReport();
}
private renderCurrentSessionState(): void {
this.loadedResourcesContainer.clear();
this.chatContainer.clear();
@@ -3376,7 +3389,7 @@ export class InteractiveMode {
});
}
this.pendingTools.clear();
if (this.streamingMessage.stopReason === "error") this.suggestBugReport();
this.maybeSuggestBugReport(this.streamingMessage);
} else {
// Args are now complete - trigger diff computation for edit tools
for (const [, component] of this.pendingTools.entries()) {
@@ -3546,7 +3559,6 @@ export class InteractiveMode {
// Show error only on final failure (success shows normal response)
if (!event.success) {
this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
this.suggestBugReport();
}
this.ui.requestRender();
break;
@@ -0,0 +1,54 @@
import { type AssistantMessage, fauxAssistantMessage } from "@earendil-works/pi-ai";
import { describe, expect, test, vi } from "vitest";
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
type BugReportHintContext = {
suggestBugReport(): void;
};
const maybeSuggestBugReport = Reflect.get(InteractiveMode.prototype, "maybeSuggestBugReport") as (
this: BugReportHintContext,
message: AssistantMessage,
) => void;
function errorMessage(errorMessage: string): AssistantMessage {
return fauxAssistantMessage("", { stopReason: "error", errorMessage });
}
describe("InteractiveMode bug report hints", () => {
test("does not suggest reports for retryable provider failures", () => {
const context = { suggestBugReport: vi.fn() };
const failures = [
"500 Internal Server Error",
"502 Bad Gateway",
"503 Service Unavailable",
"504 Gateway Timeout",
"429 Too Many Requests",
"Provider overloaded",
"Network connection lost",
"Request timed out",
];
for (const failure of failures) maybeSuggestBugReport.call(context, errorMessage(failure));
expect(context.suggestBugReport).not.toHaveBeenCalled();
});
test("does not suggest reports for cancellations", () => {
const context = { suggestBugReport: vi.fn() };
maybeSuggestBugReport.call(context, errorMessage("This operation was aborted"));
maybeSuggestBugReport.call(context, errorMessage("Request cancelled"));
maybeSuggestBugReport.call(context, fauxAssistantMessage("", { stopReason: "aborted" }));
expect(context.suggestBugReport).not.toHaveBeenCalled();
});
test("suggests reports for unexpected errors", () => {
const context = { suggestBugReport: vi.fn() };
maybeSuggestBugReport.call(context, errorMessage("Unexpected internal state"));
expect(context.suggestBugReport).toHaveBeenCalledOnce();
});
});