diff --git a/proxy-patches/manager.js b/proxy-patches/manager.js index 2ad1181..0decebb 100644 --- a/proxy-patches/manager.js +++ b/proxy-patches/manager.js @@ -18,6 +18,9 @@ */ import { spawn } from "child_process"; import { EventEmitter } from "events"; +import fs from "fs"; +import os from "os"; +import path from "path"; import { isAssistantMessage, isResultMessage, isContentDelta } from "../types/claude-cli.js"; const DEFAULT_TIMEOUT = 86400000; // 24h — lange Agent-Loops (Pentests etc.) export class ClaudeSubprocess extends EventEmitter { @@ -26,6 +29,8 @@ export class ClaudeSubprocess extends EventEmitter { timeoutId = null; isKilled = false; _stdinClosed = false; + _systemPromptFile = null; + _systemPromptDir = null; /** * Start the Claude CLI subprocess with the given prompt */ @@ -51,6 +56,7 @@ export class ClaudeSubprocess extends EventEmitter { // Handle spawn errors (e.g., claude not found) this.process.on("error", (err) => { this.clearTimeout(); + this._cleanupSystemPrompt(); if (err.message.includes("ENOENT")) { reject(new Error("Claude CLI not found. Install with: npm install -g @anthropic-ai/claude-code")); } @@ -85,6 +91,7 @@ export class ClaudeSubprocess extends EventEmitter { this.process.on("close", (code) => { console.error(`[Subprocess] Process closed with code: ${code}`); this.clearTimeout(); + this._cleanupSystemPrompt(); // Process any remaining buffer if (this.buffer.trim()) { this.processBuffer(); @@ -96,6 +103,7 @@ export class ClaudeSubprocess extends EventEmitter { } catch (err) { this.clearTimeout(); + this._cleanupSystemPrompt(); reject(err); } }); @@ -114,13 +122,47 @@ export class ClaudeSubprocess extends EventEmitter { "stream-json", // ARIA: User-Messages via stdin (Zwischenruf) "--model", options.model, // Model alias (opus/sonnet/haiku) - "--no-session-persistence", "--dangerously-skip-permissions", "--system-prompt", options.systemPrompt, "--safe-mode", + "--no-session-persistence", "--dangerously-skip-permissions", "--safe-mode", ]; + // ARIA: System-Prompt ueber eine DATEI statt als Kommandozeilen-Argument. + // Grund: execve() begrenzt argv+envp zusammen auf ARG_MAX — im Container + // nur 128 KiB. Ein gewachsener System-Prompt (gepinnte Erinnerungen, + // Skills, Projektkontext; real gesehen ~77k Zeichen) sprengte das und der + // Start brach mit "spawn E2BIG" ab: der Turn ging verloren, die App bekam + // 502 und die Nachricht blieb unbeantwortet. Ueber die Datei gibt es + // praktisch keine Groessengrenze mehr. + this._systemPromptFile = null; + const sysPrompt = options.systemPrompt || ""; + if (sysPrompt) { + try { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), "aria-sysprompt-")); + const file = path.join(dir, "system-prompt.txt"); + fs.writeFileSync(file, sysPrompt, "utf-8"); + this._systemPromptFile = file; + this._systemPromptDir = dir; + args.push("--system-prompt-file", file); + } + catch (err) { + // Notfalls wie bisher (kleine Prompts funktionieren so weiterhin). + console.error(`[Subprocess] System-Prompt-Datei fehlgeschlagen (${err.message}) — nutze Argument`); + args.push("--system-prompt", sysPrompt); + } + } if (options.sessionId) { args.push("--session-id", options.sessionId); } return args; } + /** Temporaere System-Prompt-Datei wieder entfernen. */ + _cleanupSystemPrompt() { + try { + if (this._systemPromptFile) + fs.rmSync(this._systemPromptDir || this._systemPromptFile, { recursive: true, force: true }); + } + catch (_) { } + this._systemPromptFile = null; + this._systemPromptDir = null; + } /** * Eine User-Message im stream-json-Input-Format an stdin schreiben. * Genutzt fuer den initialen Prompt UND fuer Zwischenrufe (sendMessage).