{
  "schemaVersion": 1,
  "updatedAt": "2026-10-15",
  "protocol": {
    "acp": {
      "version": 1,
      "imageFields": ["data", "mimeType"],
      "optionalFields": ["uri"],
      "requiredCapability": "agentCapabilities.promptCapabilities.image",
      "commonLimits": "unknown",
      "evidence": ["image-input-agentclientprotocol-agent-client-protocol-schema-v1-schema-json"]
    }
  },
  "contracts": [
    {
      "harness": "codex",
      "variant": "terminal",
      "default": true,
      "verifiedAt": "2026-10-15",
      "surface": "terminal",
      "support": {
        "clipboard": "native",
        "fileInput": "native",
        "structuredInput": "native",
        "imageOnlyAdmission": "native",
        "pixelPersistence": "native",
        "modelPixelDelivery": "native"
      },
      "evidence": [
        "image-input-openai-codex-codex-rs-app-server-protocol-schema-typescript-v2-UserInput-ts",
        "image-input-openai-codex-codex-rs-app-server-src-image_url-rs",
        "image-input-openai-codex-codex-rs-app-server-src-request_processors-turn_processor-rs",
        "image-input-openai-codex-codex-rs-core-src-context_manager-normalize-rs",
        "image-input-openai-codex-codex-rs-core-tests-suite-image_rollout-rs",
        "image-input-openai-codex-codex-rs-exec-src-lib-rs",
        "image-input-openai-codex-codex-rs-protocol-src-local_media-rs",
        "image-input-openai-codex-codex-rs-protocol-src-models-rs",
        "image-input-openai-codex-codex-rs-tui-src-bottom_pane-chat_composer-rs",
        "image-input-openai-codex-codex-rs-tui-src-chatwidget-image_submission-rs",
        "image-input-openai-codex-codex-rs-tui-src-clipboard_paste-rs",
        "image-input-openai-codex-codex-rs-tui-src-keymap-rs",
        "image-input-openai-codex-codex-rs-utils-image-src-lib-rs"
      ],
      "references": [
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server-protocol/schema/typescript/v2/UserInput.ts",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server/src/image_url.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server/src/request_processors/turn_processor.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/core/src/context_manager/normalize.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/core/tests/suite/image_rollout.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/exec/src/lib.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/protocol/src/local_media.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/protocol/src/models.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/bottom_pane/chat_composer.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/chatwidget/image_submission.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/clipboard_paste.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/keymap.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/utils/image/src/lib.rs"
      ],
      "details": {
        "launchFlags": ["-i", "--image"],
        "pathHandling": "Pasted paths attach only after image dimensions can be read.",
        "formats": ["PNG", "JPEG", "GIF", "WebP"],
        "localResizeMaxPixels": 2048,
        "exec": "Use a prompt; exec fork rejects images without a prompt."
      },
      "conditions": [
        "Native clipboard needs OS access; terminal bytes do not upload pixels.",
        "Persisted rollout images are subject to compaction and ephemeral-session retention.",
        "Retained details and top-level references include the 0.160.1 source snapshot; newer reader answers identify their own source revisions. Do not treat all fields as a single 0.162.1 source audit."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ],
      "answers": {
        "paste": {
          "status": "supported",
          "answer": "Copy an image (or an image file in Finder or Explorer), then press `Ctrl+V` in the composer; `Alt+V` and `Ctrl+Alt+V` also work, and the shortcut list shows `Ctrl+Alt+V` under WSL. Codex reads the system clipboard, saves it as a temporary PNG and inserts an `[Image #1]` placeholder. Your terminal's own paste (`Cmd+V`, `Ctrl+Shift+V`) only sends text, so it attaches an image only if the pasted text is a path to an image file.",
          "reference": "https://github.com/openai/codex/blob/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs/tui/src/chatwidget/interaction.rs#L156-L172",
          "evidence": [
            "steering-codex-codex-rs-tui-src-chatwidget-interaction-rs",
            "steering-codex-codex-rs-tui-src-keymap-rs",
            "image-input-codex-codex-rs-tui-src-bottom-pane-shortcut-overlay-rs",
            "image-input-codex-codex-rs-tui-src-clipboard-paste-rs",
            "image-input-codex-learn-chatgpt-com-docs-image-inputs-surface-cli"
          ],
          "notes": [
            "Under WSL, if the Linux clipboard has no image Codex runs PowerShell to fetch the image from the Windows clipboard.",
            "Image paste does not work on Android/Termux, and over SSH it reads the clipboard of the machine running Codex, not your local one.",
            "`Ctrl+V` is a reserved key: you cannot rebind it in `tui.keymap`."
          ]
        },
        "file": {
          "status": "supported",
          "answer": "Start with `codex -i shot.png \"prompt\"` or `--image a.png,b.png` (comma-separated or repeated). Inside the TUI, paste or drag-and-drop a single image path (quoted, shell-escaped, `file://` and Windows paths work) or pick a `.png`, `.jpg`, `.jpeg`, `.gif` or `.webp` file from the `@` file search. Each becomes an `[Image #N]` attachment. An image path you just type as plain text is not attached; it stays text that the agent can open with `view_image`.",
          "reference": "https://learn.chatgpt.com/docs/cli/reference#global-flags",
          "evidence": [
            "image-input-codex-learn-chatgpt-com-docs-cli-reference",
            "image-input-codex-codex-rs-utils-cli-src-shared-options-rs",
            "image-input-codex-codex-rs-tui-src-bottom-pane-chat-composer-paste-input-rs",
            "steering-codex-codex-rs-tui-src-bottom-pane-chat-composer-rs",
            "image-input-codex-learn-chatgpt-com-docs-image-inputs-surface-cli"
          ],
          "notes": [
            "A pasted or dropped path only attaches if Codex can read the image's dimensions; otherwise it is inserted as text.",
            "The top-level `-i` takes several values, so a prompt written right after it can be read as another image path. Put the prompt first or end the image list with `--`."
          ]
        },
        "headless": {
          "status": "supported",
          "answer": "Yes: `codex exec -i shot.png \"Explain this error\"` (or `--image a.png,b.png`), and `codex exec resume --last -i shot.png \"follow-up\"` or `codex exec fork <id> -i ...` for later turns. Stdin is read only as prompt text, so you can't pipe image bytes. Pass image files with `-i` and the prompt as an argument or on stdin.",
          "reference": "https://learn.chatgpt.com/docs/cli/reference#codex-exec",
          "evidence": [
            "image-input-codex-learn-chatgpt-com-docs-cli-reference",
            "image-input-codex-codex-rs-exec-src-lib-rs",
            "image-input-codex-codex-rs-exec-src-cli-rs"
          ],
          "notes": [
            "`codex exec fork` with `-i` but no prompt fails with `Forking with images requires a prompt`.",
            "`codex debug prompt-input -i shot.png \"text\"` prints the model input Codex would build, without running a turn."
          ]
        },
        "api": {
          "status": "supported",
          "answer": "App Server `turn/start` (and `turn/steer`) accepts input items `{ \"type\": \"localImage\", \"path\": \"/abs/shot.png\" }` and `{ \"type\": \"image\", \"url\": \"data:image/png;base64,...\" }` or `{ \"type\": \"image\", \"fileId\": \"...\" }`, each with an optional `detail` of `auto`, `high` or `original`. The TypeScript SDK takes `{ type: \"local_image\", path }` entries and passes them to `codex exec --image`. The Python SDK has `LocalImageInput(path)` and `ImageInput(url)`.",
          "reference": "https://learn.chatgpt.com/docs/app-server#turns",
          "evidence": [
            "codex-appserver-docs",
            "image-input-codex-dex-rs-app-server-protocol-schema-typescript-v2-userinput-ts",
            "steering-codex-codex-rs-app-server-src-request-processors-turn-processor-rs",
            "image-input-codex-codex-rs-app-server-src-image-url-rs",
            "image-input-codex-sdk-typescript-readme-md",
            "image-input-codex-sdk-typescript-src-thread-ts",
            "image-input-codex-sdk-python-src-openai-codex-inputs-py",
            "image-input-codex-src-codexacpclient-ts"
          ],
          "notes": [
            "Remote `http(s)` image URLs are rejected with `remote image URLs are not supported; use an inline data URL instead`, even though the App Server docs show an `https://` example.",
            "`localImage` paths are read on the machine running the app server. The TUI connected to a remote app server sends local images inline, up to 32 MiB per message.",
            "ACP is available through the separate `codex-acp` adapter. It turns ACP image blocks into `image` items, and the turn fails if a block has an `http(s)` URI. It also rejects images when the current model is text-only."
          ]
        },
        "formats": {
          "status": "supported",
          "answer": "PNG, JPEG, GIF and WebP are accepted. PNG, JPEG and WebP are sent unchanged when no resize is needed, and anything else, including GIF, is re-encoded as PNG. Images are scaled down to at most 2048 px on the long side and 2,500 32-px tiles, so roughly 1600x1600 for a square image, and the model is told the original and new sizes. With `detail: original` on models that support it, the limit is 6000 px and 10,000 tiles. There is no attachment count limit, and files over 1 GiB are refused.",
          "reference": "https://github.com/openai/codex/blob/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs/utils/image/src/lib.rs#L24-L84",
          "evidence": [
            "image-input-codex-codex-rs-utils-image-src-lib-rs",
            "image-input-codex-codex-rs-utils-image-cargo-toml",
            "image-input-codex-codex-rs-core-src-image-preparation-rs",
            "image-input-codex-codex-rs-core-src-context-image-resize-notice-rs",
            "transcripts-codex-codex-rs-protocol-src-models-rs",
            "image-input-codex-learn-chatgpt-com-docs-image-inputs-surface-cli"
          ],
          "notes": [
            "An unreadable or unsupported file (for example SVG, BMP or HEIC) does not stop the turn. The model gets a text note saying the image is invalid or unsupported.",
            "`detail: low` is not supported, and the image is replaced with a note asking for `high`, `original` or `auto`.",
            "Clipboard pastes are always saved as PNG."
          ]
        },
        "imageOnly": {
          "status": "supported",
          "answer": "Yes in the TUI: attach or paste an image and press Enter with no text, and the message is sent as just `[Image #1]`. In App Server, a `turn/start` whose `input` contains only an image item is accepted, since only an empty list is refused. `codex exec` still needs a prompt argument or stdin text.",
          "reference": "https://github.com/openai/codex/blob/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs/tui/src/bottom_pane/chat_composer.rs#L11346-L11367",
          "evidence": [
            "steering-codex-codex-rs-tui-src-bottom-pane-chat-composer-rs",
            "image-input-codex-codex-rs-tui-src-chatwidget-input-submission-rs",
            "steering-codex-codex-rs-app-server-src-request-processors-turn-processor-rs",
            "image-input-codex-codex-rs-exec-src-lib-rs"
          ]
        },
        "model": {
          "status": "supported",
          "answer": "If the model's catalog entry doesn't list image input, the TUI refuses the attachment with `Model <name> does not support image inputs. Remove images or switch models.` and keeps your draft. On a model switch, path pastes stop attaching. Images already in the history (or sent through exec or the API) are replaced with the text `image content omitted because you do not support image input` before each request, and `view_image` refuses to run.",
          "reference": "https://github.com/openai/codex/blob/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs/tui/src/chatwidget/settings.rs#L361-L385",
          "evidence": [
            "image-input-codex-codex-rs-tui-src-chatwidget-settings-rs",
            "steering-codex-codex-rs-tui-src-chatwidget-interaction-rs",
            "image-input-codex-codex-rs-tui-src-chatwidget-input-submission-rs",
            "image-input-codex-codex-rs-core-src-context-manager-normalize-rs",
            "image-input-codex-codex-rs-core-src-context-unsupported-media-rs",
            "image-input-codex-codex-rs-core-src-tools-handlers-view-image-rs",
            "codex-appserver-docs"
          ],
          "notes": [
            "If the model catalog can't be read, the TUI assumes the model supports images."
          ]
        },
        "saved": {
          "status": "supported",
          "answer": "Yes. Each image is saved inline as a base64 data URL inside the session rollout under `~/.codex/sessions`, so `codex resume` and `codex fork` send the same pixels again, still labelled with their original path. Local compaction keeps only the text of earlier messages and drops their images. OpenAI-hosted (remote) compaction can keep recent images within a 64,000-token retained-message budget.",
          "reference": "https://github.com/openai/codex/blob/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs/core/tests/suite/image_rollout.rs#L150-L245",
          "evidence": [
            "image-input-codex-codex-rs-core-tests-suite-image-rollout-rs",
            "compaction-codex-codex-rs-core-src-compact-rs",
            "compaction-codex-codex-rs-core-src-compact-remote-v2-rs",
            "steering-codex-codex-rs-features-src-lib-rs",
            "image-input-codex-codex-rs-core-src-image-preparation-rs"
          ],
          "notes": [
            "`--ephemeral` sessions are not written to disk, so nothing is kept.",
            "Rollouts with screenshots can get large because the bytes are stored inline."
          ]
        },
        "readTool": {
          "status": "supported",
          "answer": "Yes. The built-in `view_image` tool (feature `view_image`, on by default) lets the agent load any local image file by path and send its pixels to the model. On some models it can ask for `detail: original` to keep full resolution. There's no built-in screenshot tool, so the agent captures screenshots with a shell command (for example `screencapture` or Playwright) and then opens them with `view_image`.",
          "reference": "https://github.com/openai/codex/blob/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs/core/src/tools/handlers/view_image_spec.rs#L16-L50",
          "evidence": [
            "image-input-codex-codex-rs-core-src-tools-handlers-view-image-spec-rs",
            "mcp-codex-codex-rs-core-src-tools-spec-plan-rs",
            "steering-codex-codex-rs-features-src-lib-rs",
            "image-input-codex-codex-rs-core-src-tools-handlers-view-image-rs",
            "codex-appserver-docs"
          ],
          "notes": [
            "The tool is unavailable on text-only models, and App Server clients see each use as an `imageView` item."
          ]
        }
      },
      "observedVersion": "0.162.1"
    },
    {
      "harness": "codex",
      "variant": "0.160.1-app-server",
      "default": false,
      "verifiedAt": "2026-10-08",
      "surface": "app-server",
      "support": {
        "clipboard": "unknown",
        "fileInput": "native",
        "structuredInput": "native",
        "imageOnlyAdmission": "native",
        "pixelPersistence": "native",
        "modelPixelDelivery": "unknown"
      },
      "evidence": [
        "image-input-openai-codex-codex-rs-app-server-protocol-schema-typescript-v2-UserInput-ts",
        "image-input-openai-codex-codex-rs-app-server-src-image_url-rs",
        "image-input-openai-codex-codex-rs-app-server-src-request_processors-turn_processor-rs",
        "image-input-openai-codex-codex-rs-core-src-context_manager-normalize-rs",
        "image-input-openai-codex-codex-rs-core-tests-suite-image_rollout-rs",
        "image-input-openai-codex-codex-rs-exec-src-lib-rs",
        "image-input-openai-codex-codex-rs-protocol-src-local_media-rs",
        "image-input-openai-codex-codex-rs-protocol-src-models-rs",
        "image-input-openai-codex-codex-rs-tui-src-bottom_pane-chat_composer-rs",
        "image-input-openai-codex-codex-rs-tui-src-chatwidget-image_submission-rs",
        "image-input-openai-codex-codex-rs-tui-src-clipboard_paste-rs",
        "image-input-openai-codex-codex-rs-tui-src-keymap-rs",
        "image-input-openai-codex-codex-rs-utils-image-src-lib-rs"
      ],
      "references": [
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server-protocol/schema/typescript/v2/UserInput.ts",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server/src/image_url.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server/src/request_processors/turn_processor.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/core/src/context_manager/normalize.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/core/tests/suite/image_rollout.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/exec/src/lib.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/protocol/src/local_media.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/protocol/src/models.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/bottom_pane/chat_composer.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/chatwidget/image_submission.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/clipboard_paste.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/keymap.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/utils/image/src/lib.rs"
      ],
      "details": {
        "methods": ["turn/start", "turn/steer"],
        "inputTypes": ["image", "localImage"],
        "imageSources": ["inline data URL", "provider fileId"],
        "localImagePath": "Server-host filesystem path",
        "httpUrls": "rejected",
        "remoteTuiAggregateDataUrlBudgetBytes": 33554432
      },
      "conditions": [
        "32 MiB is a remote TUI preparation budget, not a universal App Server limit.",
        "Invalid local images can become text errors; unsupported model modalities can strip images."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ]
    },
    {
      "harness": "codex",
      "variant": "acp-adapter-1.11.0-codex-0.160.1",
      "default": false,
      "verifiedAt": "2026-10-08",
      "surface": "acp-adapter",
      "support": {
        "clipboard": "unknown",
        "fileInput": "unknown",
        "structuredInput": "adapter",
        "imageOnlyAdmission": "unknown",
        "pixelPersistence": "unsupported",
        "modelPixelDelivery": "unknown"
      },
      "evidence": [
        "image-input-agentclientprotocol-codex-acp-src-CodexAcpClient-ts",
        "image-input-agentclientprotocol-codex-acp-src-CodexAcpServer-ts",
        "image-input-openai-codex-codex-rs-app-server-protocol-schema-typescript-v2-UserInput-ts",
        "image-input-openai-codex-codex-rs-app-server-src-image_url-rs",
        "image-input-openai-codex-codex-rs-app-server-src-request_processors-turn_processor-rs",
        "image-input-openai-codex-codex-rs-core-src-context_manager-normalize-rs",
        "image-input-openai-codex-codex-rs-core-tests-suite-image_rollout-rs",
        "image-input-openai-codex-codex-rs-exec-src-lib-rs",
        "image-input-openai-codex-codex-rs-protocol-src-local_media-rs",
        "image-input-openai-codex-codex-rs-protocol-src-models-rs",
        "image-input-openai-codex-codex-rs-tui-src-bottom_pane-chat_composer-rs",
        "image-input-openai-codex-codex-rs-tui-src-chatwidget-image_submission-rs",
        "image-input-openai-codex-codex-rs-tui-src-clipboard_paste-rs",
        "image-input-openai-codex-codex-rs-tui-src-keymap-rs",
        "image-input-openai-codex-codex-rs-utils-image-src-lib-rs"
      ],
      "references": [
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server-protocol/schema/typescript/v2/UserInput.ts",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server/src/image_url.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/app-server/src/request_processors/turn_processor.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/core/src/context_manager/normalize.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/core/tests/suite/image_rollout.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/exec/src/lib.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/protocol/src/local_media.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/protocol/src/models.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/bottom_pane/chat_composer.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/chatwidget/image_submission.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/clipboard_paste.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/tui/src/keymap.rs",
        "https://github.com/openai/codex/blob/d27764b82f7118f674371e6d6e76271d9d606edb/codex-rs/utils/image/src/lib.rs",
        "https://github.com/agentclientprotocol/codex-acp/blob/effb0fe670a49dfbb5071764b5f8a2e3c09e2393/src/CodexAcpClient.ts",
        "https://github.com/agentclientprotocol/codex-acp/blob/effb0fe670a49dfbb5071764b5f8a2e3c09e2393/src/CodexAcpServer.ts"
      ],
      "details": {
        "adapterRevision": "effb0fe670a49dfbb5071764b5f8a2e3c09e2393",
        "advertisedImage": true,
        "embeddedContext": true,
        "conversion": "Prefers HTTP(S)/data uri over constructing a data URL from data.",
        "replay": "Images become text links, not a round-trip pixel archive."
      },
      "conditions": [
        "Omit remote HTTP(S) uri because the pinned Codex runtime rejects it.",
        "Adapter and runtime versions are separate constraints."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ]
    },
    {
      "harness": "claude-code",
      "variant": "terminal",
      "default": true,
      "verifiedAt": "2026-10-15",
      "surface": "terminal",
      "support": {
        "clipboard": "native",
        "fileInput": "native",
        "structuredInput": "native",
        "imageOnlyAdmission": "unknown",
        "pixelPersistence": "partial",
        "modelPixelDelivery": "native"
      },
      "evidence": ["image-input-docs-2", "image-input-docs-3"],
      "references": [
        "https://code.claude.com/docs/en/common-workflows#work-with-images",
        "https://code.claude.com/docs/en/interactive-mode#general-controls"
      ],
      "details": {
        "delivery": ["native clipboard", "drag/drop", "path in prompt"],
        "pathHandling": "Plain text path may require a later tool read."
      },
      "conditions": ["Dated unversioned documentation; no clipboard or image-only probe."],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ],
      "answers": {
        "paste": {
          "status": "supported",
          "answer": "Yes. Press `Ctrl+V` to paste an image from the clipboard (also `Cmd+V` in iTerm2; `Alt+V` on Windows; on WSL both `Ctrl+V` and `Alt+V` work). The image appears as an `[Image #N]` chip at the cursor so you can refer to it in your text, and you can rebind the key through the `chat:imagePaste` action in `~/.claude/keybindings.json`.",
          "reference": "https://code.claude.com/docs/en/interactive-mode#general-controls",
          "evidence": [
            "claude-interactive-docs",
            "image-input-claude-code-code-claude-com-docs-en-keybindings",
            "image-input-claude-code-code-claude-com-docs-en-common-workflows",
            "image-input-claude-code-changelog-md"
          ],
          "notes": [
            "Linux needs `xclip` or `wl-paste` to read the clipboard; WSL2 falls back to PowerShell.",
            "Over SSH the remote machine cannot see your local clipboard; copy the file across with `scp` and give Claude its path."
          ]
        },
        "file": {
          "status": "supported",
          "answer": "Drag an image file onto the terminal, or paste or type its path (for example `Analyze this image: /path/to/screenshot.png`). A dragged or pasted image path is attached as an image straight away; a path Claude only sees as text, or an `@path/to/image.png` mention, is opened with the Read tool, which also hands Claude the pixels. There is no launch flag for attaching a local image to an interactive session.",
          "reference": "https://code.claude.com/docs/en/common-workflows#work-with-images",
          "evidence": [
            "image-input-claude-code-code-claude-com-docs-en-common-workflows",
            "claude-permissions-docs",
            "image-input-claude-code-changelog-md"
          ],
          "notes": [
            "A `Read(...)` deny rule that matches the path stops a pasted or dragged image from being attached.",
            "Pasted and attached images are also saved to a per-session `images/` folder in Claude Code's temp directory, so Claude can open or copy them as files."
          ]
        },
        "headless": {
          "status": "supported",
          "answer": "Yes. The simplest route is to name the file in the prompt, as in `claude -p \"Describe the UI in ./screenshot.png\"`, and Claude reads it with the Read tool. To send the image itself, use `claude -p --input-format stream-json --output-format stream-json --verbose` and write one JSON user message per line on stdin whose `message.content` holds an `image` block with base64 data. A plain-text `-p` prompt cannot carry image bytes.",
          "reference": "https://code.claude.com/docs/en/cli-reference#cli-flags",
          "evidence": [
            "claude-cli-reference-docs",
            "image-input-docs-1",
            "steering-claude-code-docs-en-headless",
            "memory-claude-code-code-claude-com-docs-en-errors"
          ],
          "notes": [
            "Each stream-json message must be a single newline-terminated JSON line.",
            "Reading an image path in `-p` needs Read permission for that path, for example via `--allowedTools Read`."
          ]
        },
        "api": {
          "status": "supported",
          "answer": "Yes, through the Agent SDK (TypeScript and Python) in streaming-input mode: yield a user message whose `message.content` is an array such as `[{type: \"text\", text: \"...\"}, {type: \"image\", source: {type: \"base64\", media_type: \"image/png\", data: \"<base64>\"}}]`. Single-message (plain string) prompts cannot attach images. If an image block's `source` is missing or malformed, Claude gets a text note such as `[Image could not be processed: ...]` and the session continues.",
          "reference": "https://code.claude.com/docs/en/agent-sdk/streaming-vs-single-mode#implementation-example",
          "evidence": ["image-input-docs-1", "image-input-claude-code-changelog-md"],
          "notes": [
            "Claude Code has no built-in ACP server; the SDK and `--input-format stream-json` are the structured inputs."
          ]
        },
        "formats": {
          "status": "supported",
          "answer": "PNG, JPEG, GIF and WebP are sent as images; macOS TIFF screenshots, WSL BMP clipboards and CMYK JPEGs are converted first. Claude Code downscales anything over 2000x2000 px and compresses pasted, attached and Read-tool images to the same token budget, re-encoding a still-over-500KB result as JPEG. Requests are capped at 32 MB, and when the API's per-request image count or Claude Code's total image size cap would be exceeded, the oldest images and PDFs are dropped in batches.",
          "reference": "https://code.claude.com/docs/en/errors#unable-to-resize-image",
          "evidence": [
            "memory-claude-code-code-claude-com-docs-en-errors",
            "image-input-claude-code-code-claude-com-docs-en-tools-reference",
            "claude-prompt-caching-docs",
            "image-input-claude-code-changelog-md"
          ],
          "notes": [
            "An image Claude Code cannot decode or resize is refused with a message telling you to convert it to PNG, JPEG, GIF or WebP.",
            "PDFs are separate: Read takes up to 20 pages per call for PDFs over 10 pages, and the API limit is 100 pages / 20 MB."
          ]
        },
        "imageOnly": {
          "status": "unknown",
          "answer": "The terminal docs don't say whether a prompt holding only a pasted image is accepted; a paste does leave an `[Image #N]` chip in the prompt text. The VS Code extension explicitly lets you send an attached image without typing anything, and Remote Control accepts images sent without a caption.",
          "reference": "https://github.com/anthropics/claude-code/blob/94506ff0d3bd9185f1752da36e968775021fb48f/CHANGELOG.md#L2074",
          "evidence": ["image-input-claude-code-changelog-md"]
        },
        "model": {
          "status": "unknown",
          "answer": "Claude Code runs Claude models, which all accept images, so there is no documented fallback for a model that can't see them. If the API refuses an image (too large, too many, or unprocessable), Claude Code swaps that image for a text placeholder or drops the oldest images and retries, and Claude no longer sees them.",
          "reference": "https://code.claude.com/docs/en/errors#request-rejected-as-unprocessable-by-the-model",
          "evidence": [
            "memory-claude-code-code-claude-com-docs-en-errors",
            "image-input-claude-code-changelog-md"
          ],
          "notes": [
            "Behaviour with a non-Claude model behind `ANTHROPIC_BASE_URL` or an LLM gateway is not documented."
          ]
        },
        "saved": {
          "status": "partial",
          "answer": "The session transcript (`~/.claude/projects/<project>/<session>.jsonl`) keeps every message, including image content, so `--resume`, `--continue` and forks reload it; the separate copies of pasted images in the temp `images/` folder are deleted after `cleanupPeriodDays`. Images do not survive compaction: `/compact` or auto-compaction replaces them with a text summary, and in long sessions the oldest images are also dropped from requests once size or count limits are hit.",
          "reference": "https://code.claude.com/docs/en/claude-directory#application-data",
          "evidence": [
            "transcripts-claude-code-code-claude-com-docs-en-claude-directory",
            "memory-claude-code-code-claude-com-docs-en-errors",
            "claude-prompt-caching-docs",
            "image-input-claude-code-changelog-md"
          ],
          "notes": [
            "With `--no-session-persistence` or `CLAUDE_CODE_SKIP_PROMPT_HISTORY` nothing is written, so there is nothing to resume.",
            "If Claude needs a dropped or compacted image again, share it again."
          ]
        },
        "readTool": {
          "status": "supported",
          "answer": "Yes. The Read tool returns PNG, JPEG and other image files as visual content Claude can see, resized and recompressed to fit the model's limits, and reads PDFs page by page. Images returned by MCP tools (for example browser screenshots from Claude in Chrome or Playwright) are shown inline and also saved to the session's `tool-results` folder so Claude can crop or reuse the full-size file.",
          "reference": "https://code.claude.com/docs/en/tools-reference#read-tool-behavior",
          "evidence": [
            "image-input-claude-code-code-claude-com-docs-en-tools-reference",
            "claude-mcp-docs",
            "image-input-claude-code-changelog-md"
          ],
          "notes": [
            "For fine detail in a large screenshot, ask Claude to crop the region first (for example with ImageMagick), since the copy it sees may be downscaled.",
            "Read detects the format from the file bytes, not the extension; a non-image saved as `.png` is read as text."
          ]
        }
      },
      "observedVersion": "2.1.299"
    },
    {
      "harness": "claude-code",
      "variant": "documentation-agent-sdk-2026-10-08",
      "default": false,
      "verifiedAt": "2026-10-08",
      "surface": "agent-sdk",
      "support": {
        "clipboard": "unknown",
        "fileInput": "unknown",
        "structuredInput": "native",
        "imageOnlyAdmission": "unknown",
        "pixelPersistence": "unknown",
        "modelPixelDelivery": "unknown"
      },
      "evidence": ["image-input-docs-0", "image-input-docs-1"],
      "references": [
        "https://code.claude.com/docs/en/agent-sdk/sessions",
        "https://code.claude.com/docs/en/agent-sdk/streaming-vs-single-mode"
      ],
      "details": {
        "delivery": "AsyncIterable SDKUserMessage.message.content image blocks",
        "encoding": "source.type=base64, media_type, raw base64 data",
        "stringPromptDirectAttachments": "unsupported",
        "persistence": "Resume/fork documented; persistSession=false disables disk persistence; exact pixel retention unknown."
      },
      "conditions": [
        "Streaming input required for direct attachments.",
        "Messages API backend limits are not an established Agent SDK upload ceiling.",
        "Malformed image sources can become text explanations."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ]
    },
    {
      "harness": "cursor-agent",
      "variant": "2026.09.02-c22c1a3-acp",
      "default": false,
      "verifiedAt": "2026-10-08",
      "surface": "acp",
      "support": {
        "clipboard": "unknown",
        "fileInput": "unknown",
        "structuredInput": "native",
        "imageOnlyAdmission": "unknown",
        "pixelPersistence": "unknown",
        "modelPixelDelivery": "unknown"
      },
      "evidence": ["cursor-acp-image-initialize", "cursor-acp-docs"],
      "references": ["https://cursor.com/docs/cli/acp"],
      "details": {
        "initializeResponse": {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "protocolVersion": 1,
            "agentCapabilities": {
              "loadSession": true,
              "mcpCapabilities": {
                "http": true,
                "sse": true
              },
              "promptCapabilities": {
                "audio": false,
                "embeddedContext": false,
                "image": true
              },
              "sessionCapabilities": {
                "list": {}
              }
            },
            "authMethods": [
              {
                "id": "cursor_login",
                "name": "Cursor Login",
                "description": "Authenticate using existing Cursor login credentials. Run 'agent login' first if not logged in."
              }
            ]
          }
        },
        "modelCalls": 0,
        "sessionCreated": false
      },
      "conditions": [
        "Only capability advertisement verified; no decoding or pixel-delivery probe."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ]
    },
    {
      "harness": "cursor-agent",
      "variant": "terminal",
      "default": true,
      "verifiedAt": "2026-10-15",
      "surface": "terminal",
      "support": {
        "clipboard": "native",
        "fileInput": "native",
        "structuredInput": "native",
        "imageOnlyAdmission": "partial",
        "pixelPersistence": "native",
        "modelPixelDelivery": "native"
      },
      "evidence": ["cursor-cli-using-docs"],
      "references": ["https://cursor.com/docs/cli/using"],
      "details": {
        "delivery": "@ file context",
        "pathHandling": "File reference until attachment/read behavior is verified."
      },
      "conditions": ["Editor image-upload claims do not establish CLI clipboard support."],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ],
      "answers": {
        "paste": {
          "status": "supported",
          "answer": "Press `Ctrl+V` in the prompt to paste a clipboard image; it appears as an `[Image #1]` token. On macOS, a normal `Cmd+V` that delivers an empty paste (clipboard holds only an image) also attaches it, and a file copied in Finder attaches the actual image. In CLI 2026.10.12 the macOS reader takes PNG, JPEG or TIFF (TIFF is converted to PNG with `sips`); Linux paste works on Wayland and X11.",
          "reference": "https://cursor.com/docs/cli/changelog#april-2026",
          "evidence": ["cursor-cli-changelog"],
          "notes": [
            "Windows clipboard support is listed in the January 2026 changelog, but image paste on Windows is not described separately."
          ]
        },
        "file": {
          "status": "supported",
          "answer": "Drag an image file into the terminal, paste its path, or type `@path/to/shot.png`. In CLI 2026.10.12, a path that ends in `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.bmp`, `.tif`, `.tiff`, `.heic` or `.heif` (quoted, `~/`, absolute or `file://`) becomes an `[Image #N]` attachment. The hidden `--image <path>` launch flag only works in print mode (`-p`).",
          "reference": "https://cursor.com/docs/cli/using#selecting-context",
          "evidence": ["cursor-cli-using-docs", "cursor-cli-changelog"],
          "notes": [
            "On macOS, screenshots dragged straight from the floating thumbnail can be unreadable (permission denied); the CLI suggests `Cmd+Shift+Ctrl+4` and then pasting.",
            "Only PNG, JPEG, GIF and WebP contents are accepted when the message is sent, so HEIC, BMP or TIFF files dropped in fail with an unsupported-type error."
          ]
        },
        "headless": {
          "status": "supported",
          "answer": "The documented way is to put the path in the prompt, e.g. `agent -p \"Describe ./screenshot.png\"`, and the agent opens the file with its read tool. CLI 2026.10.12 also has a hidden flag, `--image <path>` (repeatable), that attaches the image directly to a `-p` prompt. A text prompt is still required, because print mode exits with `No prompt provided for print mode` if it is empty.",
          "reference": "https://cursor.com/docs/cli/headless#working-with-images",
          "evidence": [
            "image-input-cursor-agent-cursor-com-docs-cli-headless",
            "cursor-cli-changelog"
          ],
          "notes": [
            "`--image` is missing from `agent --help` and is labelled internal-only in the binary, so it may change without notice.",
            "There is no documented stdin or stream-json input for images."
          ]
        },
        "api": {
          "status": "supported",
          "answer": "Over ACP (`agent acp`), the CLI advertises `promptCapabilities.image: true` and accepts `{ \"type\": \"image\", \"data\": \"<base64>\", \"mimeType\": \"image/png\" }` blocks in `session/prompt`. The TypeScript and Python SDKs take `agent.send({ text, images: [{ data, mimeType }] })` or `{ url }`. The Cloud Agents API accepts `prompt.images` (up to 5, 15 MB each, PNG/JPEG/GIF/WebP).",
          "reference": "https://cursor.com/docs/sdk/typescript#sending-messages",
          "evidence": [
            "image-input-cursor-agent-cursor-com-docs-sdk-typescript",
            "cursor-acp-docs",
            "image-input-cursor-agent-cursor-com-docs-sdk-python",
            "cursor-cloud-api-docs"
          ],
          "notes": [
            "In CLI 2026.10.12, ACP image blocks are passed through as raw bytes without the CLI's own resizing, and blocks that carry only a `uri` with no `data` arrive empty.",
            "ACP also sends a `cursor/generate_image` notification when the agent generates an image."
          ]
        },
        "formats": {
          "status": "supported",
          "answer": "Images attached to a message must be PNG, JPEG, GIF or WebP (checked by file signature). In CLI 2026.10.12, attachments are scaled to fit 2048x2048 and re-encoded as JPEG (quality 80), with quality and size stepped down until the image is at most 5 MiB. Images the agent opens with its read tool are re-encoded as WebP at no more than 1280 px on the long side and 1 MiB.",
          "reference": "https://cursor.com/docs/cli/headless#working-with-images",
          "evidence": [
            "image-input-cursor-agent-cursor-com-docs-cli-headless",
            "cursor-cloud-api-docs"
          ],
          "notes": [
            "The CLI sets no per-message image count limit; the Cloud Agents API allows at most 5 images per prompt."
          ]
        },
        "imageOnly": {
          "status": "partial",
          "answer": "In the interactive CLI you can submit a prompt that contains only an attached image, but its `[Image #1]` placeholder is sent as the message text. Print mode (`-p`) rejects an empty text prompt even when `--image` is given. Over ACP, a `session/prompt` that contains only an image block is accepted.",
          "reference": "https://cursor.com/docs/cli/acp#request-flow",
          "evidence": ["cursor-acp-docs", "image-input-cursor-agent-cursor-com-docs-cli-headless"]
        },
        "model": {
          "status": "unknown",
          "answer": "Cursor does not document what happens when the selected model cannot read images. In CLI 2026.10.12, the CLI never checks the model's image support itself; images go to Cursor's backend with the request, and any refusal comes from there.",
          "reference": "https://cursor.com/docs/models-and-pricing#model-pricing",
          "evidence": ["compaction-cursor-agent-cursor-com-docs-models-and-pricing"]
        },
        "saved": {
          "status": "partial",
          "answer": "Image bytes are stored with the user message in the local chat store, so `/rewind` puts a turn's images back in the prompt and `--resume`/`--continue` reload a conversation that still contains them. Since 2026.10.08, images attached to a message queued during an interrupted turn are sent on the next turn. Cursor does not document whether images survive `/summarize`; plain-text transcripts show them only as `[Image]`.",
          "reference": "https://cursor.com/docs/cli/changelog#may-20-2026",
          "evidence": ["cursor-cli-changelog", "cursor-cli-using-docs"]
        },
        "readTool": {
          "status": "supported",
          "answer": "Yes. The read tool returns image pixels for `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.bmp` and `.ico` files (and files detected as images by content), so you can just mention a path. In CLI 2026.10.12 the read tool re-encodes them as WebP at up to 1280 px and 1 MiB, and reports an error for files that cannot be decoded.",
          "reference": "https://cursor.com/docs/cli/headless#how-it-works",
          "evidence": [
            "image-input-cursor-agent-cursor-com-docs-cli-headless",
            "cursor-cli-changelog"
          ],
          "notes": [
            "Attached and generated images show as inline previews in terminals that support inline images."
          ]
        }
      },
      "observedVersion": "2026.10.12-d47ca18"
    },
    {
      "harness": "opencode",
      "variant": "1.x",
      "default": false,
      "verifiedAt": "2026-10-15",
      "surface": "terminal",
      "evidence": ["image-input-opencode-packages-tui-src-clipboard-ts-a2ef71"],
      "references": [
        "https://github.com/anomalyco/opencode/blob/733b2c627e5aec39cb33cc22373bdce33053ef2c/packages/tui/src/clipboard.ts#L30-L75"
      ],
      "conditions": ["Source and documentation review; no live image submission."],
      "unknowns": ["No paid inference or live clipboard probe was performed."],
      "details": {},
      "answers": {
        "paste": {
          "status": "supported",
          "answer": "Press `Ctrl+V` in the prompt (the `input_paste` keybind) to paste a clipboard image; it appears as an `[Image 1]` chip. On macOS it reads the clipboard as PNG via `osascript`, on Windows/WSL via PowerShell, and on Linux via `wl-paste` (Wayland) or `xclip` (X11). If your terminal's own paste shortcut sends an empty paste for an image-only clipboard, OpenCode reads the image the same way.",
          "reference": "https://github.com/anomalyco/opencode/blob/733b2c627e5aec39cb33cc22373bdce33053ef2c/packages/tui/src/clipboard.ts#L30-L75",
          "evidence": [
            "image-input-opencode-packages-tui-src-clipboard-ts",
            "delegation-opencode-packages-tui-src-config-keybind-ts",
            "compaction-opencode-packages-tui-src-component-prompt-index-tsx",
            "image-input-opencode-opencode-ai-docs-keybinds",
            "opencode-storage-docs"
          ],
          "notes": [
            "On Linux, install `wl-clipboard` or `xclip`; without them only text paste works."
          ]
        },
        "file": {
          "status": "supported",
          "answer": "Paste or drag a single image path (or `file://` URL) into the prompt and it becomes an `[Image N]` attachment; `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp` and `.avif` are recognised by extension, `.svg` is inserted as text and `.pdf` as `[PDF N]`. You can also `@`-mention an image file; it is read with the Read tool, which recognises the image from its bytes and attaches the pixels. Outside the TUI, use `opencode run -f <file>`.",
          "reference": "https://github.com/anomalyco/opencode/blob/733b2c627e5aec39cb33cc22373bdce33053ef2c/packages/tui/src/component/prompt/index.tsx#L1183-L1204",
          "evidence": [
            "compaction-opencode-packages-tui-src-component-prompt-index-tsx",
            "image-input-opencode-packages-tui-src-component-prompt-local-attachment-ts",
            "mcp-opencode-packages-tui-src-component-prompt-autocomplete-tsx",
            "steering-opencode-packages-opencode-src-session-prompt-ts",
            "opencode-tui-docs"
          ]
        },
        "headless": {
          "status": "supported",
          "answer": "Use `opencode run -f shot.png \"what is wrong here?\"`; `-f/--file` can be repeated. A text message is required, and piped stdin is read as message text, not as an image. With `--attach <url>` the file is uploaded inline and must be 10 MiB or less.",
          "reference": "https://opencode.ai/docs/cli/#run-1",
          "evidence": [
            "opencode-cli-stable-docs",
            "steering-opencode-packages-opencode-src-cli-cmd-run-ts"
          ]
        },
        "api": {
          "status": "supported",
          "answer": "Send a file part in `parts` to `session.prompt` / `POST /session/:id/message` (or `prompt_async`): `{ type: \"file\", mime: \"image/png\", url: \"data:image/png;base64,...\" | \"file:///abs/path.png\", filename? }`. ACP (`opencode acp`) advertises `image: true` and accepts `image` content blocks with base64 `data` or a `data:` URI.",
          "reference": "https://opencode.ai/docs/server/#messages",
          "evidence": [
            "opencode-server-docs",
            "image-input-opencode-packages-sdk-js-src-gen-types-gen-ts",
            "transcripts-opencode-opencode-ai-docs-sdk",
            "image-input-opencode-packages-opencode-src-acp-service-ts",
            "image-input-opencode-packages-opencode-src-acp-content-ts"
          ],
          "notes": ["`http(s)` image URLs are not fetched; send a `data:` or `file:` URL."]
        },
        "formats": {
          "status": "supported",
          "answer": "Clipboard images arrive as PNG; the Read tool and models get PNG, JPEG, GIF and WebP (PDF also attaches). Before sending, images over 2000x2000 px or 5 MiB of base64 are scaled down and re-encoded as PNG or JPEG; change this with `attachment.image` (`auto_resize`, `max_width`, `max_height`, `max_base64_bytes`) in `opencode.json`. There is no image count limit, but `opencode run --attach` caps each file at 10 MiB.",
          "reference": "https://opencode.ai/docs/config/#image-attachments",
          "evidence": [
            "instructions-opencode-opencode-ai-docs-config",
            "image-input-opencode-packages-opencode-src-image-image-ts",
            "instructions-opencode-packages-opencode-src-tool-read-ts"
          ],
          "notes": [
            "With `auto_resize: false`, an oversized pasted image fails the prompt instead of being resized.",
            "xAI only accepts PNG, JPEG and WebP; other formats are skipped for that provider."
          ]
        },
        "imageOnly": {
          "status": "supported",
          "answer": "Yes. Paste an image and press Enter; the prompt is never empty because the `[Image 1]` label is part of the text, so the model gets that label plus the image. `opencode run` still needs a text message.",
          "reference": "https://github.com/anomalyco/opencode/blob/733b2c627e5aec39cb33cc22373bdce33053ef2c/packages/tui/src/component/prompt/index.tsx#L1224-L1260",
          "evidence": [
            "compaction-opencode-packages-tui-src-component-prompt-index-tsx",
            "steering-opencode-packages-opencode-src-cli-cmd-run-ts"
          ]
        },
        "model": {
          "status": "supported",
          "answer": "The message is still sent, but the image is replaced by the text `ERROR: Cannot read \"<file>\" (this model does not support image input). Inform the user.`, so the model tells you it cannot see it. Image support comes from each model's `capabilities.input` metadata; pick a vision model with `/models`.",
          "reference": "https://github.com/anomalyco/opencode/blob/733b2c627e5aec39cb33cc22373bdce33053ef2c/packages/opencode/src/provider/transform.ts#L409-L445",
          "evidence": [
            "compaction-opencode-packages-opencode-src-provider-transform-ts",
            "opencode-tui-docs"
          ]
        },
        "saved": {
          "status": "partial",
          "answer": "Pasted and attached images are saved in the session as base64 `data:` URLs, so resuming (`-c`, `-s`) or forking (`--fork`) sends the pixels again. Compaction summarises them as `[Attached image/png: name]` text, though images in the most recent turns it keeps stay intact. Pruned old tool results lose their image attachments.",
          "reference": "https://github.com/anomalyco/opencode/blob/733b2c627e5aec39cb33cc22373bdce33053ef2c/packages/opencode/src/session/compaction.ts#L54-L83",
          "evidence": [
            "steering-opencode-packages-opencode-src-session-compaction-ts",
            "steering-opencode-packages-opencode-src-session-prompt-ts",
            "transcripts-opencode-packages-opencode-src-session-session-ts",
            "steering-opencode-packages-opencode-src-session-message-v2-ts"
          ]
        },
        "readTool": {
          "status": "supported",
          "answer": "Yes. The built-in `read` tool detects PNG, JPEG, GIF and WebP (and PDF) from file contents and returns the image to the model, resized to the same limits; images it cannot shrink are left out with a note. For providers that don't accept images inside tool results, OpenCode re-sends them in a follow-up user message. There is no built-in screenshot tool.",
          "reference": "https://github.com/anomalyco/opencode/blob/733b2c627e5aec39cb33cc22373bdce33053ef2c/packages/opencode/src/tool/read.ts#L300-L325",
          "evidence": [
            "instructions-opencode-packages-opencode-src-tool-read-ts",
            "compaction-opencode-packages-opencode-src-session-processor-ts",
            "steering-opencode-packages-opencode-src-session-message-v2-ts"
          ]
        }
      },
      "support": {
        "clipboard": "native",
        "fileInput": "native",
        "structuredInput": "native",
        "imageOnlyAdmission": "native",
        "pixelPersistence": "native",
        "modelPixelDelivery": "native"
      },
      "observedVersion": "1.19.0"
    },
    {
      "harness": "opencode",
      "variant": "2.x",
      "default": true,
      "verifiedAt": "2026-10-15",
      "surface": "terminal",
      "support": {
        "clipboard": "native",
        "fileInput": "native",
        "structuredInput": "native",
        "imageOnlyAdmission": "native",
        "pixelPersistence": "partial",
        "modelPixelDelivery": "native"
      },
      "evidence": [
        "image-input-anomalyco-opencode-packages-cli-src-acp-content-ts",
        "image-input-anomalyco-opencode-packages-cli-src-acp-service-ts",
        "image-input-anomalyco-opencode-packages-tui-src-clipboard-ts",
        "image-input-anomalyco-opencode-packages-tui-src-component-prompt-index-tsx",
        "image-input-anomalyco-opencode-packages-tui-src-component-prompt-local-attachment-ts",
        "image-input-docs-7"
      ],
      "references": [
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/cli/src/acp/content.ts",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/cli/src/acp/service.ts",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/tui/src/clipboard.ts",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/tui/src/component/prompt/index.tsx",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/tui/src/component/prompt/local-attachment.ts",
        "https://opencode.ai/docs/cli/#run-1"
      ],
      "details": {
        "launchFlags": ["run --file"],
        "localAttachmentBudgetBytes": 20971520,
        "maxParsedPaths": 32,
        "acceptedSuffixes": ["PNG", "JPEG", "GIF", "WebP", "AVIF", "PDF", "SVG"],
        "svgDelivery": "text",
        "emptyPrompt": "New session rejects blank input; existing session follows empty-submit/model-selection path."
      },
      "conditions": [
        "V2 source does not establish V1 behavior.",
        "Accepted file suffixes do not establish provider image formats; include explicit text."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ],
      "answers": {
        "paste": {
          "status": "supported",
          "answer": "Press `Ctrl+V` (`prompt.paste`) to paste a clipboard image as an `[Image N]` chip. OpenTUI's host clipboard is asked for `image/png` first, then text. An empty bracketed paste from the terminal also triggers an image read. Press `<leader>i` (default leader `ctrl+x`) to preview the attached images.",
          "reference": "https://github.com/anomalyco/opencode/blob/042d0832f5f3a73eaa039a58a7d565776f6d4290/packages/tui/src/clipboard.ts#L22-L52",
          "evidence": [
            "image-input-opencode-packages-tui-src-clipboard-ts",
            "steering-opencode-packages-tui-src-config-keybind-ts",
            "steering-opencode-packages-tui-src-component-prompt-index-tsx"
          ]
        },
        "file": {
          "status": "supported",
          "answer": "Paste or drag image paths into the prompt: one path, or up to 32 space-separated paths totalling 20 MiB, using the extensions `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.avif` and `.pdf` (`.svg` goes in as text). `@`-mentioning a file attaches it by `file://` URI, and the server detects images from their bytes. For scripted runs, use `opencode run -f`.",
          "reference": "https://github.com/anomalyco/opencode/blob/042d0832f5f3a73eaa039a58a7d565776f6d4290/packages/tui/src/component/prompt/local-attachment.ts#L1-L70",
          "evidence": [
            "image-input-opencode-packages-tui-src-component-prompt-local-attachment-ts",
            "steering-opencode-packages-tui-src-component-prompt-index-tsx",
            "delegation-opencode-packages-tui-src-component-prompt-autocomplete-tsx",
            "mcp-opencode-opencode-ai-v2-docs-attachments"
          ]
        },
        "headless": {
          "status": "supported",
          "answer": "`opencode run -f shot.png \"describe this\"` sends up to 100 files of 10 MiB each, inlined as `data:` URLs. A text message is required, and stdin only adds to the message text.",
          "reference": "https://github.com/anomalyco/opencode/blob/042d0832f5f3a73eaa039a58a7d565776f6d4290/packages/cli/src/commands/commands.ts#L408-L412",
          "evidence": [
            "opencode-2-0-29-source",
            "image-input-opencode-packages-cli-src-run-run-ts",
            "mcp-opencode-opencode-ai-v2-docs-cli"
          ]
        },
        "api": {
          "status": "supported",
          "answer": "`POST /api/session/{sessionID}/prompt` (SDK `session.prompt`) takes `{ text, files: [{ uri, name?, description? }] }` with `uri` set to an absolute `file:` URL or a `data:` URL. `http(s)` is rejected. ACP advertises `image: true`: `image` blocks with base64 `data` become attachments, while an image block that has only an `http(s)` URI is passed as a text link.",
          "reference": "https://opencode.ai/v2/docs/attachments/#syntax",
          "evidence": [
            "mcp-opencode-opencode-ai-v2-docs-attachments",
            "instructions-opencode-opencode-ai-v2-docs-api",
            "steering-opencode-packages-core-src-session-prompt-ts",
            "image-input-opencode-packages-cli-src-acp-content-ts",
            "image-input-opencode-packages-cli-src-acp-service-ts"
          ]
        },
        "formats": {
          "status": "partial",
          "answer": "Only PNG, JPEG, GIF and WebP reach the model. AVIF, BMP and other binaries are accepted but left out of the request, and SVG is sent as text. Each attachment can be up to 20 MiB decoded. Images over 2000x2000 px or 5 MiB of base64 are scaled down and re-encoded as PNG or JPEG; set this under `media.image` (`auto_resize`, `max_width`, `max_height`, `max_base64_bytes`). If a request holds more than 25 MiB of images, the oldest are replaced with a placeholder until it is under 15 MiB.",
          "reference": "https://opencode.ai/v2/docs/attachments/#formats",
          "evidence": [
            "mcp-opencode-opencode-ai-v2-docs-attachments",
            "image-input-opencode-packages-core-src-session-runner-to-llm-message-ts",
            "steering-opencode-packages-core-src-session-model-request-ts",
            "steering-opencode-packages-core-src-session-prompt-ts"
          ],
          "notes": [
            "v1's `attachment.image` config key is `media.image` in v2.",
            "The media type is detected from the file bytes, so renaming a file does not change it."
          ]
        },
        "imageOnly": {
          "status": "supported",
          "answer": "In the TUI, yes: the `[Image N]` label counts as prompt text, so paste and press Enter. A truly blank prompt is refused in a new session. `opencode run` needs a message, and the API's `text` field is required.",
          "reference": "https://github.com/anomalyco/opencode/blob/042d0832f5f3a73eaa039a58a7d565776f6d4290/packages/tui/src/component/prompt/index.tsx#L1119-L1121",
          "evidence": [
            "steering-opencode-packages-tui-src-component-prompt-index-tsx",
            "instructions-opencode-opencode-ai-v2-docs-api"
          ]
        },
        "model": {
          "status": "supported",
          "answer": "The image is replaced by `ERROR: Cannot read \"<file>\" (this model does not support image input). Inform the user.`, so the turn still runs and the model says it can't see it. The docs warn to pick a model with image input first.",
          "reference": "https://github.com/anomalyco/opencode/blob/042d0832f5f3a73eaa039a58a7d565776f6d4290/packages/core/src/session/model-request.ts#L132-L147",
          "evidence": [
            "steering-opencode-packages-core-src-session-model-request-ts",
            "mcp-opencode-opencode-ai-v2-docs-attachments"
          ]
        },
        "saved": {
          "status": "partial",
          "answer": "Attachments are stored as base64 in the session message, so resuming or forking keeps the pixels. After summary compaction, the kept recent conversation is converted to text and images become `[image/png omitted]`, so the model can't see older images any more. Native (OpenAI Responses) compaction keeps recent attachments alongside an encrypted checkpoint.",
          "reference": "https://opencode.ai/v2/docs/compaction/#summaries",
          "evidence": [
            "transcripts-opencode-opencode-ai-v2-docs-compaction",
            "compaction-opencode-packages-core-src-session-compaction-ts"
          ]
        },
        "readTool": {
          "status": "supported",
          "answer": "Yes. `read` detects PNG, JPEG, GIF, WebP and PDF from file contents, reads up to 20 MiB, and passes them to the model after the same resize step. Images that can't be decoded or resized are left out with a note. There is no built-in screenshot tool, though the browser tool can capture pages.",
          "reference": "https://opencode.ai/v2/docs/tools/#read",
          "evidence": [
            "mcp-opencode-opencode-ai-v2-docs-tools",
            "image-input-opencode-packages-core-src-tool-read-filesystem-ts",
            "image-input-opencode-packages-core-src-tool-ts"
          ]
        }
      },
      "observedVersion": "2.0.29"
    },
    {
      "harness": "opencode",
      "variant": "2.0.20-acp",
      "default": false,
      "verifiedAt": "2026-10-08",
      "surface": "acp",
      "support": {
        "clipboard": "unknown",
        "fileInput": "unknown",
        "structuredInput": "native",
        "imageOnlyAdmission": "unknown",
        "pixelPersistence": "partial",
        "modelPixelDelivery": "unknown"
      },
      "evidence": [
        "image-input-anomalyco-opencode-packages-cli-src-acp-content-ts",
        "image-input-anomalyco-opencode-packages-cli-src-acp-service-ts",
        "image-input-anomalyco-opencode-packages-tui-src-clipboard-ts",
        "image-input-anomalyco-opencode-packages-tui-src-component-prompt-index-tsx",
        "image-input-anomalyco-opencode-packages-tui-src-component-prompt-local-attachment-ts"
      ],
      "references": [
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/cli/src/acp/content.ts",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/cli/src/acp/service.ts",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/tui/src/clipboard.ts",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/tui/src/component/prompt/index.tsx",
        "https://github.com/anomalyco/opencode/blob/84c9be93a56304a108f1a22df0c5d62c26d5b6ca/packages/tui/src/component/prompt/local-attachment.ts"
      ],
      "details": {
        "advertisedImage": true,
        "embeddedContext": true,
        "conversion": "Prefers data; HTTP(S)/data uri fallback only when data is empty.",
        "replay": "Stored data URLs reconstruct images; file URLs remain references."
      },
      "conditions": [
        "File links require downstream access; replay does not guarantee external file lifetime."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ]
    },
    {
      "harness": "pi",
      "variant": "terminal",
      "default": true,
      "verifiedAt": "2026-10-15",
      "surface": "terminal",
      "support": {
        "clipboard": "native",
        "fileInput": "native",
        "structuredInput": "native",
        "imageOnlyAdmission": "native",
        "pixelPersistence": "native",
        "modelPixelDelivery": "partial"
      },
      "evidence": [
        "image-input-earendil-works-pi-packages-coding-agent-docs-cli-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-keybindings-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-rpc-commands-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-session-format-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-usage-md",
        "image-input-earendil-works-pi-packages-coding-agent-src-core-agent-session-ts",
        "image-input-earendil-works-pi-packages-coding-agent-src-utils-image-resize-core-ts"
      ],
      "references": [
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/cli.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/keybindings.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/rpc-commands.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/session-format.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/usage.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/src/core/agent-session.ts",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/src/utils/image-resize-core.ts"
      ],
      "details": {
        "delivery": ["native clipboard", "@path", "compatible terminal paste/drag"],
        "persistence": "Session content retains normalized image blocks, potentially transformed."
      },
      "conditions": ["Clipboard depends on the runtime platform and environment."],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ],
      "answers": {
        "paste": {
          "status": "supported",
          "answer": "Press `Ctrl+V` in the editor (`Alt+V` on Windows and WSL; rebind via `app.clipboard.pasteImage`). Pi saves the clipboard image (PNG, JPEG, WebP or GIF; other formats such as BMP are converted to PNG) to a temp file and inserts that file's path into your prompt, and the agent opens it with its `read` tool. On Linux it uses `wl-paste` or `xclip`, on WSL it falls back to PowerShell for Windows screenshots, and in Termux only text can be pasted.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/src/modes/interactive/interactive-mode.ts#L3152-L3192",
          "evidence": [
            "steering-pi-kages-coding-agent-src-modes-interactive-interactive-mode-ts",
            "steering-pi-packages-coding-agent-docs-keybindings-md",
            "image-input-pi-packages-coding-agent-src-core-keybindings-ts",
            "image-input-pi-packages-coding-agent-src-utils-clipboard-image-ts",
            "image-input-pi-packages-coding-agent-docs-termux-md"
          ],
          "notes": [
            "A pasted image arrives as a temp-file path in the prompt text, not as an attachment, so the model sees the pixels only after it reads that file.",
            "If the clipboard holds files copied in Finder (macOS), `Ctrl+V` pastes their paths instead."
          ]
        },
        "file": {
          "status": "supported",
          "answer": "At launch, pass `@path` arguments, e.g. `pi @screenshot.png \"What is wrong here?\"`: image files are attached to the first prompt as real images. Inside the session, type `@` to fuzzy-search a file or drag the file into a terminal that pastes its path; that puts the path in your prompt and the agent opens it with `read`.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/docs/cli.md#invocation-and-output",
          "evidence": [
            "pi-docs-1-2-0",
            "steering-pi-packages-coding-agent-docs-usage-md",
            "image-input-pi-packages-coding-agent-src-cli-file-processor-ts",
            "image-input-pi-packages-coding-agent-src-cli-args-ts"
          ],
          "notes": [
            "Only `@file` launch arguments become image attachments; paths typed, completed or dropped in the editor stay text."
          ]
        },
        "headless": {
          "status": "supported",
          "answer": "Yes: combine `@file` arguments with `-p`/`--print` or `--mode json`, e.g. `pi -p @mockup.png \"Build this layout\"`. Piped stdin is read as text and prepended to the prompt, so pipe the prompt and pass the image as `@file`. `--mode rpc` rejects `@file` arguments; send base64 images in RPC commands instead.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/docs/cli.md#invocation-and-output",
          "evidence": [
            "pi-docs-1-2-0",
            "transcripts-pi-packages-coding-agent-src-main-ts",
            "image-input-pi-packages-coding-agent-src-modes-print-mode-ts"
          ]
        },
        "api": {
          "status": "supported",
          "answer": "RPC mode (`pi --mode rpc`) accepts an optional `images` array on `prompt`, `steer` and `follow_up` commands, each `{\"type\":\"image\",\"data\":\"<base64>\",\"mimeType\":\"image/png\"}`. In the SDK, call `session.prompt(text, { images })`, `session.steer(text, images)` or `session.followUp(text, images)` with the same `ImageContent` objects. Pi has no ACP surface.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/docs/rpc-commands.md#prompt",
          "evidence": [
            "steering-pi-packages-coding-agent-docs-rpc-commands-md",
            "image-input-pi-packages-coding-agent-src-modes-rpc-rpc-types-ts",
            "steering-pi-packages-coding-agent-src-core-agent-session-ts",
            "transcripts-pi-packages-coding-agent-docs-message-types-md"
          ],
          "notes": [
            "Images sent with `steer`, `follow_up`, or a `prompt` that is queued during a run skip Pi's resize step and go to the provider as given."
          ]
        },
        "formats": {
          "status": "supported",
          "answer": "Files are detected by content as PNG, JPEG, GIF, WebP or BMP (BMP is converted to PNG). By default (`images.autoResize: true`) Pi shrinks images to fit 2000 x 2000 pixels and 4.5 MiB of base64, trying PNG and JPEG and lowering JPEG quality or dimensions until it fits, and tells the model the original size so it can scale coordinates. Per-model limits go in `models.json` under `inputLimits.images.resize` (`maxWidth`, `maxHeight`, `maxBytes`, `jpegQuality`); Pi does not enforce a count limit.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/docs/models.md#describe-model-input-and-caching",
          "evidence": [
            "image-input-pi-packages-coding-agent-docs-models-md",
            "steering-pi-packages-coding-agent-docs-settings-md",
            "image-input-pi-packages-coding-agent-src-utils-image-resize-core-ts",
            "image-input-pi-packages-coding-agent-src-utils-mime-ts",
            "steering-pi-packages-coding-agent-src-core-agent-session-ts"
          ],
          "notes": [
            "An image that cannot be converted or shrunk below the limit is dropped and replaced by a text note such as `[Image omitted: could not be resized below the inline image size limit.]`.",
            "Animated PNGs are not recognised as images."
          ]
        },
        "imageOnly": {
          "status": "supported",
          "answer": "Yes. `pi @screenshot.png` with no message sends the image with only a file-name tag, an RPC `prompt` with `\"message\": \"\"` and `images` is accepted, and in the terminal a pasted image path alone is a valid prompt. Pi drops the empty text part for OpenAI-compatible providers that reject it.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/src/core/agent-session.ts#L1907-L1916",
          "evidence": [
            "steering-pi-packages-coding-agent-src-core-agent-session-ts",
            "image-input-pi-packages-coding-agent-src-cli-file-processor-ts",
            "image-input-pi-packages-coding-agent-changelog-md"
          ]
        },
        "model": {
          "status": "supported",
          "answer": "Pi replaces each image with `(image omitted: model does not support images)` (or `(tool image omitted: ...)` for tool results) before sending to a model whose `input` lacks `image`, and the `read` tool adds a note that the image will be omitted. The images stay in the session, so switching to a vision model later sends them again. `pi --list-models` shows which models take images, and `images.blockImages: true` withholds images from every model.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/ai/src/api/transform-messages.ts#L12-L57",
          "evidence": [
            "image-input-pi-packages-ai-src-api-transform-messages-ts",
            "image-input-pi-packages-coding-agent-src-core-tools-read-ts",
            "image-input-pi-packages-coding-agent-src-core-sdk-ts",
            "image-input-pi-packages-coding-agent-src-cli-list-models-ts",
            "steering-pi-packages-coding-agent-docs-settings-md"
          ]
        },
        "saved": {
          "status": "partial",
          "answer": "Image blocks are stored as base64 inside the session JSONL (user attachments and `read`/tool results), so `pi -c`, `/resume` and forks send the same pixels again. Compaction summarises older turns as text only, so images before the kept recent window are lost from context. A pasted image is saved only as its temp-file path in your message; its pixels persist only if the agent read the file.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/docs/session-format.md#messages",
          "evidence": [
            "transcripts-pi-packages-coding-agent-docs-session-format-md",
            "transcripts-pi-packages-coding-agent-src-core-session-manager-ts",
            "compaction-pi-packages-coding-agent-src-core-compaction-utils-ts",
            "image-input-pi-packages-coding-agent-docs-models-md"
          ],
          "notes": [
            "Images are encoded once at their first resize; changing models later does not re-encode stored images."
          ]
        },
        "readTool": {
          "status": "supported",
          "answer": "Yes. The built-in `read` tool opens JPEG, PNG, GIF, WebP and BMP files and returns them to the model as image attachments, resized with the same rules as prompt images. This is how pasted and dragged image paths reach the model, and MCP resources and tool results that contain images are passed on as images too.",
          "reference": "https://github.com/earendil-works/pi/blob/06a466935536f8c9bcacb2d5efc0fc021be1bdff/packages/coding-agent/src/core/tools/read.ts#L133-L152",
          "evidence": [
            "image-input-pi-packages-coding-agent-src-core-tools-read-ts",
            "pi-docs-1-2-0",
            "mcp-pi-packages-coding-agent-docs-mcp-md"
          ]
        }
      },
      "observedVersion": "1.2.0"
    },
    {
      "harness": "pi",
      "variant": "0.99.1-rpc-sdk",
      "default": false,
      "verifiedAt": "2026-10-08",
      "surface": "rpc-sdk",
      "support": {
        "clipboard": "unknown",
        "fileInput": "unknown",
        "structuredInput": "native",
        "imageOnlyAdmission": "unknown",
        "pixelPersistence": "partial",
        "modelPixelDelivery": "unknown"
      },
      "evidence": [
        "image-input-earendil-works-pi-packages-coding-agent-docs-cli-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-keybindings-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-rpc-commands-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-session-format-md",
        "image-input-earendil-works-pi-packages-coding-agent-docs-usage-md",
        "image-input-earendil-works-pi-packages-coding-agent-src-core-agent-session-ts",
        "image-input-earendil-works-pi-packages-coding-agent-src-utils-image-resize-core-ts"
      ],
      "references": [
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/cli.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/keybindings.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/rpc-commands.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/session-format.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/docs/usage.md",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/src/core/agent-session.ts",
        "https://github.com/earendil-works/pi/blob/d86654abb8862e201933517d6f1fce9f88dd117f/packages/coding-agent/src/utils/image-resize-core.ts"
      ],
      "details": {
        "rpcMethods": ["prompt", "steer", "follow-up"],
        "rpcField": "images",
        "sdk": "Prompt image options",
        "resizeDefaults": {
          "width": 2000,
          "height": 2000,
          "base64Bytes": 4718592
        }
      },
      "conditions": [
        "Resize defaults have model-specific overrides and are not provider acceptance limits."
      ],
      "unknowns": [
        "No paid inference, native clipboard, PTY, image-only, remote filesystem, or transcript restart probes.",
        "Cursor CLI clipboard image transport, MIME/size limits, selected-model pixel delivery and image persistence remain unverified.",
        "Claude Agent SDK docs are dated observations, not a pinned SDK build or pixel-retention guarantee.",
        "OpenCode V2 source claims do not establish V1 behavior.",
        "Documented limits are surface-specific and do not establish provider acceptance for all model/backend combinations."
      ]
    }
  ],
  "dimensions": [
    "clipboard",
    "fileInput",
    "structuredInput",
    "imageOnlyAdmission",
    "pixelPersistence",
    "modelPixelDelivery"
  ]
}
