From 76918553c82d939f4de476d2b72eaa097b946894 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 6 Nov 2026 00:09:31 -0600 Subject: [PATCH] fix(runner): emit finish diagnostics only after a bound rejection Co-Authored-By: Paperclip --- .../cli/opencode-proxy-finish-diagnostics.test.ts | 12 +++++++++++- .../src/cli/opencode-proxy-finish-diagnostics.ts | 3 +-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.test.ts b/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.test.ts index 7614cc98edf..cc1512befe8 100644 --- a/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.test.ts +++ b/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.test.ts @@ -11,6 +11,7 @@ it("retains closed diagnostics through normal warning output before an empty-res forward.diagnostic(JSON.stringify(offered)); expect(frames).toEqual([]); expect(forward.bind("actual-session")).toBe(true); + expect(frames).toEqual([]); forward.diagnostic(JSON.stringify(rejected)); output.send({ method: "turn/completed", params: { threadId: "actual-session", turn: { status: "failed", error: { code: "opencode_empty_response" } } } }); expect(frames).toEqual([ @@ -33,7 +34,7 @@ it("rejects unbound rejections, foreign binding/schema, unknown fields and arbit expect(forward.bind("actual-session")).toBe(true); expect(forward.bind("foreign-session")).toBe(false); for (const invalid of [{ ...rejected, schemaSha256: "c".repeat(64) }, { ...rejected, input: "private" }, { ...rejected, rootShape: {} }, { ...rejected, rootShape: ["object"] }, { ...rejected, continuationShape: "invalid" }]) forward.diagnostic(JSON.stringify(invalid)); - expect(frames).toHaveLength(1); + expect(frames).toHaveLength(0); for (let i = 0; i < 12; i++) forward.diagnostic(JSON.stringify(rejected)); forward.diagnostic(JSON.stringify(offered)); expect(frames).toHaveLength(9); @@ -58,6 +59,7 @@ it("forwards from the actual proxy open callback instead of losing normal semant return open({model:"provider/model",cwd:"/synthetic"},false); `); await invoke(Driver,createOpenCodeFinishDiagnosticForwarder,output.send,{env:{},cwd:()=>"/synthetic",stderr:{write:(message:string)=>stderr.push(message)}}); + expect(frames).toEqual([]); diagnostic(JSON.stringify(rejected)); diagnostic("unrelated private diagnostic"); output.send({method:"turn/completed",params:{threadId:"actual-session",turn:{status:"failed",error:{code:"opencode_empty_response"}}}}); @@ -66,3 +68,11 @@ it("forwards from the actual proxy open callback instead of losing normal semant expect(stderr).toHaveLength(3); expect(JSON.stringify(frames)).not.toContain("unrelated private diagnostic"); }); + +it("keeps healthy open and success silent", () => { + const frames:unknown[]=[],output=createOpenCodeProxyOutput(frame=>frames.push(frame)); + const forward=createOpenCodeFinishDiagnosticForwarder(output.send); + forward.diagnostic(JSON.stringify(offered));forward.bind("healthy-session"); + output.send({method:"turn/completed",params:{threadId:"healthy-session",turn:{status:"completed"}}}); + expect(frames).toEqual([{method:"turn/completed",params:{threadId:"healthy-session",turn:{status:"completed"}}}]); +}); diff --git a/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.ts b/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.ts index 69210d1f226..c9790efbc8b 100644 --- a/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.ts +++ b/packages/paperclip-runner/src/cli/opencode-proxy-finish-diagnostics.ts @@ -11,7 +11,6 @@ export function createOpenCodeFinishDiagnosticForwarder(send: (frame: Warning) = bind(id: string) { if (!id || id.length > 512 || threadId) return false; threadId = id; - if (offered) emit(offered); return true; }, diagnostic(message: string) { @@ -25,7 +24,6 @@ export function createOpenCodeFinishDiagnosticForwarder(send: (frame: Warning) = if (value.stage === "offered") { if (offered || keys !== "schema,schemaSha256,stage") return; offered = value; - emit(value); } else if (value.stage === "input_rejected") { if (!threadId || !offered || rejections >= 8 || value.schemaSha256 !== offered.schemaSha256 || keys !== "callIdSha256,continuationShape,rootShape,schema,schemaSha256,stage,validationCategory" @@ -34,6 +32,7 @@ export function createOpenCodeFinishDiagnosticForwarder(send: (frame: Warning) = || !["root", "continuation", "other"].includes(String(value.validationCategory)) || !["object", "array", "string", "number", "boolean", "null", "omitted"].includes(String(value.rootShape)) || !["object", "array", "string", "number", "boolean", "null", "omitted"].includes(String(value.continuationShape))) return; + if (rejections === 0) emit(offered); rejections += 1; emit(value); }