Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions docs/ai/design/2026-08-11-feature-codex-print-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
phase: design
title: Codex Print-Mode Agent Design
description: Provider-minted session binding and run-per-message Codex execution
---

# Codex Print-Mode Agent Design

## Architecture Overview

Codex is added beside the existing Claude provider modules, sharing only the proven durable store/state primitives.

```mermaid
flowchart LR
CLI[agent start/list/detail/send] --> Resolver[provider-aware print resolver]
Resolver --> Claude[ClaudePrintAgentService]
Resolver --> Codex[CodexPrintAgentService]
Claude --> Repository[DurableAgentRepository]
Codex --> Repository
Codex --> Runner[CodexPrintRunner]
Runner -->|prompt via stdin| Exec[codex exec process]
Exec -->|JSONL thread/turn/item events| Runner
Runner -->|bind thread UUID during run| Repository
Exec --> Native[(Codex native session)]
```

Interactive adapters remain unchanged. No generic provider framework or persistent server is introduced.

## Data Models

```ts
type DurableProvider = 'claude' | 'codex';
type DurableAgent = ClaudeDurableAgent | CodexDurableAgent;

interface DurableAgentBase {
id: string;
name: string;
mode: 'durable';
cwd: string;
state: 'ready' | 'running' | 'degraded';
sessionHealth: 'uninitialized' | 'healthy' | 'unknown' | 'mismatch';
createdAt: string;
updatedAt: string;
lastActiveAt: string | null;
lastResult: PrintLastResult | null;
activeRun: PrintActiveRun | null;
}

interface ClaudeDurableAgent extends DurableAgentBase {
provider: 'claude';
providerSessionId: string;
}

interface CodexDurableAgent extends DurableAgentBase {
provider: 'codex';
providerSessionId: string | null;
}
```

Migration 003 stores flattened records in SQLite's `durable_agents` table. The provider column is application-validated; nullable unique session IDs allow unbound Codex creation without a follow-up migration. No legacy JSON import exists.

## API Design

```ts
create(input: { name: string; cwd: string; provider?: DurableProvider }): Promise<DurableAgent>;
bindProviderSession(agentId: string, runToken: string, providerSessionId: string): Promise<DurableAgent>;

interface CodexPrintRunRequest {
agent: CodexDurableAgent;
prompt: string;
executable?: string;
onSpawn(identity: ProcessIdentity): Promise<void>;
onSession(providerSessionId: string): Promise<void>;
}
```

`bindProviderSession` runs in `BEGIN IMMEDIATE`, verifies active-token ownership and UUID validity, permits only Codex null-to-value or same-value idempotence, relies on SQLite uniqueness, and uses a conditional update on `active_run_token`.

Initial argv is `exec --json -`; resume argv is `exec resume --json UUID -`. The prompt never enters argv.

## Component Breakdown

- `DurableAgent`: shared base and provider discriminants with canonical `AGENT_MODES.DURABLE`.
- `DurableAgentRepository`: provider-aware SQLite creation, uniqueness, CAS binding, and upstream reconciliation.
- `CodexCliProbe`: non-model version/help capability checks.
- `CodexPrintRunner`: safe spawn, process handshake, bounded JSONL parser, immediate session callback, assistant-result extraction.
- `CodexPrintAgentService`: resolve → acquire → run → bind → complete, with provider-specific health classification.
- CLI: selects service by requested/persisted provider and renders provider-specific labels/session state.
- `fake-codex.cjs`: deterministic executable contract and failure controls.

## Design Decisions

- Parallel Codex modules minimize Claude regression risk; shared-service extraction waits for another provider or demonstrated need.
- `thread.started.thread_id` is authoritative because initial and resumed 0.147.0 runs emit the same UUID.
- Binding occurs immediately during the owned first run so post-binding failure resumes instead of forking.
- Explicit UUID resume is mandatory; `--last`, names, transcript scanning, and `exec-server` are rejected.
- Unknown object events are forward-compatible, while required identity/result/completion events remain strict.

## Non-Functional Requirements

- Atomic per-agent exclusion prevents concurrent turns on one Codex thread.
- Stdout lines, stderr capture, and persisted summaries are bounded; malformed streams fail closed.
- Spawn uses no shell and no permission-bypass flags; prompt and native transcripts are never persisted.
- Creation/probe are non-billable and sends have no implicit retry.
- Existing schema records and interactive/Claude behavior remain compatible.
89 changes: 89 additions & 0 deletions docs/ai/implementation/2026-08-11-feature-codex-print-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
phase: implementation
title: Codex Print-Mode Agent Implementation
description: Implementation record, decisions, validation, and deviations
---

# Codex Print-Mode Agent Implementation

## Status

