fix(runner): compose native eval instructions

This commit is contained in:
Dotta 2026-09-06 11:54:11 -05:00
parent 9421d65359
commit 4425f73c27
5 changed files with 44 additions and 2 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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<Record<string, unknown> | undefined> = [];

View File

@ -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),

View File

@ -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<CapabilityRunnerdProcessEvidence>) => void;