Skip to content

feature: deliver host-captured images to vision-capable models - #91

Merged
DevMando merged 1 commit into
mainfrom
codex/agent-image-input
Sep 6, 2026
Merged

feature: deliver host-captured images to vision-capable models#91
DevMando merged 1 commit into
mainfrom
codex/agent-image-input

Conversation

@DevMando

Copy link
Copy Markdown
Owner

What this adds

The app can already tell whether the selected model accepts images. It had no way to actually send one, so a screenshot feature could not be built on top of it. This adds the delivery half.

A host captures an image and hands it over. It is refused unless the model genuinely reports image support, the content really is an image, and it fits a size limit. A text-only model is told plainly that it cannot receive images rather than being handed something it will quietly ignore.

Why this was done

Without this, the agent could take a screenshot and still have no way to look at it. This is the missing piece that makes visual checking possible at all.

How delivery works, and why it is not simpler

A tool result is text. An image cannot be returned inside one. So a captured image is added to the conversation as a normal message once the current step finishes, and the model is given another step to actually examine it. The number of such extensions per request is capped, so a model cannot loop taking pictures of itself.

Delivered images are removed from the conversation once the request finishes. Keeping them would re-upload several megabytes on every later message and crowd out the room the model needs to think. What persists is the model's written conclusion about what it saw.

Verification

716 automated tests pass on .NET 8 and .NET 10. This includes a test that asserts an attached image travels all the way to the model server in the format it expects, and a test that the image is delivered and then cleared from the conversation. Refusal cases for text-only models, unknown capability, oversized payloads, and non-image content are covered.

Scope

This delivers images. It does not capture them. The screenshot feature that uses this is a companion change in MandoCode.Desktop, which also registers its tool here so repeated captures are never served from a cached result.

Model image support was already detected but there was no way to send an image, so a
screenshot tool could not exist. Hosts can now call TryAttachImage, which refuses unless
the model reports vision support, the payload is an image, and it fits the size cap.
A tool result is text, so an image cannot ride back inside one. A queued image is added
to history as a real user message once the current turn finishes, and the turn is extended
so the model actually looks at it, bounded by MaxImageDeliveriesPerTurn.
Delivered images are retracted when the user turn ends. An image is evidence for the turn
that captured it; leaving it in history would re-upload megabytes on every later request
and crowd out the context the model needs. The model's written conclusion persists instead.
Registers screenshot_desktop_preview as a live preview tool so its results are never served
from the result cache, and adds a transport seam so delivery is asserted end to end without
a live daemon. A probe test pins the wire contract that image content reaches Ollama's
images array, which the whole feature rests on.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@DevMando