Files
paseo/plan.md
Mohamed Boudra f22f76c56b Fix Codex MCP timeline persistence after daemon restart
When daemon restarts, SESSION_HISTORY is lost because it's an in-memory
Map. This fix adds disk-based timeline loading from Codex rollout files.

Changes:
- Add loadPersistedHistoryFromDisk() method to load from rollout files
- Add helper functions for finding and parsing rollout JSONL files
- Call disk loading in connect() when resumeHandle exists but SESSION_HISTORY is empty
- Add E2E test for timeline persistence across daemon restart

The rollout files are stored at ~/.codex/sessions/<date>/rollout-*.jsonl
and contain JSONL entries with response_item and event_msg types.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 19:14:51 +07:00

5.3 KiB
Raw Blame History

Plan

Context

Build a new Codex MCP provider sidebyside with the existing Codex SDK provider. The new provider lives in packages/server/src/server/agent/providers/codex-mcp-agent.ts and is selected via a new provider id (e.g. codex-mcp). All testing is E2E only (no mocks/fakes). Use /Users/moboudra/dev/voice-dev/.tmp/happy-cli/src/codex/ as reference for MCP + elicitation.

CRITICAL RULES - READ BEFORE EVERY TASK

  1. NO VAGUE REPORTS: Never say "test hung", "was interrupted", "failed locally" without:

    • The EXACT error message or stack trace
    • The SPECIFIC line of code causing the issue
    • A concrete hypothesis for the root cause
  2. NO SKIPPING/DISABLING TESTS: Skipping tests, adding .skip, or "opt-in gating" is NOT ACCEPTABLE. Fix the actual problem. If a test hangs, find out WHY and fix the code, not the test.

  3. NO WORKAROUNDS: Adding timeouts, fallbacks, or "defensive" code that hides bugs is forbidden. The code must work correctly, not appear to work.

  4. INVESTIGATE DEEPLY: When something fails:

    • Read the actual source code
    • Add debug logging if needed
    • Trace the exact execution path
    • Find the ROOT CAUSE, not symptoms
  5. BE SPECIFIC: Every "Done" entry must include:

    • What the actual problem was (specific)
    • What code was changed (file:line)
    • How you verified it works

Completed Work (Compacted)

Codex MCP Provider (2025-12-24)

  • Created codex-mcp-agent.ts with MCP stdio client, event mapping, permissions, persistence, abort handling
  • Fixed model availability (removed hardcoded gpt-4.1), permission elicitation, exit code handling
  • Fixed thread/item event mapping for file_change, mcp_tool_call, web_search, todo_list
  • Fixed persistence to include conversationId metadata
  • Resolved Codex MCP vs Codex SDK elicitation parity (CRITICAL FINDING: codex exec ignores approval events)
  • All 14 Codex MCP unit tests pass; Codex is now the only provider (deprecated SDK provider removed)
  • Reports: CODEX_MCP_MISMATCH_REPORT.md, REPORT-codex-mcp-audit.md

UI/UX Fixes (2025-12-25)

  • Removed duplicate "Codex MCP" option - now shows only "Codex"
  • Fixed duplicate user/assistant messages (provider was emitting, but agent-manager already dispatches)
  • Fixed Codex agent-control MCP parity with Claude (added MCP servers to Codex config)
  • Fixed agent timestamp not updating on click without interaction

DaemonClient Implementation (2025-12-25)

  • Created packages/server/src/server/test-utils/daemon-client.ts (~550 lines)
  • Created packages/server/src/server/test-utils/daemon-test-context.ts
  • Created packages/server/src/server/daemon.e2e.test.ts (25 passing tests)
  • Reports: REPORT-daemon-client-design.md, REPORT-daemon-e2e-audit.md, REPORT-claude-permission-tests.md

DaemonClient API:

  • Connection: connect(), close()
  • Agent lifecycle: createAgent(), deleteAgent(), listAgents(), listPersistedAgents(), resumeAgent()
  • Agent interaction: sendMessage(), cancelAgent(), setAgentMode(), initializeAgent(), clearAgentAttention()
  • Permissions: respondToPermission(), waitForPermission()
  • Git: getGitDiff(), getGitRepoInfo()
  • Files: exploreFileSystem()
  • Models: listProviderModels()
  • Waiting: waitForAgentIdle()
  • Events: on(), getMessageQueue(), clearMessageQueue()

E2E Test Coverage:

  • Basic flow (Codex + Claude): create agent, send message, verify response
  • Permissions (Codex + Claude): approve/deny, permission_requested/resolved cycle
  • Persistence: delete agent, resume from handle, verify conversation context
  • Multi-agent: parent creates child via agent-control MCP
  • Agent management: cancelAgent, setAgentMode, listAgents
  • Timestamp: verify clicking agent doesn't update timestamp
  • Git: diff (staged/unstaged/modified), repo info (branch, dirty state)
  • Files: list directory, read file content
  • Models: list Codex and Claude provider models
  • Images: single and multiple image attachments to Claude

Tasks

  • BUG: Codex MCP agent returns 0 timeline events after daemon restart.

    • Done (2025-12-25 19:13): Fixed by implementing disk-based timeline history loading in codex-mcp-agent.ts.

    WHAT: Added rollout file parsing to load persisted timeline from ~/.codex/sessions/ when SESSION_HISTORY is empty after daemon restart.

    CHANGES:

    • codex-mcp-agent.ts:2-6: Added imports for fs, Dirent, os, path
    • codex-mcp-agent.ts:2521: Added resumeHandle field to store handle for async loading
    • codex-mcp-agent.ts:2530: Save resumeHandle in constructor
    • codex-mcp-agent.ts:2592-2596: Load history from disk in connect() when persistedHistory.length === 0
    • codex-mcp-agent.ts:2616-2628: New loadPersistedHistoryFromDisk() method
    • codex-mcp-agent.ts:3919-4181: New helper functions: resolveCodexSessionRoot(), findRolloutFile(), parseRolloutFile(), loadCodexPersistedTimeline(), plus rollout entry parsing functions

    TEST ADDED:

    • daemon.e2e.test.ts:1638-1757: New test "Codex agent timeline survives daemon restart" that verifies timeline is preserved via agent_stream_snapshot message after resume

    RESULT: E2E test passes, typecheck passes. Timeline items are loaded from rollout files when resuming an agent after daemon restart.