Skip to content

fix(session): keep images returned by tools in Claude Code and Codex sessions - #1254

Merged
SaulMoro merged 1 commit into
Tencent:mainfrom
jayhemnani9910:fix/claude-tool-result-images
Oct 21, 2026
Merged

SaulMoro merged 1 commit into
Tencent:mainfrom
jayhemnani9910:fix/claude-tool-result-images

Conversation

@jayhemnani9910

Copy link
Copy Markdown
Contributor

Summary

Images returned by a tool are dropped when a session is read, and fidelity doesn't count them as lost:

Both readers now put those images in a user message of their own right after the round's tool results. Targets with image support write them, the others show a placeholder like for any other image.

They get their own message because codebuddy, workbuddy and the CodeBuddy IDE history write a user message's text before its function_call_result lines, and Cursor starts a new turn for user content. With the image in the tool-result message, those targets put a user line between a tool call and its result. Claude Code writes each parallel tool_result as a separate record, so the reader holds the images until the last result of the round.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

  • npx tsc --noEmit passes
  • npm run lint passes
  • npx vitest run: 11258 passed; the same 5 tests fail on main without this change (cursor-bubble-range, cursor-user-images x2, submodule-checkout x2)
  • Added/updated tests for the change: claude-tool-result-images.test.ts (read, a parallel round with two images, write back to claude-code) and a Codex case in session-cmd.test.ts; all fail on main

teamai session migrate with HOME in a scratch dir and a 1x1 PNG returned by the tool:

-s claude-code -t codex   (Read returned the PNG)
main:  Fidelity: 100.0%   PNG in the Codex rollout: 0 lines
patch: Fidelity: 100.0%   PNG in the Codex rollout: 1 line

-s codex -t claude-code   (view_image returned the PNG)
main:  Fidelity: 92.5%    PNG in the Claude Code session: 0 lines
patch: Fidelity: 94.0%    PNG in the Claude Code session: 1 line

Related Issues

None.

Notes for Reviewers

The IR tool_result holds only text, so the image moves out of it. A claude-code to claude-code round trip puts the image in a user record after the tool_result instead of inside it. Targets without images (Cursor, OpenCode, CodeBuddy) show that message as a user turn with the placeholder, after the tool round.

@github-actions

Copy link
Copy Markdown

No findings in the scoped PR diff.

The PR description documents sufficient testing, including representative real-CLI migrations in both directions with PNG-preservation results. This satisfies the end-to-end testing requirement.

Review was read-only; I did not run, build, or install anything from the PR.

@SaulMoro
SaulMoro self-requested a review October 21, 2026 07:54

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

The PR keeps images that a tool returns when reading Claude Code (tool_result with an image part) and Codex (function_call_output with input_image items). Both readers move them into a separate user message after the round's tool results, so fidelity counts them and image-capable targets write them back. The earlier bot review reported no findings; nothing else is open.

Local checks, on the merge of cad23673 into origin/main (0da2be76, clean merge):

  • npx tsc --noEmit: pass (also on the head alone).
  • npm run lint: exit 0.
  • npx vitest run claude-tool-result-images session-cmd migrate fidelity-sweep agent-session-transcript session-search cursor-user-images scrub-session session-sync tclaude-tcodex save-session: 14 files, 235 tests passed.
  • Red/green: the PR's two test files on origin/main give 4 failed (all 4 new cases); on the merge they pass.
  • npm run build, then npm run test:e2e -- src/__tests__/e2e/session-migrate-opencode.test.ts: 1 passed.
  • Real CLI, teamai session migrate under a throwaway HOME with a 1x1 PNG returned by the tool:
    claude-code -> codex   main: 100.0%, PNG in rollout: 0   merge: 100.0%, PNG in rollout: 1
    codex -> claude-code   main:  92.5%, PNG in session: 0   merge:  94.0%, PNG in session: 1
    
    This matches the PR description. The written Claude Code session has tool_result, then a user record with the image, then the assistant reply. The Codex rollout has function_call_output, then a user input_image message.

I also checked the readers of the new image-only user message: fidelity (migrate.ts), the Claude Code, Codex and CodeBuddy IDE writers, scrub, the IR serializer, and the transcript/search index. None of them breaks. The extra user turn on targets that split turns at user messages is the trade-off described in the PR notes.

Findings

  • [P3 nit] src/session-flow/adapters/codex.ts:533 flushes the held images on any response_item that is not a tool output, including types the reader ignores (for example web_search_call). If such an item falls between two outputs of one round, the next output is appended to the image message (codex.ts:584). A local probe (call c1, call c2, output c1 with an image, web_search_call, output c2) gives user[tool_result], user[image, tool_result]. That is the mixed shape the PR avoids for codebuddy/workbuddy. Codex is not known to write a rollout like this, so this is optional. Flushing only on the types that start a new message (message, function_call, custom_tool_call, reasoning) would close it.

Not covered

  • Cursor, OpenCode, CodeBuddy, WorkBuddy and CodeBuddy IDE as targets: their clients are not installed here (claude-code -> codebuddy stops with "not installed" on main and on the PR). The unit tests cover them.
  • I did not resume a migrated session in the real Claude Code or Codex.
  • I did not run the full vitest suite or the full e2e suite.

Verdict: Approve.

@SaulMoro
SaulMoro merged commit 2d507f2 into Tencent:main Oct 21, 2026
17 checks passed
@jayhemnani9910

jayhemnani9910 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

This merged before the nit fix landed, so it is in #1260: held images are flushed only on message, function_call, custom_tool_call and reasoning, so an ignored item like web_search_call between two outputs keeps the round's results together. Added a test for that order.

legend91019 added a commit to legend91019/teamai-cli that referenced this pull request Oct 21, 2026
Walternater pushed a commit to Walternater/teamai-cli that referenced this pull request Nov 2, 2026
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