From a364fcf170540d08bfceddf1e416beb658774e11 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Wed, 9 Sep 2026 23:52:33 -0700 Subject: [PATCH 1/2] Gate tool-output URI reads on named truncation notices The prompt always preferred a tool-output URI over re-reading huge blobs, so complete inline wait_agents results were re-read. Only invite that read when a truncation notice named a URI. --- src/agent/prompts.ts | 4 ++-- src/prompts.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/agent/prompts.ts b/src/agent/prompts.ts index 749cfc4e4..42cf388ff 100644 --- a/src/agent/prompts.ts +++ b/src/agent/prompts.ts @@ -78,7 +78,7 @@ export function buildHarnessFacts( "- Shell file-writes and deletions are blocked; never use echo/heredoc/sed/rm as a substitute for product tools. Path tools are the DIY surface.", ]), "- Use the provided tools for file reads/searches instead of shelling out as a substitute.", - "- read_file accepts a filesystem path or a tool-output:///{callId} URI from a prior tool result when the harness exposes one; prefer the URI over re-reading huge blobs.", + "- read_file accepts a filesystem path or a tool-output:///{callId} URI from a prior tool result when the harness exposes one. Only read_file a tool-output:// URI if the truncation notice on that result named one; do not re-read a complete inline result.", "- run_shell has no default timeout; pass timeout for builds, tests, and other long commands. Prefer background:true for builds, test suites, and dev servers: it returns a shell_id at once, the result is delivered when the process finishes (foreground runs hold steers; background runs do not), and shell_collect collects or cancels later. background does not change the retained shell cwd.", "- Shell find, rg, and grep -r are blocked — they can walk huge trees and OOM the host. Prefer the bounded grep/search_files tools, and do not substitute another unbounded walk (fd, ls -R, scripted os.walk).", ...(subAgent @@ -226,7 +226,7 @@ export function buildPromptDisciplineBlock( const TOOL_SUMMARIES: Record = { read_file: - "read a file or tool-output:///{callId} from a prior tool result (prefer over cat/head/tail in the shell)", + "read a file or tool-output:///{callId} from a prior tool result (prefer over cat/head/tail in the shell). Only read_file a tool-output:// URI if the truncation notice named one", write_file: "create or overwrite a file (never shell redirects or heredocs)", edit_file: "make a surgical edit (exact old_string match, or start_line/end_line line-range mode; never include read_file's NNNNNN\\t line prefix; substring failures include nearby file text; prefer over sed/awk in the shell)", diff --git a/src/prompts.test.ts b/src/prompts.test.ts index ab52289ba..c264cae14 100644 --- a/src/prompts.test.ts +++ b/src/prompts.test.ts @@ -87,6 +87,28 @@ test("harness facts state only the non-derivable tool and safety rules", () => { expect(facts).not.toContain("Tool results already render richly"); }); +test("harness facts gate tool-output URI reads on a named truncation notice", () => { + const facts = buildHarnessFacts(); + expect(facts).toContain("read_file"); + expect(facts).toMatch(/filesystem path/i); + expect(facts).toMatch(/tool-output:\/\//); + expect(facts).toMatch(/truncat/i); + expect(facts).toMatch(/named/i); + expect(facts).not.toMatch(/prefer the URI/i); + expect(facts).not.toMatch(/re-reading huge blobs/i); + expect(facts).toMatch(/complete inline/i); +}); + +test("read_file catalog summary gates tool-output URI reads on truncation", () => { + const listed = buildAvailableTools(["read_file"]); + expect(listed).toContain("read_file"); + expect(listed).toMatch(/tool-output:\/\//); + expect(listed).toMatch(/truncat/i); + expect(listed).toMatch(/named/i); + expect(listed).toContain("cat/head/tail"); + expect(listed).not.toMatch(/prefer the URI/i); +}); + test("harness facts name skill_search as a resident catalog tool", () => { const facts = buildHarnessFacts(); expect(facts).toMatch( From c7bad0f4a21cc56b1e100832f86bb346c2890afe Mon Sep 17 00:00:00 2001 From: Sawyer Date: Thu, 10 Sep 2026 01:59:23 -0700 Subject: [PATCH 2/2] Pin tool-output URI prompt to a named truncation notice Loose /named/i matchers still passed if the URI sentence dropped "named one", because harness facts already mention named specialists. --- src/prompts.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/prompts.test.ts b/src/prompts.test.ts index c264cae14..5e2df2f6a 100644 --- a/src/prompts.test.ts +++ b/src/prompts.test.ts @@ -92,19 +92,17 @@ test("harness facts gate tool-output URI reads on a named truncation notice", () expect(facts).toContain("read_file"); expect(facts).toMatch(/filesystem path/i); expect(facts).toMatch(/tool-output:\/\//); - expect(facts).toMatch(/truncat/i); - expect(facts).toMatch(/named/i); + expect(facts).toContain("truncation notice on that result named one"); + expect(facts).toContain("do not re-read a complete inline result"); expect(facts).not.toMatch(/prefer the URI/i); expect(facts).not.toMatch(/re-reading huge blobs/i); - expect(facts).toMatch(/complete inline/i); }); test("read_file catalog summary gates tool-output URI reads on truncation", () => { const listed = buildAvailableTools(["read_file"]); expect(listed).toContain("read_file"); expect(listed).toMatch(/tool-output:\/\//); - expect(listed).toMatch(/truncat/i); - expect(listed).toMatch(/named/i); + expect(listed).toContain("truncation notice named one"); expect(listed).toContain("cat/head/tail"); expect(listed).not.toMatch(/prefer the URI/i); });