Files
openclaude/web
2e687df4ec fix(image): allow large/metadata-less screenshots through on resize failure (#1964) (#1970)
* fix(image): allow large/metadata-less screenshots through on resize failure (#1964)

Stop Alt+V paste from falling through to "No image found in clipboard" when the
native image connector returns undefined metadata or a screenshot exceeds 1568px.

- Reorder the metadata guard so an undefined metadata object is handled before
  reading metadata.format (previously threw TypeError and was swallowed into a
  "no image" result). Fall back to magic-byte format detection.
- Drop the `!overDim` gate from the catch-block allow-through so a
  dimensionally-large but base64-small image passes through. The API resizes
  images >1568px server-side; only the base64 size is a hard limit.
- Keep IMAGE_MAX_WIDTH/HEIGHT at 1568; only the crash and gate logic changed.
- Add imageResizer.test.ts covering the undefined-metadata path, the
  overDim<=5MB allow-through regression, the >5MB throw, and the happy path.

* fix(image): enforce many-image dimension limit in resize-failure fallback (#1970)

When the native image processor crashes, the fallback previously returned
the original buffer unchanged whenever its base64 size was <= 5MB, with no
dimension check. That let oversized images (e.g. 3840x2160 PNG/JPEG/WebP)
stay in conversation history and later break many-image requests with a 400
"image dimensions exceed ... many-image" error.

- Add IMAGE_MANY_IMAGE_MAX_WIDTH/HEIGHT (2000px) to apiLimits.
- In the resize-failure fallback, detect images exceeding the 2000px
  many-image limit via magic-byte dimension parsing (PNG IHDR, WebP VP8/
  VP8L, JPEG SOF0/2/3, GIF screen descriptor) and, when possible, downsample
  with platform Canvas APIs (awaiting a fully-decoded source). If downsample
  is unavailable or the result still exceeds the 5MB payload budget, reject
  with ImageResizeError instead of returning the oversized original.
- Unknown/unparseable dimensions fail safe (treated as exceeding the limit).
- Correct two pre-existing WebP dimension-offset bugs surfaced during review.

Adds regression tests for PNG/JPEG/WEBP/GIF over the many-image limit.

* fix(image): address review - media-type subtype, WebP offsets, Canvas DOM resolve, metadata-less budget

- ResizeResult.mediaType is the subtype ('png'/'jpeg'), not a full MIME
  type. The Canvas downsample branch wrongly returned 'image/png'/'image/jpeg';
  corrected to 'png'/'jpeg' to match every other return in the file.
- WebP dimension parser corrected against real sharp-encoded frames: VP8L
  transform header reads from byte 21 (bits[0..13] width, [14..27] height);
  VP8 lossy width/height read from bytes 26/28. Previous offsets were
  off and produced wrong dimensions.
- Canvas fallback now resolves createElement/Image from document (browser/
  Electron) with a globalThis fallback, so it actually engages where Canvas
  is available.
- metadata-less + oversized branch now tries progressive JPEG quality
  [80,60,40,20] and only returns an in-budget result; otherwise fails
  via the user-facing limit error instead of returning an oversized buffer that
  validateImagesForAPI would reject (the regression the PR recovers from).
- Add WebP/VP8L fixtures and tests asserting exact parsed dimensions and
  in-limit acceptance.

* fix(image): address 4 P1 review findings in resize-failure fallback

- Metadata-less branch now enforces the many-image 2000px limit via a
  shared enforceManyImageDimensionLimit helper (extracted from the
  catch-path guard), instead of letting compact high-resolution
  screenshots pass through unchecked.
- VP8 lossy WebP dimensions are stored directly (no -1 bias per RFC 6386);
  only VP8L uses minus-one encoding. Fixed parser and test fixture.
- Add VP8X (extended WebP) dimension parsing, needed for alpha/animation.
- DOM Image fallback now constructs with no args and assigns src after
  installing onload/onerror, since the Image constructor takes
  width/height, not a URL.

* fix(image): require raw dimensions before canvas downsample in many-image guard

readImageDimensions can return null for unparseable buffers; falling back to
0,0 made tryDownsampleToManyImageLimit treat the image as 1x1 and silently
downsample to a single pixel instead of throwing ImageResizeError.

* fix(image): keep in-budget 4K pastes when Canvas resize fallback is unavailable

The many-image 2000px guard rejected Windows clipboard screenshots whenever
image_processor_napi failed and Canvas was absent, recreating #1964. Pass
those images through, invoke DOM canvas methods with their owners, and skip
JPEG fill/TEM bytes before reading segment lengths.

* fix(image): reject API-hard 8000px edges and surface paste resize errors

Passthrough after a processor crash was sending images the API hard-rejects
and still mapping ImageResizeError to "No image found". Keep 4K single-image
paste, fall through from createImageBitmap to Image, and show the real
resize error on Alt+V.

* fix(image): document paste resize errors and test native rethrow

Clipboard ImageResizeError was still easy to treat as a missing reader on the native path, and the Alt+V vs generic notification contract had no focused tests. Share the paste error helpers, document the 5MB/8000px prompt, and cover native resize rejection without enabling NATIVE_CLIPBOARD_IMAGE.

* test(image): stabilize clipboard mock module identity

* fix(image): close resize fallback review gaps

* test(image): cover JPEG canvas fallback subtype

* fix(image): downsample before enforcing API edge

* fix(paste): settle rejected image path reads

* test(paste): isolate rejection regressions

* fix(image): report payload limit after fallback

* test(image): restore mocked modules after suites

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(images): enforce many-image limits at Anthropic request boundary

Count retained history and tool-result images, verify resized payload dimensions, and report actionable failures before dispatch. Exercise mock restoration deterministically.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(images): preserve resize failure diagnostics at request boundary

The many-image validation catch replaced the original image-processing
error with a generic RequestImageDimensionsError without logging it or
keeping a cause chain, so logAPIError and the assistant error path only
ever saw the wrapper message.

- Log the underlying processing failure via logError before wrapping.
- Rethrow an existing RequestImageDimensionsError unchanged instead of
  reconstructing it.
- Attach the original error as the wrapper's cause; ImageResizeError and
  RequestImageDimensionsError now forward ErrorOptions to super.
- Expand usesAnthropicImageLimits coverage to the reachable
  isFirstPartyBaseUrl:false input for bedrock/vertex/foundry.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix: count partner documents in request image limits

---------

Co-authored-by: jatmn <the@jat.mn>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-12 08:20:48 +08:00
..