feat(trace): add Codex provider

Merged
MI
1d ago
+3209-77

Developers can record Codex sessions with chainloop trace init --codex or chainloop trace run --codex. Native hooks capture session rollouts, token usage and estimated cost, tool calls, and per-file attribution for multi-file patches and shell commands.

Codex conversations and tool exchanges use the message shape expected by the existing session viewer, so it renders their text, calls, and results. Each converted entry preserves its original event in source_event. Codex regression and E2E checks reject native-only conversations, malformed tool calls, and missing or unreadable matching tool results. The shared raw_session schema stays open, and other providers retain their existing formats and transcript checks. A common versioned transcript contract is deferred to a separate change.

The repository is initialized with project-local Codex hooks. The provider preserves existing user hooks, validates transcript paths and identity, and works with the shared local-storage pipeline. The Codex E2E harness isolates personal skills and preserves native model metadata when running through OpenRouter.

Implements R-001 through R-006 in docs/specs/issue-3657-codex-trace-provider.md.

Implements milestone 1 of #3657. Milestone 2 adds Codex skill tracking, spec copy pointers, and pasted-image capture. The task remains open until both milestones are delivered.

Codex hook commands skip successfully when chainloop is absent from PATH. When available, the wrapper preserves stdin, stdout, and exit status. A native commandWindows override avoids requiring a POSIX shell on Windows. Re-running init upgrades old commands while retaining user hooks. Each hook has a descriptive Chainloop Trace status label in the Codex hooks menu; init also upgrades previously unlabeled hooks. Hook definition changes require a new trust review in Codex. Follow-up for Claude and Cursor: #3687.

Session storage caching reloads the current record before saving its root, preserving metadata written during discovery. A damaged optional record no longer prevents copying a valid transcript from configured storage.

Validation

  • Full CLI unit test suite passes, with Git signing disabled only for the test subprocess.
  • Other-provider live E2E regression run: Claude Code 2.1.305 with Haiku 5.5 through OpenRouter and OpenCode 2.0.32 with openrouter/anthropic/claude-haiku-5.5 both pass, with no failed feature checks. Claude's first-party run was blocked by the account's weekly usage limit; the successful retry used OpenRouter. These installed versions are newer than the suite pins. Cursor was skipped as requested; Pi's unsupported column skips all features.
  • Codex CLI 0.163.0 / openrouter/openai/gpt-6-luna: 22 feature checks pass, zero failures; 4 unsupported optional features are skipped, and 7 cells have no coverage. The run includes the new net contribution, session updates, and rename/rewrite checks from main. After narrowing validation to Codex, the live transcript check was rerun and passed.
  • Go formatting and git diff --check are clean. Change-scoped lint reports zero issues. Full lint still reports 77 existing issues.
  • Codex provider and E2E regressions cover normalized messages, matching readable tool results, original events, native output arrays, malformed tool calls, and rejection of native-only conversations. A regression verifies that the Codex checks do not constrain other provider formats. The targeted tests pass after narrowing the change.
  • Actual platform renderer tested against new live E2E evidence: prompts, assistant content, calls, and nonempty tool results render. The original uploaded native evidence reproduces the empty conversation. This is a local renderer integration check, not a browser E2E test.
  • Codex CLI 0.163.0 interactive verification: the welcome banner appears after the first user message, and the session link appears after push. Both compatibility cells are Yes; this manual verification is separate from local-mode E2E coverage.
  • CLI reference regenerated; root README and provider compatibility tables updated from E2E results.

Limits

Child-session transcripts, skills tracking, spec source pointers, and pasted-image pointers are deferred. Control-plane uploads and headless hook-message delivery are not verified by this local-mode suite. Windows execution tests are present but skipped on the Linux validation host. In linked worktrees, Codex 0.163 discovers project hooks from the primary checkout; that checkout also needs the installed hooks. Previously uploaded native-only artifacts retain their original format; normalized output applies to new captures. Rollout format changes may require parser updates; cost uses standard API estimates rather than subscription billing.

