Skip to content

feat: add Codex agent adapter (agent_provider: claude | codex) - #21

Merged
niranjan94 merged 14 commits into
release/v2from
feat/codex-adapter
Jun 12, 2026
Merged

feat: add Codex agent adapter (agent_provider: claude | codex)#21
niranjan94 merged 14 commits into
release/v2from
feat/codex-adapter

Conversation

@niranjan94

Copy link
Copy Markdown
Owner

Summary

Adds a second AgentAdapter so Shopfloor stages can run through OpenAI's Codex (@openai/codex-sdk) instead of Claude. Selection is global via a new agent_provider: claude | codex input (defaults to claude, no behavior change for existing consumers). Everything downstream of the adapter interface is untouched — orchestrator, StageContext, stage runners, and the state machine depend only on the AgentAdapter contract and the AgentError taxonomy.

Implements docs/superpowers/specs/2026-06-11-codex-adapter-design.md.

  • CodexAgentAdapter (src/agents/codex.ts) — wraps @openai/codex-sdk with the same structured-output and AgentError semantics as the Claude adapter. System prompt is prepended to the user prompt (Codex has no separate system-prompt field). budgetUsd/maxTurns are accepted and dropped with a warning (Codex surfaces neither).
  • In-process MCP bridge (src/agents/mcp-http-bridge.ts) — exposes the implement stage's single update_progress tool to the Codex CLI subprocess via a Streamable HTTP MCP server bound to loopback on an ephemeral port, guarded by a random bearer token, reusing the live in-memory Octokit. Runs in stateful mode (session-id handshake) because the SDK transport refuses more than one request per stateless instance.
  • SdkTool.inputSchema retyped from unknown to z.ZodRawShape; update-progress.ts no longer depends on a provider SDK's tool() helper so it stays neutral.
  • Auth (src/config/codex-options.ts) — openai_api_key (recommended) or a ChatGPT codex_auth_json seeded into a run-scoped temp CODEX_HOME/auth.json (0600) each run.
  • CLI resolution (src/setup/ensure-codex-cli.ts) — locates/installs the native codex binary, mirroring ensure-claude-cli.ts.
  • New inputs, entry wiring, example workflow (examples/shopfloor-codex.yml), README/CLAUDE.md docs.

Test Plan

  • pnpm typecheck clean
  • pnpm test — 276/276 pass (Codex adapter option-mapping + error-kind mapping + abort→timeout + budget/turns warning; MCP bridge tool call over MCP + bearer rejection; buildCodexOptions auth branches incl. 0600 auth.json + throw)
  • pnpm build reproducible (dist committed, no diff on rebuild); bundle loads without the import.meta.url failure
  • Verify on a runner: native codex binary resolves from the committed dist/index.cjs (same class as the Claude CLI), and the Codex MCP client echoes the Mcp-Session-Id header against the in-process bridge

niranjan94 added 10 commits June 4, 2026 23:18
Review lenses previously diffed each changed file individually via
`git diff 'origin/<base>...HEAD' -- '<path>'`. In a `pull/N/merge`
checkout (shallow, no base tracking ref, persist-credentials: false)
that command fails three ways: `origin/<base>` does not exist, the
merge commit's parents are absent as objects, and git cannot
authenticate a fetch. Each lens then burned turns flailing through
failing git commands at the start of the run.

Provision the base branch in-process before the lenses run:
prepareReviewBase injects the App installation token into the remote
URL and fetches the base tip into refs/remotes/origin/<base> at depth 1.
The lens prompts now run a single two-dot `git diff 'origin/<base>' HEAD`
for the whole PR, replacing the per-file three-dot diffs. Two-dot needs
no shared ancestry, so a depth-1 fetch suffices on the shallow merge
commit, and one batched command removes the per-file round trips.

