diff --git a/package-lock.json b/package-lock.json index 67fcb7e..156f28c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,22 +67,22 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.197.tgz", - "integrity": "sha512-XNIi8W1tb+QfMkcK+5kepOC6BsxG8wtupd72H+pIPzIJypVQhHy7FoX+KBMtTRYwtl+5dsjKyABhjWXebeUilw==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.201.tgz", + "integrity": "sha512-InT1XLmf2QpldWdtznKDWEoGJT4p+sXh24yxbeBQ++lMJCzMrI0W27MEmmmDWx0otpa+ubdHCF5YQ6oiNt7cmg==", "license": "SEE LICENSE IN README.md", "engines": { "node": ">=18.0.0" }, "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.197", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.197", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.197", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.197", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.197", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.197", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.197", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.197" + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.201", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.201", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.201", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.201", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.201", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.201", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.201", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.201" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", @@ -91,9 +91,9 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.197.tgz", - "integrity": "sha512-jC6WvH5Hr6APTfbMjo4nC6LlyMMqbpCMwiHXIw7/AsQXIHQhZ+cRRMesQlV6UFI1l3O53gLZHzsG9cXwfrPHKw==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.201.tgz", + "integrity": "sha512-8Mcb3BDyKUGfJWFFTWwt+at37lbDH3ZwVtUNPWGG1toZ75RDCJry5U4kXRvQ2xokvJQlA0E+eNp6keWe5ZH22Q==", "cpu": [ "arm64" ], @@ -104,9 +104,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.197.tgz", - "integrity": "sha512-ZQNvGkMrTyatBlHTIQ4w2i2aLBuvq355UP/FDLnVXIH8l23RsL1x/0w9P+dqB7EmY9OZi/cPxSrpskpo+dZWLA==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.201.tgz", + "integrity": "sha512-TFR2bu0+ml3RHoMrtsgD0qDK5Oknw8kYGBV7qpQHn+IWmE96gnHhogG1LpJwpHtni08XkJIjfWk1DdlsUYtRkQ==", "cpu": [ "x64" ], @@ -117,9 +117,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.197.tgz", - "integrity": "sha512-pWhQgCtAft4EGM4Zn24HRad1a/k2u6oA+2uM/KCdjehfKtooDiHfMNd1yzXY/n9AEBWP0RHB2Vz3mJ30X2pVAg==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.201.tgz", + "integrity": "sha512-mShTo3MwF0gkN4dDw78wWJiB6aBDVRkl81cnApvoBofpdyUBYgm9Gw16CCjDTgelMKeBFqN6ErJpwjI3wbP00A==", "cpu": [ "arm64" ], @@ -130,9 +130,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.197.tgz", - "integrity": "sha512-VuIGXsLGK/aqSQ0tTBqqPVNzjefWS5SWnK8mlYyQitT4s5UDzHXJm0UZBTGxRtlcS0e2+QAHKwbGBCq1ZKSXjg==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.201.tgz", + "integrity": "sha512-EiqbpfJIpChfkn+8Uj061Qjyw0eaRcOXtdrvVuHANyj8ZErVOr8HlH6op9PSeIUa9TX0m2+tNgKPQvOGseQckA==", "cpu": [ "arm64" ], @@ -143,9 +143,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.197.tgz", - "integrity": "sha512-AUccrbdcv4Hy/GteP/gYLjG/zDP+fe2BFtDMctEfRFVz40DazYDcOyW1+nIgSTQtxf5jSTAVVf3cNuXB2CZwlw==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.201.tgz", + "integrity": "sha512-jrJBrRWrSuoFKIgjyqxHqmfd6Pb3Bs5Bvakg0knXCTC4fbUXGnC9Q6u7gdDwgXohUNP6/DD+s8U7bivvvVv0dg==", "cpu": [ "x64" ], @@ -156,9 +156,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.197.tgz", - "integrity": "sha512-3Tuy7XhD4UIKE4A4RPmKJcbL7Q/3dcB1hEWQt2lKP7c/DlixeEv+tRzvpnFZKhFX2hy0tkBk3QjkozSAacMC/w==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.201.tgz", + "integrity": "sha512-IbxnzO5UCbqbm2TnzCHkSyJorAFw2isdKdIsFCTxJJjSs3ZC+v3LC1QSUiVCx0qi+CV6w3MKx6mLI11mrvhbbQ==", "cpu": [ "x64" ], @@ -169,9 +169,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.197.tgz", - "integrity": "sha512-Wx8uiAKBenDuL8lWQmrqnX5ppljaH5unQ9cKiCz2/9Kgf09dgnrwbX8n/FhndCZR8PmYw539eWwYVrSVc/bl6w==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.201.tgz", + "integrity": "sha512-UsoytRJ/037uHpb3ATrIoe+AgwTf+PwKuFLGjddHAV/11wERJs0hlrnSmcnp43kf0PFxoSNinngme96YYASmQg==", "cpu": [ "arm64" ], @@ -182,9 +182,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.3.197", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.197.tgz", - "integrity": "sha512-ZXJO/VvR3SI4G0gwthWeFXWdHB5RXPu3rtfGRcKZ/YgtDeW17rQ+LZIJTk2ywzbLb8EvlghR5JPgn293hC179Q==", + "version": "0.3.201", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.201.tgz", + "integrity": "sha512-PhalN/0cWcqDfbx7iwoLNR2gurjTiqhBk1G6K+NRScxEcQjWuu5xKXCcdbX8ePVpT+nbEMmFEFpn2y+8V8hIdA==", "cpu": [ "x64" ], diff --git a/src/main/ipc/attachmentHandlers.ts b/src/main/ipc/attachmentHandlers.ts index 8c2809e..eda1d93 100644 --- a/src/main/ipc/attachmentHandlers.ts +++ b/src/main/ipc/attachmentHandlers.ts @@ -53,6 +53,9 @@ export function registerAttachmentHandlers(attachments: AttachmentManager): void if (typeof p !== 'string' || p.length === 0 || p.length > ATTACHMENT_LIMITS.pathMax) { throw new Error('Invalid path'); } + if (p.includes('\0')) { + throw new Error('Invalid path'); + } } return attachments.addFromPaths(id, paths, 'drop'); }, diff --git a/src/main/managers/AgentManager.ts b/src/main/managers/AgentManager.ts index 7eddad2..7d6be32 100644 --- a/src/main/managers/AgentManager.ts +++ b/src/main/managers/AgentManager.ts @@ -1221,6 +1221,7 @@ export class AgentManager { permMode: SessionPermissionMode, ): Promise { let attempt = 0; + let resumeDropped = false; for (;;) { try { await this.runOnce(sessionId, prompt, abort, permMode); @@ -1250,6 +1251,28 @@ export class AgentManager { return; } const raw = err instanceof Error ? err.message : String(err); + + // Corrupted-resume self-heal: the CLI's `[ede_diagnostic] … stop_reason= + // tool_use` result means the resumed transcript ends in a tool_use with + // no tool_result (a prior run died mid-tool). Resuming it fails the same + // way every turn, so drop the stored SDK session id once and retry with + // a fresh SDK session (Limboo's own transcript/history is unaffected). + const resumeCorrupted = + raw.includes('ede_diagnostic') || + (raw.includes('returned an error result') && raw.includes('stop_reason=tool_use')); + if (resumeCorrupted && !resumeDropped && this.loadSdkSession(sessionId)) { + resumeDropped = true; + this.forgetSdkSession(sessionId); + this.diag( + 'recovery', + 'warning', + 'Resumed transcript was corrupted — starting a fresh SDK session', + redact(raw), + sessionId, + ); + continue; // retry — buildOptions no longer finds a resume id + } + const cls = classifyAgentError(redact(raw)); this.diag('recovery', cls.recoverable ? 'warning' : 'error', `Run error (${cls.outcome})`, redact(raw), sessionId); @@ -2323,6 +2346,16 @@ export class AgentManager { .run(sessionId, sdkSessionId, Date.now()); } + /** + * Drop the stored SDK session id so the next run starts a fresh SDK session + * instead of resuming. Used when the resumed transcript is corrupted (e.g. it + * ends in a tool_use with no tool_result after a mid-tool kill) — resuming it + * would fail identically on every turn. + */ + private forgetSdkSession(sessionId: string): void { + getDb().prepare('DELETE FROM agent_session_meta WHERE session_id = ?').run(sessionId); + } + /* ---------------------------------------------------------------- */ /* State + broadcast */ /* ---------------------------------------------------------------- */ diff --git a/src/main/managers/voice/VoiceManager.ts b/src/main/managers/voice/VoiceManager.ts index 0ad0eca..c848f14 100644 --- a/src/main/managers/voice/VoiceManager.ts +++ b/src/main/managers/voice/VoiceManager.ts @@ -51,6 +51,8 @@ export class VoiceManager { private worker: UtilityProcess | null = null; private workerReady = false; private workerRestarts = 0; + /** Set when WE asked the worker to shut down, so its exit is not a crash. */ + private expectedExit = false; private readonly loadedKinds = new Set(); private readonly loading = new Map>(); /** Signature of the VAD tuning the worker was loaded with. */ @@ -423,12 +425,16 @@ export class VoiceManager { }); this.worker = proc; this.workerReady = false; + this.expectedExit = false; this.loadedKinds.clear(); this.loading.clear(); proc.on('message', (msg: VoiceWorkerResponse) => this.onWorkerMessage(msg)); proc.on('exit', (code) => { - logger.warn(`voice: worker exited with code ${code}`); + const expected = this.expectedExit && code === 0; + this.expectedExit = false; + if (expected) logger.info('voice: worker exited cleanly'); + else logger.warn(`voice: worker exited with code ${code}`); const wasReady = this.workerReady; this.worker = null; this.workerReady = false; @@ -638,6 +644,7 @@ export class VoiceManager { this.captureSessionId !== null || this.currentJob !== null || this.ttsQueue.length > 0; if (!busy && this.worker) { logger.info('voice: shutting down idle speech worker'); + this.expectedExit = true; this.post({ t: 'shutdown' }); } }, WORKER_IDLE_MS); @@ -666,6 +673,7 @@ export class VoiceManager { this.unsubscribeAgent?.(); this.ttsQueue.length = 0; if (this.worker) { + this.expectedExit = true; this.post({ t: 'shutdown' }); this.worker = null; }