- Current task: Task 4.4, publish the reviewed branch and open the PR.
- Completed: Tasks 1.1–4.3.
- Task tracing: unavailable (`unknown command 'task'`).

## Development Setup

- Worktree: `feature-codex-print-mode`.
- Bootstrap: `npm ci` from the repository lockfile.
- Provider validation and tests are non-billable; only the deterministic fake Codex executable is used.

## Code Structure

- `packages/agent-manager/src/durable`: shared SQLite repository plus parallel Claude/Codex probe, runner, service, and errors.
- `packages/agent-manager/src/__tests__/durable`: Codex unit/service/integration and repository-binding tests.
- `packages/agent-manager/src/__tests__/fixtures/fake-codex.cjs`: executable provider fixture.
- `packages/cli/src/commands/agent.ts` and CLI tests: provider-aware routing/rendering.

## Implementation Notes

This section will be updated after each TDD task with changed files, red/green evidence, decisions, deviations, and edge cases. The load-bearing rule is that Codex's provider-minted UUID is persisted during the active first run before terminal success.

### Tasks 2.1–2.3

- Added provider-specific classified errors and a three-command, non-model Codex capability probe.
- Added safe initial/resume runner argv, process-identity-before-stdin handshake, strict bounded JSONL parsing, immediate async thread binding, ordered assistant messages, and terminal success requirements.
- Added Codex create/send orchestration with provider-aware create, run-token callbacks, mismatch/unknown health classification, and no retry.

TDD red: 14 focused tests failed on absent Codex exports. Green/refactor: those 14 tests passed; the full agent-manager suite passed 27 files/515 tests; typecheck and lint exited 0.

### Tasks 3.1–3.3

- Added an executable fake Codex CLI with deterministic provider-minted UUID, exact resume validation surface, stdin/cwd/argv capture, chunked results, and configurable protocol/process failures.
- Added integration proof that creation remains unbound/non-billable, first send binds, second send explicitly resumes, and post-binding failure retains the UUID.
- Made CLI print startup accept Claude or Codex, select the persisted provider for sends, render `Codex (print)`/`not started`, and derive JSON provider from the record.
- Preserved the common store resolver, exact-ID precedence, cross-mode ambiguity, synchronous timeout behavior, and interactive command paths.

TDD red: Codex fixture execution and two CLI routing tests failed before executable/routing support. Green/refactor: agent-manager passed 28 files/518 tests; CLI passed 79 files/932 tests; both typechecks and lints exited 0 (five existing CLI warnings).

### Tasks 4.1–4.3

- Hardened runner callback/process error classification and probe recognition of the standalone stdin dash token.
- Rebased onto migration 003's SQLite `durable_agents` table and removed all legacy JSON import/versioning assumptions.
- Reviewed all changed files against requirements/design and traced CLI/service/store call sites. No blocking security, compatibility, or integration findings remain.

TDD red/green evidence includes the standalone-dash false-positive probe test, child-process error classification test, and SQLite token-owned binding tests.

## Integration Points

- `DurableAgentRepository` remains the single SQLite mapping and exclusion authority.
- CLI start selects probe/service by requested type; send selects by persisted record provider.
- Runner callbacks persist provider process identity before stdin and provider session identity on `thread.started`.

## Error Handling

- `CodexPrintError` classifies unsupported CLI, process, protocol, session mismatch, and missing result failures.
- The service records failures through token-owned completion, mapping well-formed UUID mismatch to `mismatch` and other session/protocol failures to `unknown`.
- There is no retry, replacement session, or fallback to `--last`.

## Performance Considerations

- Each send spawns one process; no idle process or server is retained.
- JSONL line buffering, stderr capture, and stored summaries are bounded.
- SQLite transactions are short-lived; `active_run_token` CAS ownership spans the provider run.

## Security Notes

- Prompt only via stdin after provider identity persistence; `shell: false`; fixed argv.
- Exact canonical cwd; explicit UUID resume; no permission bypass or transcript copying.
- Provider output is untrusted and validated before affecting durable identity or success.

## Validation Evidence

- Base and feature lifecycle lint: passed.
- Root lint: passed with six existing warnings in unrelated files and no errors.
- Root build: six projects passed.
- Root tests: six projects passed; agent-manager 28 files/527 tests and CLI 79 files/932 tests in their fresh coverage runs.
- Agent-manager coverage: 90.23% statements and 93.4% lines overall; every new Codex module has 100% lines/functions, and `CodexCliProbe` has 100% statements/branches/functions/lines. Runner/service residual branch-only gaps are injected/default process plumbing, not pure parsing logic.
- CLI coverage: 79 files/932 tests; 71.6% statements, 61.67% branches, 69.77% functions, 72.74% lines overall.
- Known test-run warning: pre-existing max-listener warnings in agent-manager tests; no new persistent process listeners were added.
79 changes: 79 additions & 0 deletions docs/ai/planning/2026-08-11-feature-codex-print-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
phase: planning
title: Codex Print-Mode Agent Implementation Plan
description: Ordered TDD work for durable Codex print agents
---

