Skip to content

feat(assistants): implement Codex rollout-based file attachment detection - #466

Open
Sergei-Nikitin-epam wants to merge 7 commits into
codemie-ai:mainfrom
Sergei-Nikitin-epam:EPMCDME-13885
Open

feat(assistants): implement Codex rollout-based file attachment detection#466
Sergei-Nikitin-epam wants to merge 7 commits into
codemie-ai:mainfrom
Sergei-Nikitin-epam:EPMCDME-13885

Conversation

@Sergei-Nikitin-epam

@Sergei-Nikitin-epam Sergei-Nikitin-epam commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds codexUploadsDetector.ts: scans Codex session directories for the rollout whose session_meta.cwd resolves to the current working directory, then extracts input_image blocks from the most-recent user turn using a two-pass scan
  • Extends codex-message-types.ts with CodexResponseItemMessage and CodexContentBlock interfaces; adds images, local_images, and text_elements fields to CodexEventMsg
  • Wires a CODEMIE_AGENT === 'codex' dispatch gate in chat/index.ts — Codex sessions take the new path, Claude sessions continue to use CODEMIE_SESSION_ID-based detection

Achieves feature parity with the Claude file attachment detector. No regression on the Claude path (covered by EPMCDME-13907 cherry-picks on this branch).

Why direct rollout scanning

Codex hooks do not fire reliably, so hook-based session correlation is skipped. CodexSessionAdapter requires AgentMetadata.dataPaths.home which is unavailable in the CLI layer. The detector uses getCodexDiscoverySessionRoots() + readJSONLTolerant directly and matches sessions by CWD realpath (symlink-tolerant).

Changes

  • src/cli/commands/assistants/chat/codexUploadsDetector.ts (new) — rollout discovery, two-pass extraction, OOM-safe base64 size guard
  • src/agents/plugins/codex/codex-message-types.tsCodexResponseItemMessage, CodexContentBlock, CodexEventMsg extension
  • src/cli/commands/assistants/chat/index.ts — agent-aware detection dispatch

Test plan

  • Run codemie assistants chat <id> "message" inside an active Codex session — verify attached image is detected and uploaded
  • Confirm no detection occurs when CODEMIE_AGENT is unset (Claude path unchanged)
  • Confirm multi-image + file mixed turn resolves correct filenames (imageOnlyIndex fix)
  • Confirm follow-up message after upload does not displace filename chain (two-pass fix)

Fixes: EPMCDME-13885

SergeyVNikitin and others added 6 commits August 5, 2026 14:38
…oad detection

Replace broken two-pass buildAttachmentMap (wrong JSONL structure assumption)
and RECENT_MESSAGES_LIMIT=2 (too narrow for real sessions with tool-result
messages) with a turn-boundary backward scan that stops at the most recent
assistant message.

Real Claude Code JSONL has isMeta=true messages carrying both base64 attachment
data and [Image: source: /path] filename text in the same message object.
The old code expected base64 in a non-meta parent and filename text in a meta
child, so the attachment map was never populated and zero files were returned.

The scan window of 2 also broke in real sessions where tool-result messages
at positions 1-2 pushed the image meta message to position 3, outside the window.

EPMCDME-13907
…ude sessions

The previous turn-boundary scan (stopping at type==='assistant') failed in
real Claude Code JSONL because:
- Bug A: the assistant tool_use entry appears *after* the user's isMeta
  messages for the same prompt, so the scan broke before reaching the images.
- Bug B: base64 data and the [Image: source: /path] filename live in two
  *separate* isMeta entries (not the same one), so the old single-message
  pass produced the image with a fallback filename.

Fix: use the promptId field that Claude Code stamps on every message in a
single prompt turn. Find the most recent non-meta user message, capture its
promptId, collect all isMeta messages sharing that id, gather filenames
across them first, then match positionally to base64 attachment items.

Also adds promptId to the ClaudeMessage interface and updates test fixtures
to reflect the real split-message JSONL structure.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…t --conversation-id

