diff --git a/tools/peer-call/grok.ts b/tools/peer-call/grok.ts index 7db914c68..06400b034 100644 --- a/tools/peer-call/grok.ts +++ b/tools/peer-call/grok.ts @@ -26,7 +26,7 @@ // 1 — invocation error (bad arguments, cursor-agent missing, etc.) // 2 — Grok returned a non-zero exit (response captured to stderr) -import { readFileSync, statSync } from "node:fs"; +import { closeSync, openSync, readSync, statSync } from "node:fs"; import { spawnSync } from "node:child_process"; const SPAWN_MAX_BUFFER = 64 * 1024 * 1024; @@ -157,23 +157,44 @@ function isRegularFile(path: string): boolean { } function readHead(path: string, bytes: number): string { + // Read only the first `bytes` bytes (matches bash `head -c`). + // readFileSync would load the entire file before slicing — regression + // for large artifacts (logs, dumps) per Codex P1 on #898 (gemini.ts). if (!isRegularFile(path)) return ""; - const buf = readFileSync(path); - return buf.subarray(0, bytes).toString("utf8"); + const buf = Buffer.alloc(bytes); + let fd: number | undefined; + try { + fd = openSync(path, "r"); + const n = readSync(fd, buf, 0, bytes, 0); + return buf.subarray(0, n).toString("utf8"); + } catch { + return ""; + } finally { + if (fd !== undefined) closeSync(fd); + } } function runContextCmd(contextCmd: string): string { // Bash uses `eval "$context_cmd" 2>&1 | head -c 20000`. Match that - // shape: shell -c so user-supplied compound commands work, capture - // both stdout + stderr, truncate to CTX_HEAD_BYTES. User intentionally - // supplies the shell command (per --context-cmd contract); same - // security posture as the bash original's `eval`. - const result = spawnSync("/bin/sh", ["-c", contextCmd], { + // shape end-to-end: pipe through `head -c ` so the shell pipeline + // short-circuits at the truncation boundary instead of buffering the + // full output up to SPAWN_MAX_BUFFER (Codex P2 on #898 — high-volume + // commands like wide `git diff` would otherwise block much longer + // and use much more memory than the bash original). + // User intentionally supplies the shell command (per --context-cmd + // contract); same security posture as the bash original's `eval`. + const wrapped = `(${contextCmd}) 2>&1 | head -c ${String(CTX_HEAD_BYTES)}`; + const result = spawnSync("/bin/sh", ["-c", wrapped], { encoding: "utf8", maxBuffer: SPAWN_MAX_BUFFER, }); - const combined = `${result.stdout}${result.stderr}`; - return combined.slice(0, CTX_HEAD_BYTES); + // Concatenate stdout + stderr: stdout carries the command output + // truncated by `head -c`; stderr carries shell parse errors (e.g., + // syntax errors in contextCmd) which fall OUTSIDE the `( ... ) 2>&1` + // redirection. Per Codex P2 + Copilot on #899 / #898 the parse-error + // diagnostic must reach the prompt or the user sees an empty context + // block on a malformed cmd. + return `${result.stdout}${result.stderr}`.slice(0, CTX_HEAD_BYTES); } const PREAMBLE = `You are Grok, invoked as a peer reviewer by Otto (Claude Opus 4.7