# Codex Print-Mode Agent Implementation Plan

## Milestones

- [x] Milestone 1: Provider-aware durable model, migration, and session binding.
- [x] Milestone 2: Codex probe, runner, service, and deterministic fixture.
- [x] Milestone 3: CLI integration and compatibility coverage.
- [ ] Milestone 4: Documentation, full validation, review, and PR publication.

## Task Breakdown

### Phase 1: Foundation

- [x] Task 1.1: Extend the discriminated `DurableAgent` union and provider-aware SQLite creation.
- Outcome: Claude and Codex records coexist in `durable_agents`; Codex begins unbound and no legacy import is added.
- Validation: focused `DurableAgentRepository` tests and typecheck.
- [x] Task 1.2: Drive `bindProviderSession` integrity behavior with failing tests.
- Outcome: token-owned atomic null-to-UUID binding, idempotence, mismatch and duplicate rejection.
- Dependencies: Task 1.1.
- Validation: focused store tests for every binding branch and persistence after failure.

### Phase 2: Codex execution

- [x] Task 2.1: Drive `CodexCliProbe` and provider error types with failing tests.
- Outcome: version/help-only capability validation and sanitized errors.
- [x] Task 2.2: Drive `CodexPrintRunner` with fake spawn/fixture tests.
- Outcome: exact argv/cwd/stdin handshake; bounded strict JSONL; immediate UUID binding; ordered assistant output.
- Validation: normal, chunked, unknown, malformed, oversized, truncated, missing, mismatch, stderr, and exit branches.
- [x] Task 2.3: Drive `CodexPrintAgentService` orchestration with failing tests.
- Outcome: first/resume lifecycle, correct health degradation, no retry, binding retained after later failure.

### Phase 3: CLI and integration

- [x] Task 3.1: Add provider-aware exports and fake-Codex integration journey.
- Outcome: create performs probes only; first send binds; second send resumes same UUID; concurrency/recovery/cwd work offline.
- [x] Task 3.2: Drive CLI start/list/detail/send behavior with failing command tests.
- Outcome: `--type codex --mode print`, `Codex (print)`, `not started`, record-derived JSON provider, provider-selected send.
- [x] Task 3.3: Run Claude-print and interactive-Codex regression tests and inspect excluded command paths.

### Phase 4: Validation and publication

- [x] Task 4.1: Reconcile implementation/testing docs and reach 100% coverage on new pure logic.
- [x] Task 4.2: Run feature/base lifecycle lint, lint, typecheck, build, package/full tests, and coverage.
- [x] Task 4.3: Perform design-alignment and holistic code review; fix blocking findings via TDD.
- [ ] Task 4.4: Create conventional commits, fetch/rebase `origin/main`, rerun gates, push, and open the requested PR.

## Dependencies

Tasks are ordered because the domain/store contract underpins runner/service and CLI behavior. Tests use only injected process boundaries and `fake-codex.cjs`; no model credentials or calls are required. Optional task tracing is unavailable (`npx ai-devkit@latest task list --name codex-print-mode --json` returned `unknown command 'task'`).

## Timeline & Estimates

- Foundation: medium; migration and binding integrity are highest risk.
- Provider execution: medium/high; protocol and crash ordering dominate.
- CLI/integration: medium; compatibility tests dominate.
- Validation/publication: medium; coverage and rebase can reveal follow-up fixes.

Work proceeds sequentially through the approved lifecycle without a calendar commitment.

## Risks & Mitigation

- Orphan/forked sessions: bind on `thread.started`; never recover through `--last`.
- Concurrent resume: reuse SQLite `BEGIN IMMEDIATE` and token-owned CAS updates.
- Protocol drift: capability probe, strict required events, tolerant unknown objects.
- Secret leakage: stdin-only prompt, bounded/sanitized diagnostics, no transcript storage.
- Regression: parallel provider modules plus focused and full existing suites.
- Scope growth: deletion, Pi, capacity routing, server mode, and generic adapters remain deferred.

## Resources Needed

- Existing Claude print implementation/tests/docs as the template.
- Codex 0.147.0 empirical event contract supplied in the build brief.
- Node/Nx/Vitest toolchain and temporary fake-provider files.
85 changes: 85 additions & 0 deletions docs/ai/requirements/2026-08-11-feature-codex-print-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
phase: requirements
title: Codex Print-Mode Agents
description: Durable AI DevKit agents backed by synchronous Codex exec runs
---

