mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +02:00
Forward OpenCode completion feedback through its real proxy
Wait for controller acceptance before proposing a result. Preserve final-response feedback and concrete rejection reasons across the proxy. Cover the production boundary and admit only the bounded corrected source. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
3a7349ddc6
commit
e171282078
11 files changed
+148
-12
No files matched your search
@@ -13,3 +13,9 @@ Calibrate with correct paraphrases and plausible wrong answers, including the ac
|
||||
The human's paid-run authorization and subsequent request to fix the omission support one bounded corrective confirmation: the six exact `native-instruction-consolidation` candidate cells, one attempt each, no automatic retries, native Codex `gpt-5.6-sol`, ACPX Claude `claude-sonnet-5`, and OpenCode `openrouter/deepseek/deepseek-v4-flash-0731`, original local deadlines, and 1,000-cent company/agent hard stops. Each production correction must have a new immutable source/definition admission before another bounded confirmation. Preserve stopped campaigns, failed cells and canceled-before-provider cells separately. Never reroll an unchanged frozen failure. No new baseline provider run, broader campaign or merge is part of this correction. Stop and inspect a usable behavior failure.
|
||||
|
||||
Report exact source, definition/grader hashes, six outcomes, retained visible replies and document targets, actual run count, cleanup and billing coverage. Passing a single trial does not prove general equivalence, cause, live resume behavior, coding quality, speed or cost trends.
|
||||
|
||||
The receipt-only correction at `be604fcbf81ff238ee38774ff6e644bf0d84eb2d` failed its first Claude document cell; five remaining jobs were canceled before paid execution. The next correction at `3a7349ddc60142283c394121882288f2db07b215` passed both Claude and both Codex cases, but OpenCode again omitted the document link; its blocker job was canceled with the paid step skipped. Preserve both genuine failures and all canceled jobs. Browser navigation was not reached in either missing-link failure.
|
||||
|
||||
OpenCode's inner driver admitted the finishing report locally without invoking the controller feedback handler. Its proxy also discarded the controller response envelope, including the success flag. The correction forwards the exact terminal call ID, thread, turn, tool and structured result through the existing controller request channel. It waits for the controller before committing the inner result, preserves accepted response text and concrete rejection reasons, and rejects malformed acceptance or a turn that ended during feedback. Dynamic-tool unwrapping stays unchanged. A provider-free test crosses the real runnerd, proxy and fake OpenCode boundary: the first completion is rejected, the provider corrects it in the same turn, the second receives the exact final-response feedback, and only one semantic result is proposed.
|
||||
|
||||
This correction changes four additional OpenCode-only production paths. The shared instruction, server receipt/feedback and UI bytes remain identical to 3a7349d. Admit the new source and measure all scripted boundaries again. Confirm only the two affected OpenCode live cases on the new immutable source, one attempt each with the original model, deadlines and budget stops. Keep the four passing Claude/Codex observations explicitly attributed to 3a7349d, backed by the exact provider-path delta and final-head CI. Do not relabel them as live runs on the new head. No new baseline or unchanged-source paid retry is included.
|
||||
@@ -67,6 +67,7 @@ export function createNativeSessionBackend(
|
||||
);
|
||||
}
|
||||
return createOpenCodeNativeSessionBackend(input, {
|
||||
completionFeedback: options.completionFeedback,
|
||||
runtimeDirectory: options.opencodeRuntimeDirectory,
|
||||
environment: options.opencodeEnvironment,
|
||||
command: options.opencodeCommand,
|
||||
|
||||
@@ -220,7 +220,11 @@ afterAll(() => {
|
||||
const sourcePaths = ["runtime-context.ts", "codex-native-backend.ts", "opencode-native-backend.ts"].map(file => `packages/paperclip-runner/src/backends/${file}`);
|
||||
sourcePaths.push("server/src/services/native-runtime/paperclip-runner-tool-authority.ts",
|
||||
"server/src/services/native-runtime/native-completion-feedback.ts",
|
||||
"ui/src/lib/issue-reference.ts", "ui/src/components/MarkdownBody.tsx");
|
||||
"ui/src/lib/issue-reference.ts", "ui/src/components/MarkdownBody.tsx",
|
||||
"packages/paperclip-runner/src/backends/native-backend-factory.ts",
|
||||
"packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts",
|
||||
"packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts",
|
||||
"packages/paperclip-runner/src/cli/opencode-proxy-input.ts");
|
||||
const source = inspectNativeCompletionSourceMetadata({ repositoryRoot: repositoryRoot.pathname,
|
||||
sourceFiles: sourcePaths,
|
||||
baseSha: "2a8a99e4a5f69aa803b3f10b982f583e75a87042", variant: "measurement" });
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
shouldAnnounceOpenCodeProxyTurn,
|
||||
shouldForwardOpenCodeProxyItem,
|
||||
} from "./opencode-proxy-events.js";
|
||||
import { enqueueOpenCodeProxyInput } from "./opencode-proxy-input.js";
|
||||
import { enqueueOpenCodeProxyInput, openCodeProxyCompletionFeedback } from "./opencode-proxy-input.js";
|
||||
import {
|
||||
assertOpenCodeProxyCollaborationMode,
|
||||
openCodeProxyCollaborationModes,
|
||||
@@ -40,7 +40,7 @@ type RpcMessage = {
|
||||
const input = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
||||
const pending = new Map<
|
||||
string,
|
||||
{ resolve(value: unknown): void; reject(error: Error): void }
|
||||
{ resolve(value: unknown): void; reject(error: Error): void; retainEnvelope: boolean }
|
||||
>();
|
||||
let nextServerRequestId = 1;
|
||||
let driver: OpenCodeServerDriver | null = null;
|
||||
@@ -57,11 +57,11 @@ function send(value: unknown): void {
|
||||
process.stdout.write(`${JSON.stringify(value)}\n`);
|
||||
}
|
||||
|
||||
function requestController(method: string, params: unknown): Promise<unknown> {
|
||||
function requestController(method: string, params: unknown, retainEnvelope = false): Promise<unknown> {
|
||||
const id = `opencode-${nextServerRequestId++}`;
|
||||
send({ id, method, params });
|
||||
return new Promise((resolveValue, reject) =>
|
||||
pending.set(id, { resolve: resolveValue, reject }),
|
||||
pending.set(id, { resolve: resolveValue, reject, retainEnvelope }),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ async function open(
|
||||
),
|
||||
runtimeContext,
|
||||
dynamicTools,
|
||||
completionFeedback: openCodeProxyCompletionFeedback((method, params) => requestController(method, params, true)),
|
||||
dynamicToolHandler: async (call) =>
|
||||
requestController("item/tool/call", {
|
||||
threadId: session?.ids().driverSessionId ?? "opening",
|
||||
@@ -319,7 +320,7 @@ async function handle(message: RpcMessage): Promise<void> {
|
||||
else {
|
||||
const contentItems = record(message.result).contentItems;
|
||||
waiter.resolve(
|
||||
Array.isArray(contentItems)
|
||||
!waiter.retainEnvelope && Array.isArray(contentItems)
|
||||
? (contentItems[0] ?? message.result)
|
||||
: message.result,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { openCodeProxyCompletionFeedback } from "./opencode-proxy-input.js";
|
||||
import type { PrpStructuredRunResult } from "../protocol/replay-contract.js";
|
||||
|
||||
const result = { reportedWorkDisposition: "done" } as PrpStructuredRunResult;
|
||||
const call = { tool: "paperclip_finish", callId: "call-finish", threadId: "thread", turnId: "turn" };
|
||||
|
||||
describe("OpenCode controller completion feedback", () => {
|
||||
it.each(["paperclip_finish", "paperclip_block"])("preserves %s identity and exact accepted response text", async tool => {
|
||||
const feedback = "Include [Saved document](/PAP/issues/PAP-1#document-plan) in your final response.";
|
||||
const request = vi.fn().mockResolvedValue({ success: true, contentItems: [{ type: "inputText", text: feedback }] });
|
||||
await expect(openCodeProxyCompletionFeedback(request)(result, { ...call, tool })).resolves.toBe(feedback);
|
||||
expect(request).toHaveBeenCalledExactlyOnceWith("item/tool/call", { ...call, tool, arguments: result });
|
||||
});
|
||||
|
||||
it("preserves the controller rejection for same-turn correction", async () => {
|
||||
const request = vi.fn().mockResolvedValue({ success: false, contentItems: [{ type: "inputText", text: "Resolve the pending approval first." }] });
|
||||
await expect(openCodeProxyCompletionFeedback(request)(result, call)).rejects.toThrow("Resolve the pending approval first.");
|
||||
});
|
||||
|
||||
it.each([null, { accepted: true }, { success: true }, { success: true, contentItems: [{ type: "inputText", text: "" }] }, { success: true, contentItems: [{ type: "image", text: "accepted" }] }])("fails closed for malformed acceptance %j", async response => {
|
||||
await expect(openCodeProxyCompletionFeedback(vi.fn().mockResolvedValue(response))(result, call)).rejects.toThrow(/Completion controller/);
|
||||
});
|
||||
});
|
||||
@@ -1 +1,25 @@
|
||||
export { enqueueSerialInput as enqueueOpenCodeProxyInput } from "./serial-input-queue.js";
|
||||
|
||||
import type { OpenCodeCompletionFeedback } from "../drivers/opencode/opencode-server-driver.js";
|
||||
|
||||
/** Preserve the exact call binding and the controller's acceptance/rejection. */
|
||||
export function openCodeProxyCompletionFeedback(
|
||||
request: (method: string, params: Record<string, unknown>) => Promise<unknown>,
|
||||
): OpenCodeCompletionFeedback {
|
||||
return async (result, call) => {
|
||||
const response = await request("item/tool/call", { ...call, arguments: result });
|
||||
if (!response || typeof response !== "object" || Array.isArray(response))
|
||||
throw new Error("Completion controller returned an invalid response");
|
||||
const value = response as Record<string, unknown>;
|
||||
const items = Array.isArray(value.contentItems) ? value.contentItems : [];
|
||||
const feedback = items.flatMap(item => {
|
||||
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
||||
const entry = item as Record<string, unknown>;
|
||||
return entry.type === "inputText" && typeof entry.text === "string" ? [entry.text] : [];
|
||||
}).join("\n");
|
||||
if (value.success === false) throw new Error(feedback || "Completion report rejected by the controller");
|
||||
if (value.success !== true || !feedback.trim())
|
||||
throw new Error("Completion controller omitted accepted feedback");
|
||||
return feedback;
|
||||
};
|
||||
}
|
||||
@@ -85,6 +85,13 @@ type DynamicToolHandler = (call: {
|
||||
arguments: unknown;
|
||||
}) => Promise<unknown>;
|
||||
|
||||
export type OpenCodeCompletionFeedback = (result: PrpStructuredRunResult, call: {
|
||||
tool: string;
|
||||
callId: string;
|
||||
threadId: string;
|
||||
turnId: string;
|
||||
}) => Promise<string>;
|
||||
|
||||
export interface OpenCodeServerDriverOptions {
|
||||
model: string;
|
||||
permissionMode?: "allow" | "ask" | "deny";
|
||||
@@ -105,6 +112,7 @@ export interface OpenCodeServerDriverOptions {
|
||||
environment?: NodeJS.ProcessEnv;
|
||||
dynamicTools?: readonly Readonly<Record<string, unknown>>[];
|
||||
dynamicToolHandler?: DynamicToolHandler;
|
||||
completionFeedback?: OpenCodeCompletionFeedback;
|
||||
onSpawn?: (meta: {
|
||||
pid: number;
|
||||
processGroupId: number | null;
|
||||
@@ -344,6 +352,7 @@ export class OpenCodeServerDriver implements HarnessDriver {
|
||||
this.#options.systemInstructions ??
|
||||
CODEX_SKILLLESS_BASE_INSTRUCTIONS,
|
||||
dynamicToolHandler: this.#options.dynamicToolHandler,
|
||||
completionFeedback: this.#options.completionFeedback,
|
||||
snapshot,
|
||||
now: this.#options.now ?? (() => new Date()),
|
||||
});
|
||||
@@ -380,6 +389,7 @@ class OpenCodeHarnessSession implements HarnessSession {
|
||||
readonly #taskEnvelope: CodexTaskEnvelope;
|
||||
readonly #systemInstructions: string;
|
||||
readonly #dynamicToolHandler?: DynamicToolHandler;
|
||||
readonly #completionFeedback?: OpenCodeCompletionFeedback;
|
||||
readonly #now: () => Date;
|
||||
readonly #events = new AsyncQueue<PrpEvent>();
|
||||
readonly #transcript: PrpEvent[] = [];
|
||||
@@ -449,6 +459,7 @@ class OpenCodeHarnessSession implements HarnessSession {
|
||||
taskEnvelope: CodexTaskEnvelope;
|
||||
systemInstructions: string;
|
||||
dynamicToolHandler?: DynamicToolHandler;
|
||||
completionFeedback?: OpenCodeCompletionFeedback;
|
||||
snapshot: PersistedHarnessSession | null;
|
||||
now: () => Date;
|
||||
}) {
|
||||
@@ -465,6 +476,7 @@ class OpenCodeHarnessSession implements HarnessSession {
|
||||
this.#taskEnvelope = input.taskEnvelope;
|
||||
this.#systemInstructions = input.systemInstructions;
|
||||
this.#dynamicToolHandler = input.dynamicToolHandler;
|
||||
this.#completionFeedback = input.completionFeedback;
|
||||
this.#now = input.now;
|
||||
this.#sendFullContext = input.snapshot === null && this.#conversationMode !== "prepared";
|
||||
this.#sourceSequence = input.snapshot?.lastSourceSequence ?? 0;
|
||||
@@ -958,6 +970,19 @@ class OpenCodeHarnessSession implements HarnessSession {
|
||||
throw new Error(`Semantic result criteria must contain exactly these criterionIds, once each: ${JSON.stringify(expectedIds)}. Keep contractRevision ${JSON.stringify(this.#taskEnvelope.completionContract.revision)}.`);
|
||||
}
|
||||
const fingerprint = canonicalJson(validation.result);
|
||||
if (this.#resultFingerprint && this.#resultFingerprint !== fingerprint)
|
||||
throw new Error("A different semantic result was already committed");
|
||||
// Wait for the bound controller before committing or resolving the
|
||||
// provider call. A rejection remains repairable in this same turn.
|
||||
const feedback = this.#completionFeedback
|
||||
? await this.#completionFeedback(validation.result, {
|
||||
tool, callId: call.callId, threadId: this.#providerSessionId, turnId,
|
||||
})
|
||||
: "Semantic completion accepted.";
|
||||
if (typeof feedback !== "string" || !feedback.trim())
|
||||
throw new Error("Completion feedback omitted its response text");
|
||||
if (this.#closed || this.#activeTurnId !== turnId || this.#terminalTurns.has(turnId))
|
||||
throw new Error("The turn ended while checking completion. The result was not accepted.");
|
||||
if (this.#resultFingerprint && this.#resultFingerprint !== fingerprint)
|
||||
throw new Error("A different semantic result was already committed");
|
||||
if (!this.#resultFingerprint) {
|
||||
@@ -979,12 +1004,12 @@ class OpenCodeHarnessSession implements HarnessSession {
|
||||
type: "tool_result",
|
||||
id: call.callId,
|
||||
tool_use_id: call.callId,
|
||||
result: "Semantic completion accepted.",
|
||||
result: feedback,
|
||||
},
|
||||
},
|
||||
{ turnId, itemId: call.callId },
|
||||
);
|
||||
return { accepted: true };
|
||||
return { accepted: true, feedback };
|
||||
} catch (error) {
|
||||
// A rejected semantic call still completes its tool activity item.
|
||||
// Otherwise a later question can appear to have an in-flight tool.
|
||||
|
||||
@@ -7539,7 +7539,7 @@ it.each([true, false])("preserves prepared OpenCode cleanup errors (primary fail
|
||||
expect((failure as Error).message).not.toContain("fixture-secret");
|
||||
});
|
||||
|
||||
it("preserves prepared input through runnerd and the real OpenCode proxy boundary", async () => {
|
||||
it("preserves prepared input and completion feedback through runnerd and the real OpenCode proxy boundary", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "runnerd-prepared-opencode-"));
|
||||
// GitHub-hosted Linux toolcache Node can be group-writable, unlike the AWS
|
||||
// fleet. Qualify an owned copy with strict permissions, never chmod the host
|
||||
@@ -7589,6 +7589,8 @@ it("preserves prepared input through runnerd and the real OpenCode proxy boundar
|
||||
objective: "Preserve the prepared task.", contractRevision: "prepared-v1",
|
||||
criteria: [{ id: "objective", requirement: "Keep this request unchanged." }],
|
||||
});
|
||||
let completionCalls = 0;
|
||||
const feedback = "Include [Saved document](/PAP/issues/PAP-1#document-plan) in your final response.";
|
||||
const driver = new CodexAppServerDriver({
|
||||
taskEnvelope: task,
|
||||
conversationMode: "prepared",
|
||||
@@ -7596,24 +7598,37 @@ it("preserves prepared input through runnerd and the real OpenCode proxy boundar
|
||||
transportFactory: () => bundle.transport,
|
||||
workingDirectoryAuthority: "remote_runner",
|
||||
environment: { PAPERCLIP_WORKSPACE_CWD: root },
|
||||
completionFeedback: async () => {
|
||||
completionCalls += 1;
|
||||
if (completionCalls === 1) throw new Error("Keep the task's required document link in the final response.");
|
||||
return feedback;
|
||||
},
|
||||
});
|
||||
let session: Awaited<ReturnType<typeof driver.openSession>> | undefined;
|
||||
const prepared = JSON.stringify({
|
||||
schema: "paperclip.native-model-envelope.v3",
|
||||
task: { prompt: "Keep this request unchanged." },
|
||||
task: { prompt: "Keep this completion-feedback request unchanged." },
|
||||
completionContract: { revision: "prepared-v1", criteria: task.completionContract.criteria },
|
||||
});
|
||||
await withPreparedOpenCodeCleanup({
|
||||
run: async () => {
|
||||
session = await driver.openSession({ runId: "prepared-opencode", normalizedSessionId: "prepared-opencode", workingDirectory: root });
|
||||
await session.startTurn({ message: { role: "user", text: prepared } });
|
||||
const events: PrpEvent[] = [];
|
||||
for await (const event of session.events()) {
|
||||
events.push(event);
|
||||
if (event.eventType === "turn.completed") break;
|
||||
}
|
||||
expect(completionCalls).toBe(2);
|
||||
expect(events.filter(event => event.eventType === "run.result.proposed")).toHaveLength(1);
|
||||
const sessionRoots = (await readdir(runtime, { withFileTypes: true })).filter((entry) => entry.isDirectory());
|
||||
expect(sessionRoots).toHaveLength(1);
|
||||
const requests = (await readFile(join(runtime, sessionRoots[0]!.name, "data/fake-prompt-requests.ndjson"), "utf8")).trim().split("\n").map((line) => JSON.parse(line));
|
||||
expect(requests.map((request) => request.parts)).toEqual([[{ type: "text", text: prepared }]]);
|
||||
const outcomes = JSON.parse(await readFile(join(runtime, sessionRoots[0]!.name, "data/fake-completion-feedback.json"), "utf8"));
|
||||
expect(outcomes).toHaveLength(2);
|
||||
expect(outcomes[0].result).toMatchObject({ isError: true, content: [{ text: expect.stringContaining("required document link") }] });
|
||||
expect(outcomes[1].result).toMatchObject({ content: [{ text: expect.stringContaining(feedback) }] });
|
||||
},
|
||||
closeSession: async () => { await session?.close(); },
|
||||
closeTransport: () => bundle.transport.close(),
|
||||
|
||||
@@ -240,10 +240,18 @@ async function callTerminalTool(promptBody) {
|
||||
const rejected = await mcpRequest("tools/call", { name: "paperclip_finish", arguments: bad });
|
||||
await writeFile(join(process.env.XDG_DATA_HOME, "fake-criteria-repair.json"), JSON.stringify(rejected));
|
||||
}
|
||||
return mcpRequest("tools/call", {
|
||||
const call = () => mcpRequest("tools/call", {
|
||||
name: blocked ? "paperclip_block" : "paperclip_finish",
|
||||
arguments: result,
|
||||
});
|
||||
const first = await call();
|
||||
if (String(prompt.message ?? prompt.task?.prompt ?? "").includes("completion-feedback")) {
|
||||
const outcomes = [first];
|
||||
if (first.result?.isError) outcomes.push(await call());
|
||||
await writeFile(join(process.env.XDG_DATA_HOME, "fake-completion-feedback.json"), JSON.stringify(outcomes));
|
||||
return outcomes.at(-1);
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
const server = createServer(async (request, response) => {
|
||||
|
||||
@@ -37,7 +37,7 @@ describe("native instruction comparison admission", () => {
|
||||
expect(nativeInstructionVariant(file => baseline.get(file)!)).toBe("baseline");
|
||||
const candidate = new Map(files.map(file => [file, readFileSync(new URL(`../../${file}`, import.meta.url))]));
|
||||
const currentVariant = nativeInstructionVariant(file => candidate.get(file)!);
|
||||
expect(["baseline", "candidate", "corrected", "feedback"]).toContain(currentVariant);
|
||||
expect(["baseline", "candidate", "corrected", "feedback", "opencodeFeedback"]).toContain(currentVariant);
|
||||
candidate.set(files[0]!, currentVariant === "candidate" ? baseline.get(files[0]!)! : Buffer.from("unknown source"));
|
||||
expect(() => nativeInstructionVariant(file => candidate.get(file)!)).toThrow("Mixed or unknown");
|
||||
baseline.set(files[0]!, Buffer.from("unknown source"));
|
||||
@@ -91,6 +91,7 @@ describe("native instruction comparison admission", () => {
|
||||
{ ...measurement, sourceDirty: true }, { ...measurement, providerCalls: 1 },
|
||||
{ ...measurement, sourceSha: "other" }, { ...measurement, fixtureSha256: "stale" },
|
||||
{ ...measurement, sourceHashes: { ...measurement.sourceHashes, "runtime-context.ts": "wrong" } },
|
||||
{ ...measurement, sourceHashes: { ...measurement.sourceHashes, "opencode-app-server-proxy.ts": "wrong" } },
|
||||
]) expect(() => validateNativeInstructionMeasurement(invalid, source)).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -13,8 +13,15 @@ export const NATIVE_INSTRUCTION_DEFAULT_SHA256 = "c6318bf7e425cc0dd7acd4ff2905da
|
||||
export const NATIVE_INSTRUCTION_PREFLIGHT_ENV = "PAPERCLIP_NATIVE_INSTRUCTION_PREFLIGHT";
|
||||
const root = resolve(import.meta.dirname, "../..");
|
||||
const hash = (bytes: Buffer | string) => createHash("sha256").update(bytes).digest("hex");
|
||||
const OPENCODE_FEEDBACK_BASE_FILES = {
|
||||
"packages/paperclip-runner/src/backends/native-backend-factory.ts": "8ca5fdcecc13dc8749da5328a22154ec7590356fd4cb77380f802a3018af667c",
|
||||
"packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts": "a5bf84966846feab174d1ca94dda60cff06edb7cabb3d7d5d71e07d5a4af3912",
|
||||
"packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts": "1969805c0d72ee7e4bdfc518fb8d31e6aedd4bda750a2922ec0f4d5c359dfbd6",
|
||||
"packages/paperclip-runner/src/cli/opencode-proxy-input.ts": "1102bea34dedfb4b4d2ce9ca8011e95f0fce227a6a0b9d2993ad08df00b4e433",
|
||||
} as const;
|
||||
export const NATIVE_INSTRUCTION_VARIANTS = {
|
||||
baseline: {
|
||||
...OPENCODE_FEEDBACK_BASE_FILES,
|
||||
"packages/paperclip-runner/src/backends/runtime-context.ts": "e7c46e81cc9c93f9a4f805b5091ef08c59ea70c62f208cfda70d6edeed363898",
|
||||
"packages/paperclip-runner/src/backends/codex-native-backend.ts": "f276d436391c9e2f0a7e58ce1302b5c9f1a39ac01c148be257f9d455515a31b9",
|
||||
"packages/paperclip-runner/src/backends/opencode-native-backend.ts": "d54ddde3a3fdffcda3490d813d27b3b4891448e11cd05f61f9bb4b292d9fd6c2",
|
||||
@@ -24,6 +31,7 @@ export const NATIVE_INSTRUCTION_VARIANTS = {
|
||||
"ui/src/components/MarkdownBody.tsx": "07543156b81e51e72cfc77262f135bdb19eaa6bcccf6a3aef60c20faddcca759",
|
||||
},
|
||||
candidate: {
|
||||
...OPENCODE_FEEDBACK_BASE_FILES,
|
||||
"packages/paperclip-runner/src/backends/runtime-context.ts": "cae9075fac25f168972c2be4ddb58052ef2940554458e2757d88a5d0e39805c2",
|
||||
"packages/paperclip-runner/src/backends/codex-native-backend.ts": "affecc515a623e0dfeb338f553ea53ebb7d18baa3d13e4395d17b21000dbee93",
|
||||
"packages/paperclip-runner/src/backends/opencode-native-backend.ts": "d5dc4cc2c06b37d22be47c9f15f828c06b439d8241a8d34498ca4339273eed96",
|
||||
@@ -33,6 +41,7 @@ export const NATIVE_INSTRUCTION_VARIANTS = {
|
||||
"ui/src/components/MarkdownBody.tsx": "07543156b81e51e72cfc77262f135bdb19eaa6bcccf6a3aef60c20faddcca759",
|
||||
},
|
||||
corrected: {
|
||||
...OPENCODE_FEEDBACK_BASE_FILES,
|
||||
"packages/paperclip-runner/src/backends/runtime-context.ts": "bbdab79c5b1bd57c4ddbc44edfe745b40eb694aa28a465452964109aedd6104b",
|
||||
"packages/paperclip-runner/src/backends/codex-native-backend.ts": "affecc515a623e0dfeb338f553ea53ebb7d18baa3d13e4395d17b21000dbee93",
|
||||
"packages/paperclip-runner/src/backends/opencode-native-backend.ts": "d5dc4cc2c06b37d22be47c9f15f828c06b439d8241a8d34498ca4339273eed96",
|
||||
@@ -42,6 +51,7 @@ export const NATIVE_INSTRUCTION_VARIANTS = {
|
||||
"ui/src/components/MarkdownBody.tsx": "07543156b81e51e72cfc77262f135bdb19eaa6bcccf6a3aef60c20faddcca759",
|
||||
},
|
||||
feedback: {
|
||||
...OPENCODE_FEEDBACK_BASE_FILES,
|
||||
"packages/paperclip-runner/src/backends/runtime-context.ts": "bbdab79c5b1bd57c4ddbc44edfe745b40eb694aa28a465452964109aedd6104b",
|
||||
"packages/paperclip-runner/src/backends/codex-native-backend.ts": "affecc515a623e0dfeb338f553ea53ebb7d18baa3d13e4395d17b21000dbee93",
|
||||
"packages/paperclip-runner/src/backends/opencode-native-backend.ts": "d5dc4cc2c06b37d22be47c9f15f828c06b439d8241a8d34498ca4339273eed96",
|
||||
@@ -50,12 +60,29 @@ export const NATIVE_INSTRUCTION_VARIANTS = {
|
||||
"ui/src/lib/issue-reference.ts": "ab578752acc7e185333cb2b701f6db71cedbb44675db042f78fa88417e7b2ddf",
|
||||
"ui/src/components/MarkdownBody.tsx": "f3608614b143667f7dba087be81fd1449e4eac268a203e41256d1c99691c2541",
|
||||
},
|
||||
opencodeFeedback: {
|
||||
"packages/paperclip-runner/src/backends/runtime-context.ts": "bbdab79c5b1bd57c4ddbc44edfe745b40eb694aa28a465452964109aedd6104b",
|
||||
"packages/paperclip-runner/src/backends/codex-native-backend.ts": "affecc515a623e0dfeb338f553ea53ebb7d18baa3d13e4395d17b21000dbee93",
|
||||
"packages/paperclip-runner/src/backends/opencode-native-backend.ts": "d5dc4cc2c06b37d22be47c9f15f828c06b439d8241a8d34498ca4339273eed96",
|
||||
"server/src/services/native-runtime/paperclip-runner-tool-authority.ts": "d2360cdaa63902cbf0c6a6109da452ba7e0b2a5aaa76ee5cab9c63b4bf12e584",
|
||||
"server/src/services/native-runtime/native-completion-feedback.ts": "1f9da911ec5107c6b7543bc1b4134fc74597a29af6ed40101a895045a9e26ce0",
|
||||
"ui/src/lib/issue-reference.ts": "ab578752acc7e185333cb2b701f6db71cedbb44675db042f78fa88417e7b2ddf",
|
||||
"ui/src/components/MarkdownBody.tsx": "f3608614b143667f7dba087be81fd1449e4eac268a203e41256d1c99691c2541",
|
||||
"packages/paperclip-runner/src/backends/native-backend-factory.ts": "63c8aee5548bc1b711a8b92bc348a00b6a7454d7815757380fb719988a90237f",
|
||||
"packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts": "9eb9f0aa7336011dc9a28cfec10a7e3eac568923c0f69be9771d71a51b826ba6",
|
||||
"packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts": "6f84af59e2a5e03b2eac48b094da3dfcbf213fe727350c79fbbef4cc554f03d2",
|
||||
"packages/paperclip-runner/src/cli/opencode-proxy-input.ts": "a7bf61c79047bf1aa205b32bbecc962ade4934ebac1fbbb5bcaa3a404f868ce7",
|
||||
},
|
||||
} as const;
|
||||
|
||||
// Admission explicitly binds every changed production path as well as comparison setup.
|
||||
const comparisonFiles = new Set([
|
||||
...Object.keys(NATIVE_INSTRUCTION_VARIANTS.baseline),
|
||||
"packages/paperclip-runner/src/backends/runtime-context.test.ts",
|
||||
"packages/paperclip-runner/src/backends/native-backend-factory.test.ts",
|
||||
"packages/paperclip-runner/src/cli/opencode-proxy-completion.test.ts",
|
||||
"packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts",
|
||||
"packages/paperclip-runner/test/fixtures/fake-opencode-server.mjs",
|
||||
"packages/paperclip-runner/src/backends/native-instruction-measurement.test.ts",
|
||||
"tests/runner-e2e/native-instruction-consolidation.ts",
|
||||
"tests/runner-e2e/native-instruction-consolidation.test.ts",
|
||||
|
||||
Reference in new issue
Block a user