diff --git a/scripts/claude-bridge.js b/scripts/claude-bridge.js index 32dfee2..a788444 100644 --- a/scripts/claude-bridge.js +++ b/scripts/claude-bridge.js @@ -1089,16 +1089,39 @@ process.on('SIGINT', () => { clearInterval(keepAlive); cleanupDaemon(); process. process.on('exit', () => { cleanupDaemon(); }); // Globale Fehler-Handler — Bridge darf nicht still abstürzen +// WICHTIG: err.stack ist ein lazy Getter der bei jedem Zugriff neu formatiert wird +// und bei V8-OOM selbst einen OOM-Abort auslösen kann. Daher: einmal lesen, kürzen, +// try/catch drumrum — der Handler darf nicht selbst crashen. +function safeStack(err) { + try { + const raw = (err && err.stack) ? String(err.stack) : ''; + return raw.length > 2000 ? raw.slice(0, 2000) + '\n...[gekürzt]' : raw; + } catch { + return '[stack nicht lesbar]'; + } +} process.on('uncaughtException', (err) => { - process.stderr.write(`❌ Unbehandelter Fehler: ${err.message}\n${err.stack}\n`); - sendEvent('bridge-error', { type: 'uncaughtException', message: err.message, stack: err.stack }); - sendMonitorEvent('error', `Bridge Crash: ${err.message}`, { stack: err.stack }); + try { + const msg = (err && err.message) ? String(err.message).slice(0, 500) : String(err).slice(0, 500); + const stack = safeStack(err); + process.stderr.write(`❌ Unbehandelter Fehler: ${msg}\n${stack}\n`); + sendEvent('bridge-error', { type: 'uncaughtException', message: msg, stack }); + sendMonitorEvent('error', `Bridge Crash: ${msg}`, {}); + } catch (inner) { + try { process.stderr.write(`❌ Handler-Fehler: ${inner && inner.message}\n`); } catch {} + } }); process.on('unhandledRejection', (reason) => { - const msg = reason instanceof Error ? reason.message : String(reason); - process.stderr.write(`❌ Unhandled Promise Rejection: ${msg}\n`); - sendEvent('bridge-error', { type: 'unhandledRejection', message: msg }); - sendMonitorEvent('error', `Unhandled Rejection: ${msg}`, {}); + try { + const msg = reason instanceof Error + ? String(reason.message).slice(0, 500) + : String(reason).slice(0, 500); + process.stderr.write(`❌ Unhandled Promise Rejection: ${msg}\n`); + sendEvent('bridge-error', { type: 'unhandledRejection', message: msg }); + sendMonitorEvent('error', `Unhandled Rejection: ${msg}`, {}); + } catch (inner) { + try { process.stderr.write(`❌ Handler-Fehler: ${inner && inner.message}\n`); } catch {} + } }); // Bereit-Signal (im stdio-Modus sofort senden, im Daemon-Modus pro Client bei Connect) diff --git a/src-tauri/src/claude.rs b/src-tauri/src/claude.rs index 82ec8ba..48351c2 100644 --- a/src-tauri/src/claude.rs +++ b/src-tauri/src/claude.rs @@ -173,7 +173,10 @@ fn start_daemon(script_path: &std::path::Path) -> Result { println!("🔌 Starte Bridge-Daemon: {:?} --socket {}", script_path, SOCKET_PATH); + // --max-old-space-size=4096: Node-Default (~2GB) reicht nicht bei langen Sessions + // mit großen Thinking-Blocks/Agent-SDK-History (KB #crash-oom-stacktrace). let child = Command::new("node") + .arg("--max-old-space-size=4096") .arg(script_path) .arg("--socket") .arg(SOCKET_PATH) @@ -328,6 +331,7 @@ fn start_bridge_stdio(app: &AppHandle, script_path: &std::path::Path) -> Result< println!("📂 Bridge Arbeitsverzeichnis: {:?}", project_dir); let mut child = Command::new("node") + .arg("--max-old-space-size=4096") .arg(script_path) .current_dir(project_dir) .stdin(Stdio::piped())