feat: attach pasted image file paths like pasted images - #1123
feat: attach pasted image file paths like pasted images#1123josemonteiro wants to merge 4 commits into
Conversation
Typing an image path ("/Users/jose/Downloads/A-Cat.jpg what's this?") arrived as plain text, so the model only saw the image if it chose to call the read tool — whose upstream processImage/Photon resize pipeline can silently drop the image. Pasting the same image worked because the input transform attaches pasted images directly.
The clipboard-image input handler now extracts typed paths (absolute, ~/, relative, quoted) via the pure extractTypedImagePaths() util, validates them with readImageFileFromDisk(), and attaches them as ImageContent with the standard [Image #N] markers. Path images append after pasted/attached images so marker numbering and merge order are unchanged. Vision-less models keep plain text so the read tool remains the fallback.
Tests: 18 extractor unit tests, 7 input-handler wiring tests, 1 TUI e2e test (typed path -> marker visible -> image in request payload).
Co-Authored-By: Kimchi <noreply@kimchi.dev>
Kimchi Code Review
Summary📊 Review Score: 88/100 (overall code quality — 0 lowest, 100 highest) 🧪 Tests: yes — Comprehensive coverage: unit tests for the new 📝 Found 1 issue(s). See inline comments for details. What to expectKimchi will analyze the changes in this pull request and post:
The review typically completes within a few minutes. This comment will be updated once the review is ready. Interact with Kimchi
ConfigurationReviews are configured by your organization admin. Powered by Kimchi — AI-powered code review by CAST AI |
There was a problem hiding this comment.
📊 Review Score: 88/100 (overall code quality — 0 lowest, 100 highest)
⏱️ Estimated effort to review: 3/5 (1 = trivial, 5 = very complex)
🧪 Tests: yes — Comprehensive coverage: unit tests for the new extractTypedImagePaths utility cover absolute/relative/~/ paths, quoting, punctuation stripping, dedup, URLs, missing files, and directories; integration tests in clipboard-image.test.ts cover marker numbering, vision-less model fallback, and missing-path behavior; an E2E TUI test verifies the full user turn attaches the image to the completion request.
📝 Found 1 issue(s). See inline comments for details.
Detect bracketed-paste sequences in raw terminal input so typed image-path mentions are left alone while pasted or dragged paths still get attached as inline images. Updates unit and E2E tests. Co-Authored-By: Kimchi <noreply@kimchi.dev>
- Remove defensive optional chaining on ctx.ui.onTerminalInput (required method) - Extract teardownTerminalInput() helper to deduplicate session_start/shutdown cleanup - Reset lastInputWasPaste on non-paste terminal input so paste-then-type doesn't wrongly attach image paths (sticky flag bug) - Replace as-unknown-as cast in test mock with typed _mockTerminalInput handle - Add regression test for paste-then-type scenario Co-Authored-By: Kimchi <noreply@kimchi.dev>
The Enter key (\r) from terminal.submit() was hitting the else branch and clearing lastInputWasPaste before the input handler ran, so pasted image paths were never attached in the E2E test. Add hasTypedContent() to only reset the flag on actual printable typed content, ignoring control keys and CSI escape sequences. Co-Authored-By: Kimchi <noreply@kimchi.dev>
Summary
Pasting or dragging an image file path into the prompt now attaches the image inline just like pasting the image itself: the model receives it as an
ImageContentblock with the standard[Image #N]marker. Plain typed paths are left alone so filename mentions in prose don’t auto-attach.Why
Previously, a pasted/dropped image path arrived as plain text, so the model could only see the image if it chose to call the
readtool — whose upstream resize pipeline can silently drop the image, leaving the model blind with no surfaced error. Pasting the image itself worked because Kimchi’sinputtransform attaches pasted images directly. This change gives pasted/dropped paths the same parity while avoiding accidental attachments when the user simply types a filename.What changed
src/utils/typed-image-paths.ts(new) — pureextractTypedImagePaths(text, cwd): quote-aware tokenizer, image-extension filter, URL/file://rejection,~expansion + cwd resolution, size/type validation, dedup by resolved path preserving first-appearance order.src/extensions/clipboard-image.ts— onsession_start, subscribes to raw terminal input to detect bracketed-paste sequences; only when the current input originated from a paste or drop does theinputhandler convert matching image paths toImageContent. Path images append after existing attachments so marker numbering stays consistent. Vision-less models keep plain text as the fallback.src/utils/typed-image-paths.test.ts(new) — 18 extractor unit tests.src/extensions/clipboard-image.test.ts— wiring tests updated to simulate paste via bracketed-paste markers; added test verifying typed paths are left untouched.tests/e2e/tui/typed-image-path.test.ts— E2E updated to simulate paste/drop with bracketed-paste markers.Verification
pnpm run check(biome + tsc): clean