mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
fix(runner): emit finish diagnostics only after a bound rejection
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
e72803a2b6
commit
76918553c8
2 files changed
+12
-3
No files matched your search
@@ -11,6 +11,7 @@ it("retains closed diagnostics through normal warning output before an empty-res
|
|||||||
forward.diagnostic(JSON.stringify(offered));
|
forward.diagnostic(JSON.stringify(offered));
|
||||||
expect(frames).toEqual([]);
|
expect(frames).toEqual([]);
|
||||||
expect(forward.bind("actual-session")).toBe(true);
|
expect(forward.bind("actual-session")).toBe(true);
|
||||||
|
expect(frames).toEqual([]);
|
||||||
forward.diagnostic(JSON.stringify(rejected));
|
forward.diagnostic(JSON.stringify(rejected));
|
||||||
output.send({ method: "turn/completed", params: { threadId: "actual-session", turn: { status: "failed", error: { code: "opencode_empty_response" } } } });
|
output.send({ method: "turn/completed", params: { threadId: "actual-session", turn: { status: "failed", error: { code: "opencode_empty_response" } } } });
|
||||||
expect(frames).toEqual([
|
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("actual-session")).toBe(true);
|
||||||
expect(forward.bind("foreign-session")).toBe(false);
|
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));
|
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));
|
for (let i = 0; i < 12; i++) forward.diagnostic(JSON.stringify(rejected));
|
||||||
forward.diagnostic(JSON.stringify(offered));
|
forward.diagnostic(JSON.stringify(offered));
|
||||||
expect(frames).toHaveLength(9);
|
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);
|
return open({model:"provider/model",cwd:"/synthetic"},false);
|
||||||
`);
|
`);
|
||||||
await invoke(Driver,createOpenCodeFinishDiagnosticForwarder,output.send,{env:{},cwd:()=>"/synthetic",stderr:{write:(message:string)=>stderr.push(message)}});
|
await invoke(Driver,createOpenCodeFinishDiagnosticForwarder,output.send,{env:{},cwd:()=>"/synthetic",stderr:{write:(message:string)=>stderr.push(message)}});
|
||||||
|
expect(frames).toEqual([]);
|
||||||
diagnostic(JSON.stringify(rejected));
|
diagnostic(JSON.stringify(rejected));
|
||||||
diagnostic("unrelated private diagnostic");
|
diagnostic("unrelated private diagnostic");
|
||||||
output.send({method:"turn/completed",params:{threadId:"actual-session",turn:{status:"failed",error:{code:"opencode_empty_response"}}}});
|
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(stderr).toHaveLength(3);
|
||||||
expect(JSON.stringify(frames)).not.toContain("unrelated private diagnostic");
|
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"}}}]);
|
||||||
|
});
|
||||||
@@ -11,7 +11,6 @@ export function createOpenCodeFinishDiagnosticForwarder(send: (frame: Warning) =
|
|||||||
bind(id: string) {
|
bind(id: string) {
|
||||||
if (!id || id.length > 512 || threadId) return false;
|
if (!id || id.length > 512 || threadId) return false;
|
||||||
threadId = id;
|
threadId = id;
|
||||||
if (offered) emit(offered);
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
diagnostic(message: string) {
|
diagnostic(message: string) {
|
||||||
@@ -25,7 +24,6 @@ export function createOpenCodeFinishDiagnosticForwarder(send: (frame: Warning) =
|
|||||||
if (value.stage === "offered") {
|
if (value.stage === "offered") {
|
||||||
if (offered || keys !== "schema,schemaSha256,stage") return;
|
if (offered || keys !== "schema,schemaSha256,stage") return;
|
||||||
offered = value;
|
offered = value;
|
||||||
emit(value);
|
|
||||||
} else if (value.stage === "input_rejected") {
|
} else if (value.stage === "input_rejected") {
|
||||||
if (!threadId || !offered || rejections >= 8 || value.schemaSha256 !== offered.schemaSha256
|
if (!threadId || !offered || rejections >= 8 || value.schemaSha256 !== offered.schemaSha256
|
||||||
|| keys !== "callIdSha256,continuationShape,rootShape,schema,schemaSha256,stage,validationCategory"
|
|| 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))
|
|| !["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.rootShape))
|
||||||
|| !["object", "array", "string", "number", "boolean", "null", "omitted"].includes(String(value.continuationShape))) return;
|
|| !["object", "array", "string", "number", "boolean", "null", "omitted"].includes(String(value.continuationShape))) return;
|
||||||
|
if (rejections === 0) emit(offered);
|
||||||
rejections += 1;
|
rejections += 1;
|
||||||
emit(value);
|
emit(value);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user