diff --git a/packages/local-config-updater/src/config-agent.ts b/packages/local-config-updater/src/config-agent.ts index f8a1bc20d..0f69cd0ce 100644 --- a/packages/local-config-updater/src/config-agent.ts +++ b/packages/local-config-updater/src/config-agent.ts @@ -35,6 +35,20 @@ export type RunClaudeAgentResult = { resultText: string, }; +export class ClaudeAgentTimeoutError extends Error { + constructor(timeoutMs?: number) { + super(`Claude agent timed out${timeoutMs == null ? "" : ` after ${timeoutMs}ms`}.`); + this.name = "ClaudeAgentTimeoutError"; + } +} + +export class ClaudeAgentFailureError extends Error { + constructor(subtype: string) { + super(`Claude agent failed (${subtype}).`); + this.name = "ClaudeAgentFailureError"; + } +} + function isAbortError(error: unknown): boolean { return error instanceof Error && error.name === "AbortError"; } @@ -91,13 +105,13 @@ export async function runHeadlessClaudeAgent(options: RunClaudeAgentOptions): Pr sawResult = true; resultText = message.result; } else { - throw new Error(`Claude agent failed (${message.subtype}).`); + throw new ClaudeAgentFailureError(message.subtype); } } } } catch (error) { if (abortController.signal.aborted && isAbortError(error)) { - throw new Error(`Claude agent timed out${options.timeoutMs == null ? "" : ` after ${options.timeoutMs}ms`}.`); + throw new ClaudeAgentTimeoutError(options.timeoutMs ?? undefined); } throw error; } finally { diff --git a/packages/local-config-updater/src/index.ts b/packages/local-config-updater/src/index.ts index 51ee9ed32..9bce863ad 100644 --- a/packages/local-config-updater/src/index.ts +++ b/packages/local-config-updater/src/index.ts @@ -7,7 +7,7 @@ import { createHash } from "crypto"; import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs"; import { createJiti } from "jiti"; import path from "path"; -import { getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from "./config-agent"; +import { ClaudeAgentFailureError, ClaudeAgentTimeoutError, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from "./config-agent"; const jiti = createJiti(import.meta.url, { moduleCache: false }); @@ -190,10 +190,10 @@ async function runConfigUpdateAgent(options: { }, }); } catch (error) { - if (error instanceof Error && error.message.startsWith("Claude agent timed out")) { + if (error instanceof ClaudeAgentTimeoutError) { throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`); } - if (error instanceof Error && error.message.startsWith("Claude agent failed")) { + if (error instanceof ClaudeAgentFailureError) { throw new Error(`${error.message} It was unable to apply the config changes to the file.`); } throw error;