Skip to content

fix(agent): flatten content tool outputs to text for chatd - #405

Merged
ThomasK33 merged 2 commits into
mainfrom
issue-404-chatd-content-output
Oct 29, 2026
Merged

ThomasK33 merged 2 commits into
mainfrom
issue-404-chatd-content-output

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Closes #404

What changes

A client tool that returns a content output (AI SDK toModelOutput, or effect/ai toModelOutput through CoderAgentModel since #403) now submits one text string to chatd:

  • Text parts pass through, joined with newlines.
  • An inline text file ({ type: "text" } data) passes through as its text.
  • A URL file becomes a note with its URL: [image/png chart.png at https://…].
  • Any other file (base64, bytes, provider reference) becomes a note: [image/png swatch.png omitted] (the format of effect's Prompt.toolModelOutputToText).
  • A custom part becomes [custom content omitted].

Before, toolResultOutputToChatd forwarded the raw array. The model then read the file bytes as base64 characters, and never saw an image. A Uint8Array file payload went over the wire as a JSON object of byte indexes.

The README "Custom tools" section states the limit: the model does not see images in client tool results. To show the model a file, put it in a user message as a file part.

Why chatd cannot show the image (verified against source and live)

Source: coder/coder 8d643af (the commit dogfood runs, v2.38.4-devel+8d643afee4).

  1. codersdk.ToolResult has only tool_call_id, output (json.RawMessage), and is_error (codersdk/chats.go:871-876). A client cannot set is_media.
  2. CompleteRequiresAction stores each result as a tool-result part with Result: r.Output and no IsMedia (coderd/x/chatd/chatstate/transitions.go:1247-1253). Validation only checks json.Valid (chatstate/toolresults.go:13).
  3. toolResultPartToMessagePart sends string(part.Result) to the LLM as ToolResultOutputContentText (coderd/x/chatd/chatprompt/chatprompt.go:1379-1385, 1464-1472). Only IsMedia results (the computer-use screenshot shape {"data","mime_type","text"}) become media (chatprompt.go:1414-1430).

Live probe on dogfood (sonnet, client tool returns a text part plus a 4x4 solid red PNG):

Submitted output Stored result Model answer
Before (main 8d689ca) the raw array, PNG as base64 the same array RECEIVED: TEXT, COLOR: UNKNOWN
After (this PR) "Swatch label: PROBE-404-LABEL\n[image/png swatch.png omitted]" the same string RECEIVED: NOTE, COLOR: UNKNOWN

Both probe chats are archived.

Showing images from client tool results to the model needs a chatd change (for example an is_media-style field on ToolResult). That is out of scope here.

Validation

  • Node 26.12.1, pnpm 11.28.9: pnpm install --frozen-lockfile, pnpm -r build, pnpm check, pnpm -r test (agent 379, sandbox 193, provider 22, release-please-ai 13, effect 101 unit), pnpm publint, pnpm attw: all pass.
  • The new unit test fails on main and passes with the fix.
  • The advisor pass found that the first head (3b56489) dropped inline text files and file URLs, which reached the model before. ac8b577 keeps them. Gates and live e2e 7/7 rerun on ac8b577.
  • Live agent e2e against dogfood: 7/7. No unarchived chats left.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

chatd stores a client tool result as opaque JSON and gives it to the
model as text. A `content` output (from a tool's `toModelOutput`) reached
the model as a JSON array with the file bytes as base64 characters.

Keep the text parts, replace each file part with an
"[<mediaType> <filename> omitted]" note, and submit one string.

Closes #404

Signed-off-by: Thomas Kosiewski <tk@coder.com>

---
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

Change-Id: Ic94476e1bfc5933b2a18442dc3a3dcf49a6b1c07
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T08:22:44.052556Z ac8b577 Manual request
🔒 Security Review ✅ Completed 2026-10-29T08:24:12.067400Z ac8b577 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 3b564897a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 3b564897a3

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

An inline text file (`{ type: "text" }` data) now passes through as text,
and a URL file keeps its URL in the note. Both reached the model before
this branch as part of the raw JSON array.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

Change-Id: I034d3bf0abec212700176931e1516e31edba5c8f
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: ac8b57774c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: ac8b57774c

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 7edc552 Oct 29, 2026
7 checks passed
@ThomasK33
ThomasK33 deleted the issue-404-chatd-content-output branch October 29, 2026 08:28
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.

agent: verify chatd handling of content tool result outputs (toModelOutput)

1 participant