From 4425f73c2777cf0ade15a3c59d8d3f9e283c8cd7 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sun, 6 Sep 2026 11:54:11 -0500 Subject: [PATCH] fix(runner): compose native eval instructions --- .../src/cli/eval-session-contract.test.ts | 6 ++++++ .../paperclip-runner/src/cli/eval-session.ts | 11 ++++++++++ .../src/live/live-session.test.ts | 21 +++++++++++++++++++ .../paperclip-runner/src/live/live-session.ts | 6 ++++-- .../src/live/runnerd-codex-transport.ts | 2 ++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/paperclip-runner/src/cli/eval-session-contract.test.ts b/packages/paperclip-runner/src/cli/eval-session-contract.test.ts index 9f5857cc1f..322a4fbf70 100644 --- a/packages/paperclip-runner/src/cli/eval-session-contract.test.ts +++ b/packages/paperclip-runner/src/cli/eval-session-contract.test.ts @@ -12,6 +12,7 @@ import { } from "./eval-session-contract.js"; import { boundedEvalSessionUsage, + evalRuntimeSystemInstructions, evalSessionProviderVersion, prepareEvalRuntimeContext, } from "./eval-session.js"; @@ -75,6 +76,11 @@ describe("eval-session request contract", () => { join(context.instructions.bundle.rootPath, "AGENTS.md"), "utf8", )).toContain("Paperclip direct live evaluation"); + const systemInstructions = evalRuntimeSystemInstructions(context); + expect(systemInstructions).toContain("Paperclip direct live evaluation"); + expect(systemInstructions).toContain( + `Read-only instruction sibling root: ${context.instructions.bundle.rootPath}`, + ); expect((await stat(context.instructions.bundle.rootPath)).mode & 0o777) .toBe(0o555); } finally { diff --git a/packages/paperclip-runner/src/cli/eval-session.ts b/packages/paperclip-runner/src/cli/eval-session.ts index a53fa3f7d4..220aee7adb 100644 --- a/packages/paperclip-runner/src/cli/eval-session.ts +++ b/packages/paperclip-runner/src/cli/eval-session.ts @@ -9,6 +9,7 @@ import { PAPERCLIP_EXECUTION_PROMPT, PAPERCLIP_EXECUTION_PROMPT_REVISION, canonicalNativeRuntimeContextDigest, + composeNativeSystemInstructions, nativeRuntimePromptDigest, parseNativeRuntimeContext, type NativeRuntimeContextSnapshot, @@ -141,6 +142,15 @@ export async function prepareEvalRuntimeContext( }); } +export function evalRuntimeSystemInstructions( + runtimeContext: NativeRuntimeContextSnapshot, +): string { + return composeNativeSystemInstructions( + runtimeContext, + EVAL_RUNTIME_INSTRUCTIONS, + ); +} + export function evalSessionProviderVersion( request: EvalSessionRequest, ): string | null { @@ -285,6 +295,7 @@ export async function runEvalSessionCli( transportOptions: { runnerBinary: runnerdPath, runtimeContext, + baseInstructions: evalRuntimeSystemInstructions(runtimeContext), // The transport performs the provider-specific allowlisting. Supplying // the source environment here is still required: without it the // isolated Codex home has no credential source and runnerd receives no diff --git a/packages/paperclip-runner/src/live/live-session.test.ts b/packages/paperclip-runner/src/live/live-session.test.ts index b569868318..da740a6d1a 100644 --- a/packages/paperclip-runner/src/live/live-session.test.ts +++ b/packages/paperclip-runner/src/live/live-session.test.ts @@ -662,6 +662,27 @@ describe("Capability live runnerd and Codex session", () => { await service.shutdown(session.id); }); + it("passes caller-supplied native system instructions to the provider", async () => { + const state = providerState(); + const service = new CapabilityLiveSessionService({ + transportFactory: fakeTransportFactory(state), + transportOptions: { + baseInstructions: + "Native instructions\n\nRead-only instruction sibling root: /runtime/instructions", + }, + }); + const session = await service.create(); + + expect( + state.transports[0]?.requests.find( + (request) => request.method === "thread/start", + )?.params.baseInstructions, + ).toBe( + "Native instructions\n\nRead-only instruction sibling root: /runtime/instructions", + ); + await service.shutdown(session.id); + }); + it("attributes Claude Managed sessions to the pinned immutable Agent version", async () => { const state = providerState(); const managedProfiles: Array | undefined> = []; diff --git a/packages/paperclip-runner/src/live/live-session.ts b/packages/paperclip-runner/src/live/live-session.ts index 4f7b8c0f6b..88ded8caf3 100644 --- a/packages/paperclip-runner/src/live/live-session.ts +++ b/packages/paperclip-runner/src/live/live-session.ts @@ -2382,7 +2382,8 @@ export class CapabilityLiveSession { config: createSkilllessCodexThreadConfig(this.#config.workingDirectory), permissions: CODEX_PERMISSION_PROFILE, runtimeWorkspaceRoots: [this.#config.workingDirectory], - baseInstructions: LIVE_BASE_INSTRUCTIONS, + baseInstructions: + this.#transportOptions.baseInstructions ?? LIVE_BASE_INSTRUCTIONS, persistExtendedHistory: true, }); const resumedThread = record(resumed.thread); @@ -2407,7 +2408,8 @@ export class CapabilityLiveSession { permissions: CODEX_PERMISSION_PROFILE, runtimeWorkspaceRoots: [this.#config.workingDirectory], approvalPolicy: "never", - baseInstructions: LIVE_BASE_INSTRUCTIONS, + baseInstructions: + this.#transportOptions.baseInstructions ?? LIVE_BASE_INSTRUCTIONS, completionContract: LIVE_COMPLETION_CONTRACT, dynamicTools: [ ...tools.map(dynamicToolSpec), diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts index 6802992972..a28105aa37 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts @@ -937,6 +937,8 @@ export interface CapabilityRunnerdCodexTransportOptions { opencodeProxySha256?: string; opencodeRuntimeDirectory?: string; environment?: NodeJS.ProcessEnv; + /** Provider system instructions supplied by a native execution caller. */ + baseInstructions?: string; closeGraceMs?: number; onDiagnostic?: (message: string) => void; onEvidence?: (evidence: Readonly) => void;