No consumer workflow change is required: the action fetches the base
itself using the App token, so persist-credentials: false and the
existing App credentials are sufficient.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shopfloor agent review: clean across 4/4 reviewers.

  • No compliance violations against CLAUDE.md: the new files match the documented layout (src/agents/codex.ts, src/agents/mcp-http-bridge.ts, src/config/codex-options.ts, src/setup/ensure-codex-cli.ts), CLAUDE.md and the Key Files table are updated alongside the code, the design doc lives under docs/superpowers/specs/<date>-...-design.md, tests live under test/, the example workflow is in examples/, new action inputs are declared in action.yml, and dist/index.cjs is rebuilt and committed.
  • No traceable correctness bugs found. The Codex adapter, MCP HTTP bridge, options builder, review-base provisioning, and tool-shape retype all match the spec; abort/timeout, error-kind mapping, auth branching, and bridge bearer-token gating are wired consistently with the existing Claude adapter patterns.
  • No exploitable security issues found: the new MCP bridge is loopback-only with a 256-bit bearer token, the Codex auth.json is written 0600 inside a 0700 mkdtemp dir, all new git invocations use array args with non-attacker-controlled refs, and no user input flows into shell strings, eval, file paths, SQL, or outbound URLs.
  • No code smells worth flagging at the smell-reviewer confidence threshold; the four-way lens prompt duplication is pre-existing on release/v2 and not introduced by this PR.

- Defer the missing-credential check from buildCodexOptions (construction) to
  CodexAgentAdapter.runStage (first use). entry.ts builds the adapter
  unconditionally, including for mode=resolve router jobs that never run an
  agent, so a construction-time throw deadlocked split-runner workflows that
  scope Codex creds to the execute job. Now symmetric with the Claude env
  builder, which never throws.
- Distinguish our own timeout (agent_timeout) from a caller-initiated
  abortController abort (agent_execution) via a timedOut flag, instead of
  labelling every aborted signal a timeout.
- Latch the "caps are ignored" warning to once per adapter instance; budgetUsd
  always carries a numeric default, so the unconditional warn spammed every
  stage.
- Share the subprocess env allowlist: export collectPassthroughEnv from
  agent-env.ts and reuse it in codex-options.ts instead of a second copy of
  PASSTHROUGH_KEYS that could silently diverge.
- Drop the redundant chmodSync; writeFileSync({mode:0o600}) on a freshly
  mkdtemp'd path sets 0600 atomically with no world-readable window.
httpServer.close()'s callback only fires once every connection drains, and it
does not terminate idle keep-alive sockets. The Codex MCP client holds a
persistent keep-alive socket for the stateful session, so close() (awaited in
the adapter's finally) would hang indefinitely and wedge the stage. Call
httpServer.closeAllConnections() before close().
boolish() treated any value other than the literal "false" as true, so a typo
on the security-relevant codex_network_access toggle (e.g. "no", "0", "off")
silently failed open to network-enabled. Replace with a z.enum(["true","false"])
parser that rejects unrecognized values, matching the other codex enum inputs.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shopfloor agent review: clean across 4/4 reviewers.

  • No compliance violations: spec path, pnpm command references, committed dist bundle, and Key Files updates all conform to CLAUDE.md (the only convention file in this repo).
  • Traced Codex adapter error-mapping branches, MCP HTTP bridge handshake/cleanup, prepareReviewBase fetch refspec + warn-and-continue path, and the prompt template's two-dot diff change. No correctness defects traceable at ≥75 confidence.
  • No exploitable security issues found: the new MCP bridge is loopback-only with a 256-bit random bearer; the Codex auth.json is written via mkdtempSync + 0o600; subprocess env is allowlisted via collectPassthroughEnv so secrets don't leak; git argv form prevents injection through baseRef; and strictBool defends the security-relevant toggles from silent coercion.
  • No maintainability smells worth flagging — the new Codex adapter, MCP bridge, and shared helpers mirror existing Claude-side patterns, and the collectPassthroughEnv extraction actually removes a duplication risk between the two adapter env builders.

@niranjan94
niranjan94 merged commit 12b9e1b into release/v2 Jun 12, 2026
6 checks passed
@niranjan94
niranjan94 deleted the feat/codex-adapter branch June 12, 2026 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant