From 2c92e27b4d76fbaf8d256af161f13789e1e18ed2 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Wed, 17 Dec 2025 12:49:29 -0800 Subject: [PATCH] Fix log crash --- agents/gemini.ts | 12 +++++++++--- agents/opencode.ts | 26 +++++++++++++++++++++----- entry | 21 ++++++++++++++++----- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/agents/gemini.ts b/agents/gemini.ts index 89b9ee3..f73c2ba 100644 --- a/agents/gemini.ts +++ b/agents/gemini.ts @@ -184,6 +184,7 @@ export const gemini = agent({ } let finalOutput = ""; + let stdoutBuffer = ""; // buffer for incomplete lines across chunks try { const result = await spawn({ cmd: "node", @@ -195,8 +196,13 @@ export const gemini = agent({ const text = chunk.toString(); finalOutput += text; - // parse each line as JSON (gemini cli outputs one JSON object per line) - const lines = text.split("\n"); + // buffer incomplete lines across chunks (NDJSON format) + stdoutBuffer += text; + const lines = stdoutBuffer.split("\n"); + + // keep the last element (may be incomplete) in the buffer + stdoutBuffer = lines.pop() || ""; + for (const line of lines) { const trimmed = line.trim(); if (!trimmed) continue; @@ -210,8 +216,8 @@ export const gemini = agent({ await handler(event as never); } } catch { - console.log("parse error", trimmed); // ignore parse errors - might be non-JSON output from gemini cli + log.debug(`[gemini] non-JSON stdout line: ${trimmed.substring(0, 200)}`); } } }, diff --git a/agents/opencode.ts b/agents/opencode.ts index 80f7367..497ce94 100644 --- a/agents/opencode.ts +++ b/agents/opencode.ts @@ -75,6 +75,7 @@ export const opencode = agent({ let eventCount = 0; let output = ""; + let stdoutBuffer = ""; // buffer for incomplete lines across chunks const result = await spawn({ cmd: cliPath, args, @@ -83,13 +84,16 @@ export const opencode = agent({ timeout: 600000, // 10 minutes timeout to prevent infinite hangs stdio: ["ignore", "pipe", "pipe"], onStdout: async (chunk) => { - const parsed = JSON.parse(chunk); - log.debug(JSON.stringify(parsed, null, 2)); const text = chunk.toString(); output += text; - // parse each line as JSON (opencode outputs one JSON object per line) - const lines = text.split("\n"); + // buffer incomplete lines across chunks (NDJSON format) + stdoutBuffer += text; + const lines = stdoutBuffer.split("\n"); + + // keep the last element (may be incomplete) in the buffer + stdoutBuffer = lines.pop() || ""; + for (const line of lines) { const trimmed = line.trim(); if (!trimmed) { @@ -99,6 +103,12 @@ export const opencode = agent({ try { const event = JSON.parse(trimmed) as OpenCodeEvent; eventCount++; + + // debug log all events to diagnose ordering and missing MCP/bash tool calls + log.debug( + `» event: type=${event.type} data=${JSON.stringify(event).substring(0, 300)}` + ); + const timeSinceLastActivity = Date.now() - lastActivityTime; if (timeSinceLastActivity > 10000) { const activeToolCalls = toolCallTimings.size; @@ -121,7 +131,8 @@ export const opencode = agent({ ); } } catch { - // non-JSON lines are ignored + // non-JSON lines are ignored (might be debug output from opencode) + log.debug(`» non-JSON stdout line: ${trimmed.substring(0, 200)}`); } } }, @@ -473,6 +484,11 @@ const messageHandlers = { const status = event.part?.state?.status; const output = event.part?.state?.output; + // debug log all tool_use events to diagnose missing bash/MCP tool calls + if (!toolName || !toolId) { + log.debug(`» tool_use event missing toolName or toolId: ${JSON.stringify(event)}`); + } + if (toolName && toolId) { // track tool call in current step if (stepHistory.length > 0) { diff --git a/entry b/entry index ae7f07a..79c53e5 100755 --- a/entry +++ b/entry @@ -91167,6 +91167,7 @@ var gemini = agent({ log.info("\u{1F512} sandbox mode enabled: restricting to read-only operations"); } let finalOutput2 = ""; + let stdoutBuffer = ""; try { const result = await spawn4({ cmd: "node", @@ -91177,7 +91178,9 @@ var gemini = agent({ onStdout: async (chunk) => { const text = chunk.toString(); finalOutput2 += text; - const lines = text.split("\n"); + stdoutBuffer += text; + const lines = stdoutBuffer.split("\n"); + stdoutBuffer = lines.pop() || ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed) continue; @@ -91189,7 +91192,7 @@ var gemini = agent({ await handler2(event); } } catch { - console.log("parse error", trimmed); + log.debug(`[gemini] non-JSON stdout line: ${trimmed.substring(0, 200)}`); } } }, @@ -91300,6 +91303,7 @@ var opencode = agent({ let lastActivityTime = startTime; let eventCount = 0; let output = ""; + let stdoutBuffer = ""; const result = await spawn4({ cmd: cliPath, args: args3, @@ -91309,11 +91313,11 @@ var opencode = agent({ // 10 minutes timeout to prevent infinite hangs stdio: ["ignore", "pipe", "pipe"], onStdout: async (chunk) => { - const parsed2 = JSON.parse(chunk); - log.debug(JSON.stringify(parsed2, null, 2)); const text = chunk.toString(); output += text; - const lines = text.split("\n"); + stdoutBuffer += text; + const lines = stdoutBuffer.split("\n"); + stdoutBuffer = lines.pop() || ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed) { @@ -91322,6 +91326,9 @@ var opencode = agent({ try { const event = JSON.parse(trimmed); eventCount++; + log.debug( + `\xBB event: type=${event.type} data=${JSON.stringify(event).substring(0, 300)}` + ); const timeSinceLastActivity = Date.now() - lastActivityTime; if (timeSinceLastActivity > 1e4) { const activeToolCalls = toolCallTimings.size; @@ -91340,6 +91347,7 @@ var opencode = agent({ ); } } catch { + log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`); } } }, @@ -91507,6 +91515,9 @@ var messageHandlers4 = { const parameters = event.part?.state?.input; const status = event.part?.state?.status; const output = event.part?.state?.output; + if (!toolName || !toolId) { + log.debug(`\xBB tool_use event missing toolName or toolId: ${JSON.stringify(event)}`); + } if (toolName && toolId) { if (stepHistory.length > 0) { stepHistory[stepHistory.length - 1].toolCalls.push(toolName);