# Codex Print-Mode Agents

## Problem Statement

AI DevKit supports durable Claude print agents, but Codex agents still require a continuously running interactive process. Users need a durable logical Codex identity whose messages run synchronously in short-lived `codex exec` processes while retaining one native Codex conversation.

### Terminology

- **Logical agent:** durable AI DevKit identity created by `agent start --mode print`.
- **Provider session:** Codex conversation identified by a provider-minted thread UUID.
- **Provider process:** one ephemeral `codex exec` child process.
- **Run:** one `agent send` handled by one provider process.

## Goals & Objectives

### Goals

- Add `agent start --type codex --mode print` while preserving interactive Codex as the default and Claude print behavior.
- Create the logical record without a model run or invented provider UUID.
- On first send, run `codex exec --json -`, capture `thread.started.thread_id`, and bind it atomically during the owned run.
- On later sends, run `codex exec resume --json <uuid> -` and require the emitted UUID to match.
- Reuse SQLite durable state, CAS ownership, stale recovery, canonical cwd binding, safe process identity, and bounded results.
- Pass prompts only through stdin and validate Codex capabilities without a model call.
- Keep print agents visible in human and JSON list/detail output.

### Non-goals

- Queues, retry, scheduling, cancellation, background workers, `codex exec-server`, `resume --last`, session naming, or transcript copying/deletion.
- Print-agent delete/kill semantics, Pi print mode, capacity-aware routing, or a generic provider-adapter refactor.
- Permission bypass flags, authentication/quota model calls, or changes to channels/groups/TUI.

## User Stories & Use Cases

- As a user, I can create a Codex print agent without consuming tokens; its provider session displays `not started`.
- As a user, my first synchronous send creates and durably binds the Codex thread UUID.
- As a user, later sends explicitly resume the same UUID in the immutable canonical cwd.
- As a user, I receive an immediate busy error for concurrent sends, never a queue.
- As a user, failures before binding leave the session uninitialized; failures after binding retain the UUID for safe explicit resume.
- As a user, exact IDs win and ambiguous names across interactive/print modes are rejected.

## Success Criteria

### Domain and persistence

- `DurableAgent` is a `claude | codex` discriminated union; Claude session IDs remain non-null and Codex IDs begin null.
- Records persist in migration 003's `durable_agents` table; no legacy JSON import exists for this unreleased feature.
- `bindProviderSession` requires the active run token, permits Codex null-to-UUID only, is identical-UUID idempotent, rejects replacement, and rejects duplicate non-null provider/session pairs.
- First-run binding is atomically durable before success and remains durable after a later run failure.

### Provider execution

- Probe runs only `codex --version`, `codex exec --help`, and `codex exec resume --help` and verifies `exec`, `resume`, `--json`, and stdin `-` support.
- Runner uses `shell: false`, discrete fixed argv, exact stored cwd, verified process identity, and calls `onSpawn` before sending the prompt via stdin.
- Success requires a valid matching UUID, at least one valid assistant message, `turn.completed`, clean bounded JSONL termination, and exit code zero.
- Unknown object events are ignored; malformed/non-object/oversized/truncated JSONL, missing required events, mismatch, or non-zero exit fails safely.
- Assistant texts are collected in arrival order and the final non-empty text is returned; stderr and persisted summaries are bounded and sanitized.

### CLI and compatibility

- `--mode print` accepts Claude and Codex; omitted mode remains interactive.
- Human output renders `Codex (print)` and an unbound session as `not started`; JSON derives provider from the record.
- Print sends remain synchronous and preserve exact-ID/name-resolution rules.
- Claude print, interactive Codex, and excluded commands retain existing behavior.

### Validation

- Deterministic fake-Codex unit/integration tests cover initial/resume, chunking, multiple results, process/protocol failures, binding timing, mismatch, concurrency, stale recovery, cwd, and secret safety without a real model.
- New pure/unit logic reaches 100% coverage; package and repository lint, typecheck, build, tests, coverage, and lifecycle lint pass.

## Constraints & Assumptions

- Target contract is Codex CLI 0.147.0: past-tense dotted JSONL events and provider-minted UUIDs.
- Native Codex persistence remains provider-owned; AI DevKit stores only identity, binding, state, lock/process metadata, and a bounded result summary.
- The local OS account is the authorization boundary. Codex inherits configured sandbox/approval behavior.
- A crash before processing `thread.started` may orphan a native session; recovery must never guess via `--last`.

## Questions & Open Items

No blocking questions remain. Print deletion, Pi support, capacity integration, and common-service extraction are explicit follow-ups.
Loading
Loading