--conversation-id identifies the assistant chat thread (e.g. a workflow_id
generated by a skill). CODEMIE_SESSION_ID identifies the Claude session whose
JSONL contains uploaded file blobs. Using --conversation-id for session lookup
caused detectFileUploadsFromSession to look for a non-existent session file,
returning no attachments even when files were uploaded in the current Claude turn.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162w98kuqJ7EWaA6h2FjDCK
…tion

Adds detectCodexFileUploads() which discovers the active Codex rollout
by scanning ~/.codex/sessions via getCodexDiscoverySessionRoots() and
matching session_meta.cwd to the caller's CWD. Extracts input_image
blocks from the most recent user response_item record.

Wires CODEMIE_AGENT-aware dispatch in chat/index.ts: when CODEMIE_AGENT
equals 'codex', the new detector runs; otherwise the existing Claude
CODEMIE_SESSION_ID path is used (no regression).

Also adds CodexResponseItemMessage, CodexContentBlock, and extends
CodexEventMsg with images/local_images to make attachment types explicit.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
- CR-001: use imageOnlyIndex (increments only on input_image) for local_images
  lookups; the prior localImageIndex incremented on input_file blocks too,
  misaligning filename resolution in mixed file+image turns
- CR-002: find targetEventMsg in a second backward pass bounded to records at
  or before the response_item position, so a follow-up message no longer severs
  the filename chain from the attachment turn
- CR-003: estimate decoded size with base64 length math before the size guard,
  avoiding a full Buffer.from allocation that could OOM and silently drop
  valid attachments; also reject empty data URI payloads early

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Planning, review, and complexity-assessment artifacts for the Codex
file attachment detection implementation (sdlc-light flow).

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
@8nevil8

8nevil8 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Automated review — blockers

Focused on architecture, patterns, correctness, DRY, YAGNI. Minor issues intentionally omitted.

1. Existing Claude attachment path regressed — options.conversationId is dropped

src/cli/commands/assistants/chat/index.ts:102–109

The refactored Claude attachment path checks only process.env.CODEMIE_SESSION_ID, silently dropping options.conversationId that was previously included via const conversationId = options.conversationId || process.env.CODEMIE_SESSION_ID.

Any user who passes --conversation-id <id> without also having CODEMIE_SESSION_ID set now gets zero Claude attachment detection — a silent behavioral regression for the existing Claude path introduced while adding the Codex path.

Fix direction: Replace process.env.CODEMIE_SESSION_ID in the else branch with the already-computed conversationId variable.

2. CLI layer imports Codex plugin internals — architecture violation

src/cli/commands/assistants/chat/codexUploadsDetector.ts:19–21

CLI-layer module imports directly from deep Codex plugin internals: @/agents/plugins/codex/codex.paths.js and @/agents/plugins/codex/session/codex-user-prompt.js.

Per AGENTS.md the required flow is CLI → Registry → Plugin. A CLI module importing plugin internals bypasses the registry and couples the assistants command to Codex implementation details — any refactor of codex.paths.ts or codex-user-prompt.ts silently breaks the assistants command.

Fix direction: Expose a thin detectCodexFileUploads surface from the Codex plugin's public boundary (e.g. src/agents/plugins/codex/index.ts) and import only that contract in the CLI layer.

3. CodexSessionMetadata ships 5 unused fields (YAGNI)

src/agents/plugins/codex/codex-message-types.ts

CodexSessionMetadata declares projectPath, repository, branch, model, and cliVersion fields that are not read anywhere in this PR; only codexSessionId is validated by hasCodexMetadata / validateCodexMetadata.

Speculative schema fields shipped without consumers inflate the public contract surface and accumulate stale/incorrect types as Codex evolves.

Fix direction: Strip CodexSessionMetadata to the fields actually consumed (codexSessionId only), or move it to a dedicated planning doc until a consumer exists.

@codemie-ai

Copy link
Copy Markdown
Owner

[AUTO_CLOSE_WARNING] ⏰ This pull request is older than 14 days and will be automatically closed in 16 more days (when it reaches 30 days old)! To maintain this PR, either convert it to Draft or complete your changes and merge.

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.

3 participants