diff --git a/doc/plans/2026-10-02-stock-harness-paperclip-checklist.md b/doc/plans/2026-10-02-stock-harness-paperclip-checklist.md new file mode 100644 index 0000000000..f71067ff07 --- /dev/null +++ b/doc/plans/2026-10-02-stock-harness-paperclip-checklist.md @@ -0,0 +1,219 @@ +# Stock harness, with Paperclip: working checklist + +Created: 2026-10-02. Status: item 1 implemented for native Codex app-server; +PR validation in progress. Other implementation items remain open. + +Goal: keep the agent's stock harness behavior and add only what it needs to work +with Paperclip. Apply this across legacy adapters, the new Runner, and their +configuration variants. + +We will work through the numbered items one at a time with Dotta. For each item, +record the proposed behavior and Dotta's direction, make a bounded change, and +verify the affected paths before moving on. New findings get stable IDs in the +ledger below so they do not disappear into conversation history. + +**Current item: 1 — validate and review the native Codex instruction fix.** + +## Agreed direction and boundaries + +- Preserve vendor base instructions. Paperclip context should be additive where + the harness supports it. +- Reduce the default hire operating manual and review every role/team template + for reduction or removal. Prefer a tiny default, potentially one paragraph. +- Keep a minimal coordination boundary; load uncommon procedures on demand. +- Move bookkeeping into the runtime where it can own the operation reliably. +- Check capability and configuration behavior across all harness paths. +- Paperclip owns MCP configuration. Missing personal integrations are acceptable + and intentional; restoring them is not a goal of this work. +- Configuration changes and configuration-driven session resets are acceptable. +- Preserve Paperclip authentication, assigned skills, workspace access, tool + authorization, company boundaries, checkout, approvals, budget hard stops, + pause/cancel behavior, audit records, and usable task/artifact delivery. +- Restore appropriate repository instruction context without accidentally + importing unrelated host configuration or displacing Paperclip-owned MCPs. +- Decide explicitly how changed defaults affect existing agents and sessions; + do not assume existing custom instructions should be rewritten. + +## 1. Preserve the native Codex base instructions + +- [x] Trace every instruction path: backend composition, TypeScript driver, + runnerd bridge, and Rust provider; include fresh threads, resume, and recovery. +- [x] Record the chosen additive mechanism and the minimum Paperclip context it + needs to carry. Include fallback/direct execution paths. +- [x] Remove default replacement of stock base instructions across those paths. +- [x] Verify the actual app-server request and retained session instructions, + including resume; checking only a prompt builder is insufficient. +- [ ] Verify Paperclip task context, tools, auth, assigned skills, and completion + still work. Record applicable regressions and eval results. + +Starting points: [Codex backend](../../packages/paperclip-runner/src/backends/codex-native-backend.ts), +[app-server driver](../../packages/paperclip-runner/src/drivers/codex/codex-app-server-driver-impl.ts), +[runnerd transport](../../packages/paperclip-runner/src/live/runnerd-codex-transport.ts), +[Rust provider](../../packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs). + +Decision: use additive `developerInstructions` on Codex start and resume in the +TypeScript driver, Runner Lab/eval sessions, and Rust provider. Retain existing +instruction fields for other provider facades. Keep the Paperclip fragment and +its historical option/trace names unchanged in this bounded fix. + +Transition: pre-change Codex threads retain their saved replacement base prompt +and need a provider session reset. Do not reset active sessions automatically. +The separate Codex-through-ACP dependency patch remains a coverage follow-up +under item 6; this change covers the native app-server path. + +## 2. Reduce the default operating manual and shared prompt layers + +- [ ] Inventory what an agent actually receives: hire instructions, shared + prompt template, wake context, runtime prompt, bootstrap, and loaded skills. + Separate always-present text from content loaded on demand. +- [ ] Agree on the tiny common contract and the coordination details that each + runtime still requires. Legacy API coordination and native semantic tools + need appropriate instructions for their respective interfaces. +- [ ] Remove repeated workflow rules and stock coding/style/autonomy guidance. +- [ ] Move detailed planning, hiring, artifacts, and exceptional procedures to + discoverable references or tools where feasible. +- [ ] Check fresh and resumed task/chat flows for instruction duplication and + contradictory completion or waiting rules. + +Starting points: [default hire instructions](../../server/src/onboarding-assets/default/AGENTS.md), +[shared adapter utilities](../../packages/adapter-utils/src/server-utils.ts), +[native runtime contract](../../packages/paperclip-runner/src/contracts/runtime-context.ts), +[Paperclip operational skill](../../skills/paperclip/SKILL.md). + +Decision: exact retained paragraph and on-demand boundaries pending. + +## 3. Review every hiring and role template + +- [ ] Cover default hires, CEO, chief of staff, coder, QA, UX, security, CTO, and + every other shipped team/role instruction set. +- [ ] Trace onboarding, API/UI/CLI hiring, team imports, and agent-creation skill + rules so a removed manual is not regenerated through another entry point. +- [ ] For each template, decide: remove it, retain a tiny role description, or + keep specific domain guidance with a stated reason. +- [ ] Review forced delegation, mandatory memory workflows, procedural review + routing, comment requirements, and old governance instructions. +- [ ] Update hiring references and draft-review requirements alongside templates. + Native Runner agents must not be required to follow legacy skill/API procedures + that their runtime intentionally does not expose. +- [ ] Decide rollout for new hires, existing managed bundles, imported teams, + and custom agent instructions. +- [ ] If substantial behavioral instructions remain, identify the behavior they + should improve and add appropriate eval coverage. A tiny role paragraph may + need only creation/configuration coverage. + +Starting points: [onboarding assets](../../server/src/onboarding-assets/), +[default bundle service](../../server/src/services/default-agent-instructions.ts), +[agent routes](../../server/src/routes/agents.ts), +[hiring skill and references](../../skills/paperclip-create-agent/), +[teams catalog](../../packages/teams-catalog/catalog/). + +Decision: per-template disposition and migration policy pending. + +## 4. Fix repository context while retaining Paperclip configuration + +- [ ] Map instruction discovery and precedence for each harness: repository + AGENTS.md/CLAUDE.md or equivalent, project/local settings, isolated homes, and + explicit Paperclip instruction injection. +- [ ] Distinguish repository instructions from settings that also load MCPs, + plugins, credentials, or skills; choose selective loading/injection as needed. +- [ ] Verify repository instructions are available in the correct workspace, + including worktrees, remote execution, and resumed sessions. +- [ ] Verify assigned skill discovery/pinning, authentication, Paperclip MCP + ownership, tool authorization, and configuration-change invalidation. +- [ ] Record intentional isolation separately from accidental lost context. + +Claude starting points: [local adapter](../../packages/adapters/claude-local/src/server/execute.ts), +[ACP patch](../../patches/@agentclientprotocol__claude-agent-acp@0.73.0.patch), +[runtime sandbox](../../packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts). + +Decision: repository-context loading mechanism per harness pending. Personal MCP +isolation is approved and should remain. + +## 5. Let the runtime own bookkeeping + +- [ ] Map which runtime owns checkout, status transitions, completion comments, + artifacts, waiting/review states, and recovery; identify manual duplicates. +- [ ] Keep one valid completion path per runtime and preserve meaningful user + questions, approval requests, dependency waits, and final deliverables. +- [ ] Remove agent instructions for operations the runtime already performs; + retain necessary coordination for legacy/external adapters. +- [ ] Verify durable task state, visible final answer, artifact access, audit, + and restart/recovery behavior through the real product paths. + +Decision: exact runtime responsibilities and legacy compatibility pending. + +## 6. Complete the harness and configuration coverage audit + +This matrix is an inventory seed, not a claim that every path has been audited +or qualified. Expand it from the registry and provider/profile definitions. +For every numbered change, record applicability here or an explicit reason it +does not apply. + +| Execution family | Paths to cover | Audit status | +| --- | --- | --- | +| Legacy Codex / Claude | Local adapters, managed auth and isolated configuration | Initial inspection only | +| Other legacy local adapters | ACPX, OpenCode, Pi, Cursor, Gemini, Grok, Kimi, Hermes | Pending | +| Other adapter transports | Cursor Cloud, Hermes/OpenClaw gateways, process, HTTP, external adapter plugins | Pending | +| Runner Codex | App-server driver, runnerd bridge, Rust provider, direct/fallback paths | Additive instruction fix implemented; PR validation pending | +| Runner ACPX | Enabled profiles, especially Claude/Grok; declared or pending profiles tracked separately | Claude initial inspection; remaining audit pending | +| Runner OpenCode | Native provider and configuration paths | Pending | +| Hosted/remote providers | Claude Managed and AWS AgentCore; identify their own baseline rather than assuming CLI semantics | Pending | + +- [ ] Inventory supported profiles and qualification status from the + [adapter registry](../../server/src/adapters/registry.ts) and + [provider resolver](../../server/src/services/native-runtime/provider-profile.ts). +- [ ] Cover local/remote environments, fresh/resumed sessions, managed/custom + hires, task/chat/planning flows, and auth/configuration variants as applicable. +- [ ] Audit restrictions on tools, skills, subagents, memory, and other harness + capabilities. Trace effective provider configuration, not just intermediate + configuration objects; document intentional limits and decide accidental ones. +- [ ] Check shared fixes reach every relevant adapter; document provider-specific + exceptions instead of silently extending a Codex/Claude assumption. + +## Verification and evals — apply to each item + +- [ ] Map existing coverage before adding cases; use [doc/evals.md](../evals.md). + Keep Runner protocol evals and Product E2E evals distinct. +- [ ] Start with narrow deterministic checks for instruction layering, effective + configuration, skill/auth delivery, and session behavior where appropriate. +- [ ] Use Runner evals for provider/session/tool protocol changes; use Product + E2E for real hiring, repository context, task lifecycle, and artifact delivery. +- [ ] Review existing context-integrity, hiring, completion-updates, and blocker + suites for reusable coverage; record gaps rather than claiming coverage from + a similarly named case. +- [ ] Compare task quality as well as Paperclip protocol compliance when claiming + that fewer instructions improve agent performance. Keep model, effort, + permissions, tools, and fixture comparable. +- [ ] Select narrow live cells when implementation is ready; retain revisions, + profile/environment, grader, retries, usage/cost, and failure classification. +- [ ] Run the relevant checks for each change and the repository's required full + verification before a PR-ready handoff. Record unrun checks and their reasons. + +## Findings ledger + +Confirmed mechanics below do not by themselves establish an effect on task quality. + +| ID | Finding | Work item / disposition | +| --- | --- | --- | +| F1 | Native Codex sent Paperclip text as `baseInstructions`. Probes on codex-cli 0.153.4 showed replacement; additive `developerInstructions` retained the stock base on start and cold resume. | 1; app-server fix implemented, PR validation pending | +| F2 | Default hires and role templates prescribe substantial operating procedures; common prompt and wake layers add further coordination text. | 2–3; mechanics confirmed, performance effect unmeasured | +| F3 | Hiring references require legacy Paperclip skill/comment procedures, while native Runner intentionally omits that operational skill and uses semantic tools. | 2–3, 5; reconcile runtime contracts | +| F4 | Local Claude appends instructions; Runner Claude preserves the Claude Code preset. Runner isolation excludes project/local settings, which can also exclude repository instruction discovery. | 4; selective context fix to design | +| F5 | Some Codex capability settings differ between the direct driver and daemon path; an intermediate configuration does not prove the final provider behavior. | 6; effective-path audit pending | +| F6 | Omitting or nulling `baseInstructions` on an old Codex thread's resume preserves its saved replacement; an empty string produces an empty base. | 1; document the required provider session reset; no automatic migration in this PR | +| F7 | The isolated Codex-through-ACP dependency patch also sets `baseInstructions` on start/resume. It is a separate path from the native app-server backend. | 6; follow-up patch/profile audit pending | + +Append new findings with evidence, affected paths, and the numbered item that +will address them. Record intentional behavior explicitly rather than as a bug. + +## Decision and completion log + +| Date | Decision / outcome | Evidence / follow-up | +| --- | --- | --- | +| 2026-10-02 | Dotta approved preserving stock instructions, smaller defaults/templates, minimal coordination, runtime bookkeeping, and coverage across harnesses. | Implementation details to work through one item at a time. | +| 2026-10-02 | Paperclip-owned MCP isolation and configuration changes/session resets are acceptable. | Preserve Paperclip auth and assigned skills while fixing repository context. | +| 2026-10-02 | Dotta requested implementation and a PR for item 1. Native app-server paths now use additive developer instructions. | 139 targeted TypeScript tests and 91 Rust provider tests passed; repository typecheck/build passed. Remaining test/review results to record. | +| 2026-10-02 | Verified actual Codex instruction layering using a localhost Responses stub, without paid inference. | codex-cli 0.153.4 sent identical 14,732-character stock base instructions on start and cold resume, with the Paperclip marker retained in developer input. This is protocol evidence, not a task-quality eval. | + +For each completed item, add the chosen behavior, changed paths, verification +results, remaining exceptions, and follow-ups here before checking it off. diff --git a/packages/paperclip-runner/docs/tutorials/codex.md b/packages/paperclip-runner/docs/tutorials/codex.md index 47b57433d7..e4c0d1126d 100644 --- a/packages/paperclip-runner/docs/tutorials/codex.md +++ b/packages/paperclip-runner/docs/tutorials/codex.md @@ -73,6 +73,17 @@ test "$(cat "$codex_workspace/hello.txt")" = "hello from Codex runner" ## Step 3: Inspect the exact model boundary +Paperclip sends its runtime instructions as additive `developerInstructions` +on Codex thread start and resume. Codex retains its stock base instructions. +The historical `context.baseInstructions` trace field contains the Paperclip +fragment, not the full Codex base prompt. The driver option with the same name +also supplies this additive fragment. + +Threads created before this change retain their saved replacement base prompt +when resumed. Reset those provider sessions to apply the stock base instructions; +adding developer instructions does not repair an already saved base prompt. +This change does not alter session recovery or reset active sessions automatically. + ```sh jq '.context | { protocolVersion, diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs b/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs index 437e0ea01a..95bc8579d5 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs @@ -1038,12 +1038,19 @@ impl CodexProvider { "model": config.model, "approvalPolicy": config.approval_policy, "runtimeWorkspaceRoots": [config.cwd], - "baseInstructions": config.instructions, "dynamicTools": dynamic_tools, }); let params_object = params .as_object_mut() .expect("Codex thread parameters are an object"); + // Codex's baseInstructions replaces its stock prompt. OpenCode + // uses the same protocol facade but keeps its existing contract. + let instruction_field = if config.provider == "codex" { + "developerInstructions" + } else { + "baseInstructions" + }; + params_object.insert(instruction_field.to_owned(), json!(config.instructions)); if provider.permission_profile == "paperclip-runner-external-sandbox" { // The execution target (for example Daytona) is the OS sandbox. // Codex must not try to create nested user/network namespaces, diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs index dac280fdf3..601fb38394 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs @@ -6290,6 +6290,34 @@ fn skill_wire_requests(directory: &Path) -> Vec { .collect() } +#[test] +fn runtime_instructions_are_additive_for_codex_on_start_and_resume() { + let directory = temporary_directory("instruction-channel-wire"); + let log = directory.join("requests.ndjson"); + let config = provider_config(&directory, &["--request-log", log.to_str().unwrap()]); + let mut provider = CodexProvider::start(&config, None).unwrap(); + let thread_id = provider.thread_id().to_owned(); + provider.shutdown().unwrap(); + let mut resumed = CodexProvider::start(&config, Some(&thread_id)).unwrap(); + resumed.shutdown().unwrap(); + let frames = skill_wire_requests(&directory); + for method in ["thread/start", "thread/resume"] { + let frame = frames + .iter() + .find(|frame| frame["method"] == method) + .unwrap(); + assert_eq!( + frame["params"]["developerInstructions"], config.instructions, + "{method}" + ); + assert!( + frame["params"].get("baseInstructions").is_none(), + "{method}" + ); + } + fs::remove_dir_all(directory).unwrap(); +} + #[test] fn skill_instructions_flag_reaches_start_and_resume_and_preserves_absent_config() { for flag in [Some(true), Some(false), None] { diff --git a/packages/paperclip-runner/src/contracts/codex.ts b/packages/paperclip-runner/src/contracts/codex.ts index 605fd29d52..b8db8eb86b 100644 --- a/packages/paperclip-runner/src/contracts/codex.ts +++ b/packages/paperclip-runner/src/contracts/codex.ts @@ -50,6 +50,8 @@ export interface CodexModelContextSnapshot { collaborationMode: "default" | "plan"; sandbox: unknown; approvalPolicy: unknown; + /** Historical trace field for the Paperclip instruction fragment, supplied + * to Codex as developerInstructions rather than replacing its stock base. */ baseInstructions: string; instructionSources: string[]; instructionPolicy: { diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver-impl.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver-impl.ts index da221eff7f..62151df5ea 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver-impl.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver-impl.ts @@ -199,6 +199,14 @@ export class CodexAppServerDriver implements HarnessDriver { return this.#options.baseInstructions ?? CODEX_SKILLLESS_BASE_INSTRUCTIONS; } + #instructionParams(instructions = this.#baseInstructions()): Record { + // baseInstructions replaces Codex's stock prompt. Other providers use this + // driver as a protocol facade and retain their existing instruction field. + return (this.#options.driverIdentity?.kind ?? DRIVER_KIND) === DRIVER_KIND + ? { developerInstructions: instructions } + : { baseInstructions: instructions }; + } + async descriptor(): Promise { const unsupported = Object.entries(this.#caps) .filter(([, supported]) => !supported) @@ -280,7 +288,7 @@ export class CodexAppServerDriver implements HarnessDriver { ...(this.#direct() ? {} : { - baseInstructions: this.#baseInstructions(), + ...this.#instructionParams(), completionContract: { revision: this.#options.taskEnvelope.completionContract.revision, @@ -408,7 +416,7 @@ export class CodexAppServerDriver implements HarnessDriver { this.#options.includeSkillInstructions ?? false, this.#options.environment, ), - baseInstructions: this.#direct() ? "" : this.#baseInstructions(), + ...this.#instructionParams(this.#direct() ? "" : this.#baseInstructions()), approvalPolicy: this.#options.approvalPolicy ?? "never", ...(this.#options.model ? { model: this.#options.model } : {}), dynamicTools: this.#providerDynamicTools(), diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts index 91ec041ee1..81e8a3def9 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.lifecycle.test.ts @@ -477,7 +477,7 @@ describe("Codex app-server Codex driver", () => { }); }); - it("places Paperclip runtime instructions in Codex's system channel and enables only selected skill instructions", async () => { + it("adds Paperclip developer instructions without replacing the Codex base and enables selected skills", async () => { const transport = new FakeCodexTransport(); const baseInstructions = [ "You are running as a Paperclip agent.", @@ -499,17 +499,68 @@ describe("Codex app-server Codex driver", () => { (call) => call.method === "thread/start", ); expect(threadStart?.params).toMatchObject({ - baseInstructions, + developerInstructions: baseInstructions, config: { "skills.include_instructions": true, include_apps_instructions: false, }, }); + expect(threadStart?.params).not.toHaveProperty("baseInstructions"); expect(JSON.stringify(threadStart?.params.input ?? null)).not.toContain( baseInstructions, ); }); + it.each(["task", "prepared", "direct"] as const)("preserves stock Codex instructions on %s recovery", async (conversationMode) => { + const originalTransport = new FakeCodexTransport(); + const recoveryTransport = new FakeCodexTransport(); + const baseInstructions = "Paperclip coordination and assigned instruction paths."; + const driver = makeDriver([originalTransport, recoveryTransport], { + conversationMode, + baseInstructions, + includeSkillInstructions: true, + }); + const original = await driver.openSession({ + runId: "run-additive-recovery", + normalizedSessionId: "normalized-additive-recovery", + workingDirectory: WORKSPACE, + }); + const snapshot = await original.snapshot(); + recoveryTransport.readResponse = { + thread: { id: snapshot.driverSessionId, sessionId: snapshot.providerSessionId, cwd: WORKSPACE, turns: [] }, + }; + await original.close({ reason: "verify additive recovery" }); + + const recovered = await driver.recoverSession(snapshot); + expect(recovered.recovered).toBe(true); + const started = originalTransport.calls.find((call) => call.method === "thread/start")!.params; + const resumed = recoveryTransport.calls.find((call) => call.method === "thread/resume")!.params; + expect(started).not.toHaveProperty("baseInstructions"); + expect(resumed).not.toHaveProperty("baseInstructions"); + expect(started.developerInstructions).toBe(conversationMode === "direct" ? undefined : baseInstructions); + expect(resumed.developerInstructions).toBe(conversationMode === "direct" ? "" : baseInstructions); + expect(resumed.dynamicTools).toEqual(started.dynamicTools); + expect(resumed.config).toEqual(started.config); + await recovered.session?.close({ reason: "verified additive recovery" }); + }); + + it("retains the instruction field for non-Codex provider facades", async () => { + const transport = new FakeCodexTransport(); + const driver = makeDriver([transport], { + baseInstructions: "Provider-specific runtime context.", + driverIdentity: { kind: "opencode_server", displayName: "OpenCode", version: "1" }, + }); + const session = await driver.openSession({ + runId: "run-facade-instructions", + normalizedSessionId: "normalized-facade-instructions", + workingDirectory: WORKSPACE, + }); + const started = transport.calls.find((call) => call.method === "thread/start")!.params; + expect(started.baseInstructions).toBe("Provider-specific runtime context."); + expect(started).not.toHaveProperty("developerInstructions"); + await session.close({ reason: "verified facade instructions" }); + }); + it("passes the common typed-event contract and reports one provider turn terminal", async () => { const transport = new FakeCodexTransport(); const driver = makeDriver([transport]); diff --git a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.test.ts b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.test.ts index 3f68152e45..2f667d72ee 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-app-server-driver.test.ts @@ -572,7 +572,7 @@ describe("Codex app-server Codex driver", () => { }); }); - it("places Paperclip runtime instructions in Codex's system channel and enables only selected skill instructions", async () => { + it("adds Paperclip developer instructions without replacing the Codex base and enables selected skills", async () => { const transport = new FakeCodexTransport(); const baseInstructions = [ "You are running as a Paperclip agent.", @@ -592,12 +592,13 @@ describe("Codex app-server Codex driver", () => { const threadStart = transport.calls.find((call) => call.method === "thread/start"); expect(threadStart?.params).toMatchObject({ - baseInstructions, + developerInstructions: baseInstructions, config: { "skills.include_instructions": true, include_apps_instructions: false, }, }); + expect(threadStart?.params).not.toHaveProperty("baseInstructions"); expect(JSON.stringify(threadStart?.params.input ?? null)).not.toContain(baseInstructions); }); diff --git a/packages/paperclip-runner/src/drivers/codex/codex-driver-types.ts b/packages/paperclip-runner/src/drivers/codex/codex-driver-types.ts index 77303d77be..33b56c880a 100644 --- a/packages/paperclip-runner/src/drivers/codex/codex-driver-types.ts +++ b/packages/paperclip-runner/src/drivers/codex/codex-driver-types.ts @@ -20,6 +20,8 @@ export interface CodexAppServerDriverOptions { /** Per-run reasoning effort sent with each Codex turn, including resumed turns. */ reasoningEffort?: string; approvalPolicy?: "never" | "on-request" | "untrusted"; + /** Paperclip runtime instructions. Codex receives these as additive developer + * instructions; the historical option name remains compatible with callers. */ baseInstructions?: string; includeSkillInstructions?: boolean; /** Private instruction directory registered by the control plane for this run. */ diff --git a/packages/paperclip-runner/src/live/live-session.test.ts b/packages/paperclip-runner/src/live/live-session.test.ts index 8eef8f295f..938479a0da 100644 --- a/packages/paperclip-runner/src/live/live-session.test.ts +++ b/packages/paperclip-runner/src/live/live-session.test.ts @@ -918,8 +918,9 @@ describe("Capability live runnerd and Codex session", () => { expect(names.filter((name) => name === "paperclip_finish")).toHaveLength(1); expect(names.filter((name) => name === "paperclip_block")).toHaveLength(1); expect(names).not.toContain("create_task"); - expect(opened.params.baseInstructions).toContain('"revision":"paperclip-capability-live-v1"'); - expect(opened.params.baseInstructions).toContain('"criterionIds":["objective"]'); + expect(opened.params).not.toHaveProperty("baseInstructions"); + expect(opened.params.developerInstructions).toContain('"revision":"paperclip-capability-live-v1"'); + expect(opened.params.developerInstructions).toContain('"criterionIds":["objective"]'); await first.suspend(); const restoredService = new CapabilityLiveSessionService({ store, transportFactory: factory }); const restored = await restoredService.restore(first.id); @@ -928,7 +929,8 @@ describe("Capability live runnerd and Codex session", () => { names.filter((name) => name !== "paperclip_finish" && name !== "paperclip_block"), ); const resumed = state.transports[1]!.requests.find((request) => request.method === "thread/resume")!; - expect(resumed.params.baseInstructions).toBe(opened.params.baseInstructions); + expect(resumed.params).not.toHaveProperty("baseInstructions"); + expect(resumed.params.developerInstructions).toBe(opened.params.developerInstructions); await restoredService.shutdown(restored.id); await firstService.shutdown(first.id); }); @@ -989,11 +991,11 @@ describe("Capability live runnerd and Codex session", () => { expect( state.transports[0]?.requests.find( (request) => request.method === "thread/start", - )?.params.baseInstructions, + )?.params.developerInstructions, ).toContain( "Native instructions\n\nRead-only instruction sibling root: /runtime/instructions", ); - expect(state.transports[0]?.requests.find((request) => request.method === "thread/start")?.params.baseInstructions) + expect(state.transports[0]?.requests.find((request) => request.method === "thread/start")?.params.developerInstructions) .toContain('Native completion report contract: {"revision":"paperclip-capability-live-v1","criterionIds":["objective"]}'); await service.shutdown(session.id); }); diff --git a/packages/paperclip-runner/src/live/live-session.ts b/packages/paperclip-runner/src/live/live-session.ts index d24995b5ec..28ec49d172 100644 --- a/packages/paperclip-runner/src/live/live-session.ts +++ b/packages/paperclip-runner/src/live/live-session.ts @@ -2479,7 +2479,7 @@ export class CapabilityLiveSession { config: createSkilllessCodexThreadConfig(this.#config.workingDirectory), permissions: CODEX_PERMISSION_PROFILE, runtimeWorkspaceRoots: [this.#config.workingDirectory], - baseInstructions, + ...(provider === "codex" ? { developerInstructions: baseInstructions } : { baseInstructions }), persistExtendedHistory: true, }); const resumedThread = record(resumed.thread); @@ -2504,7 +2504,7 @@ export class CapabilityLiveSession { permissions: CODEX_PERMISSION_PROFILE, runtimeWorkspaceRoots: [this.#config.workingDirectory], approvalPolicy: "never", - baseInstructions, + ...(provider === "codex" ? { developerInstructions: baseInstructions } : { baseInstructions }), completionContract: LIVE_COMPLETION_CONTRACT, dynamicTools: [...semanticTools, ...codexSemanticToolSpecs()], experimentalRawEvents: true, diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts index 94c744c7e5..8ab547d221 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts @@ -4247,11 +4247,14 @@ it("captures exact provider frames and correlates Rust and TypeScript interpreta decodedFrames.find((frame) => frame.method === "thread/start"), ).toMatchObject({ params: { - baseInstructions: withCodexCollaborationRuntimeInstructions( + developerInstructions: withCodexCollaborationRuntimeInstructions( CODEX_SKILLLESS_BASE_INSTRUCTIONS, ), }, }); + expect( + (decodedFrames.find((frame) => frame.method === "thread/start")?.params as Record), + ).not.toHaveProperty("baseInstructions"); const stages = new Set( [...nativeEntries, ...rehydratedEntries] .filter((entry) => entry.kind === "interpretation") diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts index 1a49999f21..c067f2a541 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts @@ -4544,7 +4544,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { provider === "codex" && record(params.config).include_collaboration_mode_instructions !== false; const unboundBaseInstructions = String( - params.baseInstructions ?? "You are a Paperclip agent.", + params.developerInstructions ?? params.baseInstructions ?? "You are a Paperclip agent.", ); const baseInstructions = sourceRuntimeContext && runtimeContext