AI assistance: Codex.

Provider checklist

Provider package (app/cli/internal/trace/<agent>/)

  • Name constant and var _ trace.Provider = (*Provider)(nil).
  • InstallHooks: a second call changes nothing. The hooks of the user stay.
  • InstallHooksForTraceRun: all hooks except session end.
  • SettingsFile: the file that install writes.
  • UninstallHooks: removes only the Chainloop hooks.
  • HooksInstalled: true only for the Chainloop hooks.
  • Hook events: session start, user prompt (if the agent has it), and session end.
  • Hook events: before and after each file tool and shell tool, and after the skill tool (for skills tracking). (No skill tool integration in this provider.)
  • ReadHookInput: typed struct, size limit, absolute FilePath, Cwd and TranscriptPath, ToolUseID and AgentID, Command, AgentVersion and Model when the transcript does not hold them. (Multi-file patch paths are expanded into per-file inputs by the shared handlers; version comes from the rollout.)
  • IsFileWritingTool and IsCommandTool: one Go slice is the source of each list (and of the plugin, if there is one).
  • Attribution strategy selected: snapshot (CaptureFileSnapshot, ResolveBeforeContent, CleanupAfterEdit) or reverse edits (HookInput.Edits).
  • Shell tool sends pre and post hooks with the same ToolUseID.
  • CopySessionData: safe to repeat, checks paths from the payload, does not fail the hook when the agent or the transcript is missing.
  • ParseSession: reads only the copy in opts.SessionDir.
  • CopySessionData and ParseSession: no control plane and no network, so that local mode works.
  • Trace state only through the *state.Store, so that trace run --local works outside a git repository.
  • Evidence: agent name and version, session ID and times, primary model, models used, model provider.
  • Evidence: token usage (input, output, total, cache read, cache write).
  • Evidence: cost from the agent, or from an embedded price table, or zero with a warning.
  • Evidence: tools used, conversation counts, subagents (if the agent has them). Root-session tools and conversation counts are implemented; child-session capture is deferred.
  • Evidence: RawSession["main"] is never nil. Each line is a JSON object.
  • Evidence: a warning for each data item that cannot be read.
  • Session-start, prompt-submit, and post-tool responses: one JSON document each, and nothing for an empty message.
  • Supports* methods tell the truth about each channel.
  • AnnounceToUser returns trace.ErrAnnounceUnsupported when there is no channel. Codex has a documented announcement channel.
  • Optional: SkillTracker, SpecCopyReplacer, PastedImageFinder, each with a compile-time check. Deferred in this provider; the compatibility table marks these features No.
  • Unit tests: hooks, parse with real testdata/, announce responses, copy.

CLI wiring

  • Registered in providers/registry.go.
  • traceProviderFlags field, any(), names().
  • --<agent> flag in trace init (with help text) and in trace run.
  • chainloop trace hook <agent> … subcommands in cmd/trace_hook.go, with skipActionOptsInit.
  • Pre-tool response written first, if the agent blocks tools without one. Not required by Codex.
  • providers/capabilities_test.go updated.
  • cmd/trace_init_providers_test.go and cmd/trace_init_test.go updated.
  • make generate run for the CLI reference. CLI reference regenerated with go generate ./app/cli/documentation; full API generation was unnecessary.

End-to-end tests and documentation

  • e2e/<agent>.go implements e2e.Harness.
  • Harness in the harnesses map of e2e/e2e_test.go.
  • Version and models in e2e/config.yaml.
  • Suite run for the new column, with the results checked.
  • README.md: new column in each feature table, with values from the suite.
  • README.md: a "Why not Yes" line for each cell that is not Yes.
  • README.md: row in "How each agent is hooked in", and provider list at the top updated.
  • Spec in docs/specs/ for a large provider, with requirement IDs cited in the pull request.
  • License header year updated in each changed file.
  • make -C app/cli lint and make -C app/cli test pass. CLI tests pass; full lint reports 77 existing issues. Change-scoped lint reports zero issues.