Skip to content

feat(codex): one-shot runner, model registry, 4-way backend parity tests - #175

Merged
claudiusthebot merged 1 commit into
mainfrom
feat/codex-parity
May 15, 2026
Merged

feat(codex): one-shot runner, model registry, 4-way backend parity tests#175
claudiusthebot merged 1 commit into
mainfrom
feat/codex-parity

Conversation

@claudiusthebot

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #174 (which added the Codex backend's basic
handleMessage). This PR brings Codex to parity with Claude/Kilo/
OpenCode on the optional QueryBackend surface that core features
(heartbeat, dream, /status, /model, /settings) rely on.

What's new

src/backend/codex/one-shot.tsrunOneShotAgent for heartbeat

  • dream. Drives thread.runStreamed directly, translates Codex's
    full ThreadEvent surface into structured run-log lines:
  • thread.started → records the thread id
  • turn.started / turn.completed / turn.failed → lifecycle markers
  • item.completed for:
    • agent_message → "Assistant" block with the model's text
    • reasoning → "Reasoning" block (private scratchpad surfaced in logs)
    • mcp_tool_call → "MCP tool call" block with server.tool + arguments
    • command_execution → "Command" block with status + exit code
    • file_change → "File changes" block with kind + path list
    • web_search → "Web search" block with query
    • todo_list → checkbox-style todo list rendering
    • error → "Error item" block

Honors the heartbeat module's AbortSignal so timeouts stop the
model and surface "Aborted" in the log.

src/backend/codex/models.ts — model catalog + UnifiedModel
helpers. Codex ships a fixed set rather than a live provider catalog,
so this is a hand-maintained array (gpt-5-codex, gpt-5, gpt-5-mini,
o4-mini — Codex's reasoning-effort variants flow through
modelReasoningEffort thread option rather than separate model ids).
Provides resolveModel / getModelInfo / getSettingsPresentation /
getProviders / getProviderModels / formatModelError /
listModels with identical signatures to kilo / opencode / claude-sdk.

src/backend/codex/factory.ts — wires the full QueryBackend
surface for Codex (resolveModel/getModelInfo/..., runOneShotAgent,
backendLabel).

Tests

19 new unit tests for codex/models.ts:

  • Catalog presence (gpt-5-codex flagship + every model has provider/providerName/selectable)
  • resolveModel: exact match, missing on empty, missing on bogus, ambiguous on multi-prefix
  • getModelInfo: hits + misses
  • getSettingsPresentation: active marker, callbackPrefix override
  • getProviders: OpenAI as sole provider, modelCount accurate
  • getProviderModels: pagination, unknown provider → empty
  • formatModelError: ambiguous + missing variants
  • listModels: default / free (empty — no free Codex models) / all

2 new tests for codex/one-shot.ts using a mocked @openai/codex-sdk:

  • Event sequence (thread.started → turn lifecycle → agent message → MCP tool call → turn.completed) emits the expected log lines
  • Pre-aborted signal yields the "Aborted" log entry

6 new tests for backend registry parity (backend-registry-parity.test.ts):

  • All four backends (claude/kilo/opencode/codex) self-register on side-effect import
  • listBackends() returns them sorted by id
  • Every factory has non-empty label + init function
  • Expected labels (Anthropic, Kilo, OpenCode, Codex)
  • Duplicate-id registration throws

Full suite: 2301 passing (was 2274), 12 skipped (live-tier), 0 failing.

Test plan

  • npx tsc --noEmit clean
  • npm test — 2301 passing, 12 skipped, 0 failing
  • npm run lint — 0 errors, 15 pre-existing warnings unchanged
  • npm run format:check clean
  • CI matrix on push

🤖 Generated with Claude Code

…arity tests

Brings Codex up to parity with Claude/Kilo/OpenCode on the optional
`QueryBackend` surface that core features (heartbeat, dream, /status,
/model, /settings) depend on.

