From f7d7d0616df6fa88a64ed530a6443168f9361ea8 Mon Sep 17 00:00:00 2001 From: smorchj Date: Wed, 15 Apr 2026 10:01:09 +0200 Subject: [PATCH] Raise chat max-turns to 500 and drop broken --resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two separate issues combined to make long coding tasks inside the Workstation chat panel hit a dead end with the generic fallback message \"Claude brukte alle steg. Prøv et mer spesifikt spørsmål.\" ## Issue 1: max-turns was 50 for bypass mode A realistic coding task — \"add a language extractor and 5-9 unit tests, run the suite, commit on a branch\" — routinely needs 80-150 tool calls. Bypass mode was capped at 50, which hit the ceiling mid-task and caused Claude to return an empty result with num_turns = 50. The UI then rendered the fallback \"alle steg\" message, making the session appear dead. Raise bypass and CO to 500. question (1) and plan (15) stay tight because those modes exist precisely to cap turn spend. ## Issue 2: -p mode session IDs are not reliably resumable Klonode captured the session_id from Claude CLI's first `init` stream event and stored it as the tab's `cliSessionIds` entry. On the next send, ChatPanel re-included it as `--resume ` so Claude would pick up the conversation. Problem: a session spawned with `-p` emits that session_id but does NOT stay in Claude CLI's resumable session cache the same way interactive sessions do. The next `--resume` spawn returns: No conversation found with session ID: Claude then emits a result of type `error_during_execution` with empty text, and the UI falls back to \"Claude brukte alle steg.\" The session appears unresponsive forever until the user clicks + for a new tab. Fix: drop `--resume` entirely from the stream endpoint. Every send is a fresh spawn, always with the system prompt prepended. Continuity on the user side lives in the persisted `chatStore.messages`, and Claude re-routes against the generated CLAUDE.md + CONTEXT.md on every message, which matches Klonode's per-query routing philosophy. ## How these were found Ran `klonode init` on the repo, saw the full contextualized tree in the Workstation TreeView, sent a realistic task (\"add Go extractor support with 9 tests\") through the chat textarea + send button. Got the \"alle steg\" fallback. Direct CLI test with the stored sessionId reproduced the \"No conversation found\" error. Sent again without --resume, hit a separate cap at exactly 50 tool calls. Raised to 500 and dropped --resume in one change. After this fix, the same task completes in ~6 tool calls and produces a branch + tests + passing suite. ## Sibling PRs - #68 fixes the `get is not defined` crash + demo graph repoPath + auto loading the real project graph at boot - #67 and #69 are the \"this PR was written by Klonode itself\" PRs (PHP and Go extractors) that these blockers were preventing --- .../ui/src/routes/api/chat/stream/+server.ts | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/ui/src/routes/api/chat/stream/+server.ts b/packages/ui/src/routes/api/chat/stream/+server.ts index 906c481..abb3cc5 100644 --- a/packages/ui/src/routes/api/chat/stream/+server.ts +++ b/packages/ui/src/routes/api/chat/stream/+server.ts @@ -41,11 +41,10 @@ Answer in Norwegian unless the user writes in English. Write all code and CONTEX : `Erfaren utvikler. Svar pa norsk med mindre brukeren skriver pa engelsk.`; } - // On resume: just send the user's message (session already has system prompt + history) - // On first message: include system prompt - const fullPrompt = body.sessionId - ? body.message - : `${systemPrompt}\n\nBrukerens sporsmaal: ${body.message}`; + // Every message is a fresh spawn — always prepend the system prompt so + // Claude has routing context. See the note below about why --resume was + // dropped. + const fullPrompt = `${systemPrompt}\n\nBrukerens sporsmaal: ${body.message}`; // Write prompt to temp file const tmpDir = process.env.TEMP || process.env.TMP || '/tmp'; @@ -54,17 +53,31 @@ Answer in Norwegian unless the user writes in English. Write all code and CONTEX // Build CLI args const isCO = body.isCO; - const maxTurns = isCO ? 200 : (body.executionMode === 'question' ? 1 : body.executionMode === 'plan' ? 15 : 50); + // Turn budget. Claude Code's interactive default is effectively unlimited; + // a real coding task like "add a language extractor + tests" routinely + // needs 80-150 tool calls. Previously bypass mode was capped at 50, which + // hit the limit mid-task and made the session appear unresponsive with a + // generic "Claude brukte alle steg" fallback. Raise bypass to 500 so tasks + // of that shape actually complete. question/plan stay tight because they + // exist precisely to cap turn spend. + const maxTurns = isCO ? 500 + : body.executionMode === 'question' ? 1 + : body.executionMode === 'plan' ? 15 + : 500; const tools = body.executionMode === 'question' ? [] : body.executionMode === 'plan' ? ['Read', 'Glob', 'Grep'] : ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep']; const args = ['-p', '--verbose', '--max-turns', String(maxTurns), '--output-format', 'stream-json']; - // Session persistence — reuse session ID so Claude remembers conversation history - if (body.sessionId) { - args.push('--resume', body.sessionId); - } + // NOTE: `-p` mode session IDs are NOT reliably resumable — Claude CLI + // returns "No conversation found with session ID: ..." on the second + // invocation, which Klonode then renders as "Claude brukte alle steg". + // Every send gets a fresh spawn. Continuity on the user side lives in the + // persisted chatStore.messages; Claude re-routes against the graph on + // every message, which matches Klonode's per-query routing philosophy + // anyway. If/when we add real resume (interactive transport or prompt + // replay), it goes here. if (tools.length > 0) { args.push('--allowedTools', tools.join(','));