diff --git a/docs/adapters/codex-local.md b/docs/adapters/codex-local.md index ad187f757d..ff30263b94 100644 --- a/docs/adapters/codex-local.md +++ b/docs/adapters/codex-local.md @@ -40,6 +40,12 @@ pnpm paperclipai agent local-cli codexcoder --company-id This installs any missing skills, creates an agent API key, and prints shell exports to run as that agent. +## Instructions Resolution + +If `instructionsFilePath` is configured, Paperclip reads that file and prepends it to the stdin prompt sent to `codex exec` on every run. + +This is separate from any workspace-level instruction discovery that Codex itself performs in the run `cwd`. Paperclip does not disable Codex-native repo instruction files, so a repo-local `AGENTS.md` may still be loaded by Codex in addition to the Paperclip-managed agent instructions. + ## Environment Test The environment test checks: diff --git a/packages/adapters/codex-local/src/index.ts b/packages/adapters/codex-local/src/index.ts index 0d881a2bf3..58511eb623 100644 --- a/packages/adapters/codex-local/src/index.ts +++ b/packages/adapters/codex-local/src/index.ts @@ -40,6 +40,8 @@ Operational fields: Notes: - Prompts are piped via stdin (Codex receives "-" prompt argument). +- If instructionsFilePath is configured, Paperclip prepends that file's contents to the stdin prompt on every run. +- Codex exec automatically applies repo-scoped AGENTS.md instructions from the active workspace. Paperclip cannot suppress that discovery in exec mode, so repo AGENTS.md files may still apply even when you only configured an explicit instructionsFilePath. - Paperclip injects desired local skills into the active workspace's ".agents/skills" directory at execution time so Codex can discover "$paperclip" and related skills without coupling them to the user's login home. - Unless explicitly overridden in adapter config, Paperclip runs Codex with a per-company managed CODEX_HOME under the active Paperclip instance and seeds auth/config from the shared Codex home (the CODEX_HOME env var, when set, or ~/.codex). - Some model/tool combinations reject certain effort levels (for example minimal with web search enabled). diff --git a/packages/adapters/codex-local/src/server/execute.ts b/packages/adapters/codex-local/src/server/execute.ts index b6bda8dfa7..eaf3d1b527 100644 --- a/packages/adapters/codex-local/src/server/execute.ts +++ b/packages/adapters/codex-local/src/server/execute.ts @@ -427,16 +427,22 @@ export async function execute(ctx: AdapterExecutionContext): Promise { - if (!instructionsFilePath) return [] as string[]; + if (!instructionsFilePath) { + return [repoAgentsNote]; + } if (instructionsPrefix.length > 0) { return [ `Loaded agent instructions from ${instructionsFilePath}`, `Prepended instructions + path directive to stdin prompt (relative references from ${instructionsDir}).`, + repoAgentsNote, ]; } return [ `Configured instructionsFilePath ${instructionsFilePath}, but file could not be read; continuing without injected instructions.`, + repoAgentsNote, ]; })(); const bootstrapPromptTemplate = asString(config.bootstrapPromptTemplate, ""); diff --git a/server/src/__tests__/codex-local-execute.test.ts b/server/src/__tests__/codex-local-execute.test.ts index 3f1f15dfdd..9d386397a8 100644 --- a/server/src/__tests__/codex-local-execute.test.ts +++ b/server/src/__tests__/codex-local-execute.test.ts @@ -139,6 +139,62 @@ describe("codex execute", () => { } }); + it("emits a command note that Codex auto-applies repo-scoped AGENTS.md files", async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-execute-notes-")); + const workspace = path.join(root, "workspace"); + const commandPath = path.join(root, "codex"); + const capturePath = path.join(root, "capture.json"); + await fs.mkdir(workspace, { recursive: true }); + await writeFakeCodexCommand(commandPath); + + const previousHome = process.env.HOME; + process.env.HOME = root; + + let commandNotes: string[] = []; + try { + const result = await execute({ + runId: "run-notes", + agent: { + id: "agent-1", + companyId: "company-1", + name: "Codex Coder", + adapterType: "codex_local", + adapterConfig: {}, + }, + runtime: { + sessionId: null, + sessionParams: null, + sessionDisplayId: null, + taskKey: null, + }, + config: { + command: commandPath, + cwd: workspace, + env: { + PAPERCLIP_TEST_CAPTURE_PATH: capturePath, + }, + promptTemplate: "Follow the paperclip heartbeat.", + }, + context: {}, + authToken: "run-jwt-token", + onLog: async () => {}, + onMeta: async (meta) => { + commandNotes = Array.isArray(meta.commandNotes) ? meta.commandNotes : []; + }, + }); + + expect(result.exitCode).toBe(0); + expect(result.errorMessage).toBeNull(); + expect(commandNotes).toContain( + "Codex exec automatically applies repo-scoped AGENTS.md instructions from the current workspace; Paperclip does not currently suppress that discovery.", + ); + } finally { + if (previousHome === undefined) delete process.env.HOME; + else process.env.HOME = previousHome; + await fs.rm(root, { recursive: true, force: true }); + } + }); + it("uses a worktree-isolated CODEX_HOME while preserving shared auth and config", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-execute-")); const workspace = path.join(root, "workspace"); diff --git a/skills/paperclip/SKILL.md b/skills/paperclip/SKILL.md index ee5ac2aea9..407f08da8a 100644 --- a/skills/paperclip/SKILL.md +++ b/skills/paperclip/SKILL.md @@ -330,7 +330,7 @@ Use this when validating Paperclip itself (assignment flow, checkouts, run visib 1. Create a throwaway issue assigned to a known local agent (`claudecoder` or `codexcoder`): ```bash -pnpm paperclipai issue create \ +npx paperclipai issue create \ --company-id "$PAPERCLIP_COMPANY_ID" \ --title "Self-test: assignment/watch flow" \ --description "Temporary validation issue" \ @@ -341,19 +341,19 @@ pnpm paperclipai issue create \ 2. Trigger and watch a heartbeat for that assignee: ```bash -pnpm paperclipai heartbeat run --agent-id "$PAPERCLIP_AGENT_ID" +npx paperclipai heartbeat run --agent-id "$PAPERCLIP_AGENT_ID" ``` 3. Verify the issue transitions (`todo -> in_progress -> done` or `blocked`) and that comments are posted: ```bash -pnpm paperclipai issue get +npx paperclipai issue get ``` 4. Reassignment test (optional): move the same issue between `claudecoder` and `codexcoder` and confirm wake/run behavior: ```bash -pnpm paperclipai issue update --assignee-agent-id --status todo +npx paperclipai issue update --assignee-agent-id --status todo ``` 5. Cleanup: mark temporary issues done/cancelled with a clear note. diff --git a/ui/src/adapters/codex-local/config-fields.tsx b/ui/src/adapters/codex-local/config-fields.tsx index 125630bae3..86bef6009b 100644 --- a/ui/src/adapters/codex-local/config-fields.tsx +++ b/ui/src/adapters/codex-local/config-fields.tsx @@ -11,7 +11,7 @@ import { LocalWorkspaceRuntimeFields } from "../local-workspace-runtime-fields"; const inputClass = "w-full rounded-md border border-border px-2.5 py-1.5 bg-transparent outline-none text-sm font-mono placeholder:text-muted-foreground/40"; const instructionsFileHint = - "Absolute path to a markdown file (e.g. AGENTS.md) that defines this agent's behavior. Injected into the system prompt at runtime."; + "Absolute path to a markdown file (e.g. AGENTS.md) that defines this agent's behavior. Injected into the system prompt at runtime. Note: Codex may still auto-apply repo-scoped AGENTS.md files from the workspace."; export function CodexLocalConfigFields({ mode,