mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-09-28 05:54:50 +08:00
The SDK now emits ConversationErrorEvents with a meaningful `code` (ACPAuthRequired / ACPSpawnError / ACPInitError / ACPPromptError / UsagePolicyRefusal) and a rich `detail`, but the canvas banner discarded the code (analytics only) and showed `detail` alone. So a credential failure looked the same as any other error, with no recovery path. Thread `code` through the error-message store to the banner: - Show a localized header for known ACP codes (auth failures get "Authentication required"; the rest reuse the generic "Agent error" title). The rich detail still renders verbatim below it. - For credential failures (ACPAuthRequired), show an "Update credentials" action that deep-links to Settings → Agent (/settings/agent). Pairs with software-agent-sdk #3756 (which produces the code + detail). Adds a small `acp-error-codes` mapping util, two i18n keys (all 15 locales), and tests for the util, store, and banner. Co-authored-by: Debug Agent <simon@openhands.dev> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { I18nKey } from "#/i18n/declaration";
|
|
import {
|
|
getAcpErrorHeaderKey,
|
|
isAcpAuthErrorCode,
|
|
} from "#/utils/acp-error-codes";
|
|
|
|
describe("acp-error-codes", () => {
|
|
it("maps the auth code to the auth header key", () => {
|
|
expect(getAcpErrorHeaderKey("ACPAuthRequired")).toBe(
|
|
I18nKey.ERROR$ACP_AUTH_REQUIRED_TITLE,
|
|
);
|
|
});
|
|
|
|
it("maps other ACP codes to the generic agent-error header", () => {
|
|
for (const code of [
|
|
"ACPSpawnError",
|
|
"ACPInitError",
|
|
"ACPPromptError",
|
|
"UsagePolicyRefusal",
|
|
]) {
|
|
expect(getAcpErrorHeaderKey(code)).toBe(
|
|
I18nKey.CHAT_INTERFACE$AGENT_ERROR_MESSAGE,
|
|
);
|
|
}
|
|
});
|
|
|
|
it("returns null for unknown, empty, or missing codes", () => {
|
|
expect(getAcpErrorHeaderKey(null)).toBeNull();
|
|
expect(getAcpErrorHeaderKey(undefined)).toBeNull();
|
|
expect(getAcpErrorHeaderKey("")).toBeNull();
|
|
expect(getAcpErrorHeaderKey("RequestError")).toBeNull();
|
|
});
|
|
|
|
it("flags only the auth code for re-authentication", () => {
|
|
expect(isAcpAuthErrorCode("ACPAuthRequired")).toBe(true);
|
|
expect(isAcpAuthErrorCode("ACPPromptError")).toBe(false);
|
|
expect(isAcpAuthErrorCode(null)).toBe(false);
|
|
expect(isAcpAuthErrorCode(undefined)).toBe(false);
|
|
});
|
|
});
|