Added:

  - `src/backend/codex/one-shot.ts` — `runOneShotAgent` for heartbeat
    + dream support. Drives `thread.runStreamed` directly, translates
    Codex's full ThreadEvent surface (thread.started, turn.{started,
    completed, failed}, item.completed for agent_message / reasoning /
    mcp_tool_call / command_execution / file_change / web_search /
    todo_list / error) into structured run-log lines.

  - `src/backend/codex/models.ts` — model catalog + UnifiedModel
    helpers. Codex ships a fixed set (gpt-5-codex, gpt-5, gpt-5-mini,
    o4-mini) rather than a live provider catalog, so this is a
    hand-maintained array — kept short because Codex's
    reasoning-effort variants are configured via
    `modelReasoningEffort` rather than separate model ids. Provides
    resolveModel / getModelInfo / getSettingsPresentation /
    getProviders / getProviderModels / formatModelError / listModels
    with identical signatures to kilo/opencode/claude-sdk.

  - `src/__tests__/codex-models.test.ts` — 19 tests covering all
    catalog helpers (exact match, prefix ambiguity, missing,
    presentation buttons, provider pagination, error formatting,
    free-filter behaviour).

  - `src/__tests__/codex-one-shot.test.ts` — 2 tests using a mocked
    `@openai/codex-sdk` to drive a representative event sequence and
    verify run-log translation (thread-started, turn lifecycle,
    agent message, MCP tool call, abort path).

  - `src/__tests__/backend-registry-parity.test.ts` — 6 tests proving
    all four backends (claude, kilo, opencode, codex) register
    themselves with non-empty labels + init functions, sorted output
    from listBackends, duplicate-id rejection.

Factory now wires the full QueryBackend surface for Codex:
  - resolveModel / getModelInfo / getSettingsPresentation /
    getProviders / getProviderModels / formatModelError / listModels
  - runOneShotAgent
  - backendLabel

Heartbeat + dream now backend-agnostic across all 4 backends.

Full suite: 2301 passing (was 2274 + new tests), 12 skipped (live),
0 failing. tsc clean, lint 0 errors / 15 pre-existing warnings.
@claudiusthebot
claudiusthebot enabled auto-merge (squash) May 15, 2026 23:50
@claudiusthebot
claudiusthebot merged commit 44ab734 into main May 15, 2026
31 checks passed
claudiusthebot added a commit that referenced this pull request May 16, 2026
… line (#177)

Three small parity improvements building on the Codex backend
foundation (#174, #175, #176):

1. **Cleanup hook in codex factory.** Kilo/OpenCode return a
   `cleanup: stopServer` callback from their factory `init` so the
   dispatcher can clean up on hot-reload or shutdown. Codex now
   returns a `cleanup` that calls `resetCodexState()` — clearing the
   cached Codex instance + frontend label + gateway port. Each
   `runStreamed` spawns a fresh subprocess so there's no long-lived
   server to stop, but resetting the module-scoped state matters for
   test isolation and future hot-reload paths.

2. **Backend conformance expanded to cover Codex.** Two of the four
   conformance tests (`routeDelivery` route parity + synthetic-error
   label prefix) now drive all three remote-route-using backends
   (Kilo / OpenCode / Codex) through the same shared `routeDelivery`
   helper and assert identical decisions + identical
   `⚠️ <Backend>: <msg>` formatting. The MCP-rotation + SSE event
   tests stay Kilo/OpenCode-only since Codex's MCP wiring is
   constructor-time TOML, not HTTP-API registration.

3. **Codex model-resolution log line.** Kilo emits
   `[chatId] Kilo model resolved: provider=X model=Y`; OpenCode emits
   the same. Codex now emits `[chatId] Codex model resolved: <model>`
   on every turn so the agent log has a uniform per-turn fingerprint
   across all four backends.

Tests: 2305 passing, 12 skipped (live-tier), 0 failing.
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