Skip to content

feat(chat/ios): inline image resize + EXIF metadata strip for attachments#73710

Closed
devicemanager wants to merge 6 commits into
openclaw:mainfrom
devicemanager:pr/ios-chat-image-resize-exif
Closed

feat(chat/ios): inline image resize + EXIF metadata strip for attachments#73710
devicemanager wants to merge 6 commits into
openclaw:mainfrom
devicemanager:pr/ios-chat-image-resize-exif

Conversation

@devicemanager

@devicemanager devicemanager commented Apr 28, 2026

Copy link
Copy Markdown

Summary

Wires client-side resize and JPEG re-encode into the chat composer attachment path, built on the existing JPEGTranscoder in OpenClawKit (also used by PhotoCapture) so we don't duplicate ImageIO logic.

  • Problem: chat composer rejects attachments over 5 MB before any client-side transformation, then base64-encodes raw bytes from the MainActor before send. Modern phone photos (12–48 MP) blow that budget instantly, encode work blocks UI, and source EXIF/GPS metadata is sent verbatim.
  • Why it matters: image attachments from camera roll were effectively unusable; raw EXIF/GPS leak is a privacy issue; MainActor encode stalls the keyboard.
  • What changed: resize to 1600 px long-edge, JPEG re-encode at q=0.8 with progressive quality+pixel fallback to a 3.5 MB byte budget, off-MainActor on a detached Task, source-derived metadata not forwarded to destination.
  • What did NOT change: the existing 5 MB hard gate in ChatViewModel is kept as final safety net for inputs even q≈0.2 at ~256 px can't fit. PhotoCapture's call into JPEGTranscoder is byte-for-byte identical (alpha-flatten is a no-op when input has no alpha — camera output never has alpha). Network/protocol/auth/permissions surface unchanged.

AI-assisted PR. Implemented with Claude (Opus 4.7) under direction of repo owner. Lightly tested — unit tests pass on macOS with swift test; not yet validated on a physical iPhone (camera roll → composer → send). Author understands and has reviewed every line. Greptile + GitHub Codex review feedback addressed in code (alpha flatten, encode refactor, privacy bytes-test, JPEGTranscoder consolidation per Codex's recommendation).

Closes #68524.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: addImageAttachment in OpenClawChatViewModel accepted the user's raw image bytes verbatim, applied only a hard size cap, and forwarded the raw bytes to base64 encode on the MainActor. There was no resize, no re-encode, no metadata stripping — by design, the path predates JPEGTranscoder.
  • Missing detection / guardrail: no client-side image policy layer between picker and send; no test asserting source EXIF/GPS strings don't appear in the byte stream.
  • Contributing context: when JPEGTranscoder was added (for PhotoCapture), the chat path was not updated to use it. This PR closes that gap.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test: apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatImageProcessorTests.swift (new, 7 tests).
  • Scenario the test should lock in:
    • Long-edge ≤ 1600 px after processing
    • Small image not upscaled
    • Output ≤ payload budget
    • Non-image data rejected
    • Parsed EXIF/GPS/TIFF dictionaries empty
    • Raw output bytes contain no planted needle strings ("Leaky Lens", "LeakCorp", "Privacy-Leaker", a date)
    • Transparent PNG → opaque JPEG (alpha flatten)
  • Why this is the smallest reliable guardrail: bytes-level scan is decoder-independent and catches "we forgot to strip" regressions even when ImageIO behavior changes.
  • Existing test that already covers this: none (gap before this PR).

User-visible / Behavior Changes

  • Image attachments larger than 1600 px on the long edge are resized client-side before send.
  • Source EXIF/GPS/IPTC/TIFF Make/Model metadata no longer leaves the device.
  • Send is faster on large images (work moved off MainActor).
  • The 5 MB hard gate remains; users will still see "too large" for inputs even ~256 px at q≈0.2 can't fit.

Diagram (if applicable)

Before:
[picker.bytes] -> [5 MB hard gate] -> base64(MainActor) -> send  // raw EXIF/GPS shipped

After:
[picker.bytes] -> validate(isImage)
              -> ChatImageProcessor.processForUpload (off-MainActor)
                   └── JPEGTranscoder: orient → resize→1600 → alpha-flatten → JPEG q=0.8
                       progressive q×0.75 / px×0.85 search to ≤3.5 MB
              -> [5 MB hard gate] (final safety net)
              -> base64 -> send

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? Yes (reducing) — source EXIF/GPS/IPTC/TIFF Make/Model dictionaries are no longer forwarded to the destination JPEG. ImageIO still emits a minimal format-default APP1 EXIF segment (orientation/version) on JPEG output regardless of input — that's the format default, not source data. Bytes-level test (test_outputContainsNoLeakedSensitiveStrings) asserts source-derived needle strings do not survive into output bytes. Risk: a future ImageIO change could regress this; mitigation: the bytes-level test is decoder-independent and would fail on regression.

Repro + Verification

Environment

  • OS: macOS 26 (host); SwiftPM target builds for iOS as well
  • Runtime/container: Swift 6.2 toolchain, Xcode 26
  • Model/provider: N/A (image pipeline)
  • Integration/channel: chat composer (iOS app)
  • Relevant config: none

Steps

  1. cd apps/shared/OpenClawKit
  2. swift build
  3. swift test --filter "ChatImageProcessorTests|JPEGTranscoderTests"

Expected

  • Build succeeds.
  • 7 ChatImageProcessorTests pass.
  • 4 existing JPEGTranscoderTests still pass.

Actual

  • Build succeeds (~3.5 s on M1 Max).
  • All 11 tests pass.

Evidence

Test Suite 'ChatImageProcessorTests' passed at 2026-04-28 20:30:30
     Executed 7 tests, with 0 failures (0 unexpected) in 0.229 (0.230) seconds
✔ Test downscalesToMaxWidthPx() passed after 0.051 seconds
✔ Test normalizesOrientationAndUsesOrientedWidthForMaxWidthPx() passed
✔ Test doesNotUpscaleWhenSmallerThanMaxWidthPx() passed
✔ Test respectsMaxBytes() passed after 2.517 seconds

Human Verification (required)

What I (the contributor) personally verified, not just CI:

  • Verified scenarios (on macOS via swift test):
    • Long-edge resize to 1600 px on a synthetic 3000×2000 PNG
    • Small (800×600) image not upscaled
    • Output under 3.5 MB on a synthetic 4000×3000 image
    • Non-image data ("hello") raises ProcessError.notAnImage
    • Parsed EXIF/GPS/TIFF dicts empty after processing a JPEG with planted EXIF/GPS/TIFF
    • Output bytes do not contain planted needle strings (decoder-independent)
    • Transparent PNG flattened to opaque JPEG (no black background)
  • Edge cases checked: alpha PNG (decoded as RGBA); JPEG with all four metadata families; very small input
  • What I did not verify: real-device camera roll → composer → server round-trip on a physical iPhone (deferred to maintainer / device validation phase).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Greptile review (3 threads) addressed in code and resolved. GitHub Codex review answered with a top-level comment and the recommended JPEGTranscoder consolidation was executed.

Compatibility / Migration

  • Backward compatible? Yes (no schema, protocol, or storage change; receiver gets a JPEG)
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: ImageIO behavior change causes future regression in metadata stripping.
    • Mitigation: test_outputContainsNoLeakedSensitiveStrings is decoder-independent; would fail before reaching review.
  • Risk: Q-fallback floor (≈0.2 at ~256 px) still can't fit some pathological inputs (e.g., highly noisy 50 MP).
    • Mitigation: 5 MB hard gate remains; user sees the existing "too large" surface, not a silent corruption.
  • Risk: Alpha-flatten changes pixel output for inputs with transparency.
    • Mitigation: white background is documented as the intentional default for chat; PhotoCapture output (no alpha) is byte-for-byte identical.

@greptile-apps

greptile-apps Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires client-side image resize, JPEG re-encode, and EXIF/GPS metadata stripping into the chat composer attachment path by adapting the existing JPEGTranscoder (used by PhotoCapture) through a new thin ChatImageProcessor shim, moves the heavy encode work off the MainActor via a detached Task, and adds 7 unit tests covering dimensions, metadata stripping, budget compliance, alpha flattening, and a bytes-level privacy needle scan.

  • The 5 MB post-check on processed in addImageAttachment is unreachable: processForUpload either throws (caught earlier) or returns data ≤ 3.5 MB, and the accompanying comment incorrectly states the processor returns a best-effort result when over budget (it throws instead).

Confidence Score: 5/5

Safe to merge; the only finding is a dead-code guard with a misleading comment — no runtime behavior is affected.

All findings are P2 (dead code / inaccurate comment). The core logic — resize, alpha flatten, metadata strip, off-MainActor encode, best-effort quality fallback — is sound and well-tested. No P0 or P1 issues found.

apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel.swift — dead 5 MB post-check with inaccurate comment at line 1160.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel.swift
Line: 1160-1166

Comment:
**5 MB post-check is dead code**

`processForUpload` either throws `ProcessError.encodeFailed` (caught by the `do/catch` block above, which returns early) or returns data guaranteed to be ≤ `ChatImageProcessor.maxPayloadBytes` (3.5 MB). There is no code path where it returns data between 3.5 MB and 5 MB, so this guard is unreachable.

The comment "the processor returns its lowest-quality pass even if budget couldn't be met" is also inaccurate — `JPEGTranscoder.transcodeToJPEG` throws `sizeLimitExceeded` when the budget is not met, and that error propagates as `ProcessError.encodeFailed` before this check is ever reached.

Consider either removing the check (since it is provably dead) or updating the comment to accurately describe the control flow, so future maintainers don't assume this gate is active.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "feat(chat): inline image resize + EXIF s..." | Re-trigger Greptile

Comment thread apps/shared/OpenClawKit/Sources/OpenClawKit/ChatImageProcessor.swift Outdated
Comment thread apps/shared/OpenClawKit/Sources/OpenClawKit/ChatImageProcessor.swift Outdated
Comment thread apps/shared/OpenClawKit/Sources/OpenClawKit/ChatImageProcessor.swift Outdated
@clawsweeper

clawsweeper Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge.

Summary
The PR adds an iOS/OpenClawKit chat image preprocessing path that resizes and JPEG re-encodes attachments through JPEGTranscoder, strips source metadata, adds tests and a changelog entry, and carries unrelated Settings/Onboarding setup-code changes.

Reproducibility: yes. Source inspection on current main gives a high-confidence path: an iOS chat image Data payload over 5,000,000 bytes is rejected before resize, and accepted attachment bytes are base64-encoded directly from the view model.

Real behavior proof
Needs stronger real behavior proof before merge: Older terminal proof shows real iPhone/exiftool results for head 5ae00595, but latest head c881fc1 has no current-head camera-roll-to-receiver proof in the PR body or comments. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, ask a maintainer to comment @clawsweeper re-review.

Next step before merge
External PR needs contributor current-head real behavior proof plus maintainer review after the Settings parser regression and unrelated drift are cleaned up; automation cannot provide the contributor’s device proof.

Security
Cleared: No concrete security or supply-chain regression was found in the diff; the privacy hardening still needs current-head real behavior proof.

Review findings

  • [P2] Keep setup input parsing on the broad parser — apps/ios/Sources/Settings/SettingsTab.swift:829
Review details

Best possible solution:

Land a focused iOS chat preprocessing patch after restoring the setup-input parser behavior and adding redacted current-head device proof; keep media-limit configurability in the separate canonical issue.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection on current main gives a high-confidence path: an iOS chat image Data payload over 5,000,000 bytes is rejected before resize, and accepted attachment bytes are base64-encoded directly from the view model.

Is this the best way to solve the issue?

No, not in the current branch shape. Reusing JPEGTranscoder is the right implementation direction, but the branch should restore the broad setup parser, trim unrelated Settings drift, and provide current-head real-device proof before merge.

Full review comments:

  • [P2] Keep setup input parsing on the broad parser — apps/ios/Sources/Settings/SettingsTab.swift:829
    This Settings field still accepts pasted setup material, but fromSetupCode only decodes setup-code payloads. Current shared parsing also accepts copied setup messages, openclaw://gateway links, and raw ws/wss gateway URLs, so this change would reject supported setup inputs; restore GatewayConnectDeepLink.fromSetupInput or remove the Settings drift from this image PR.
    Confidence: 0.92

Overall correctness: patch is incorrect
Overall confidence: 0.9

Acceptance criteria:

  • cd apps/shared/OpenClawKit && swift test --filter "ChatImageProcessorTests|JPEGTranscoderTests"
  • Run an iOS project-generation/build check after restoring Settings parser behavior and rebasing.
  • Verify on a real iPhone: camera-roll photo through composer to receiver, dimensions capped, payload under budget, and source EXIF/GPS/Make/Model absent.

What I checked:

Likely related people:

  • steipete: Local blame ties the current raw attachment/base64 path and JPEGTranscoder snapshot to Peter Steinberger, and GitHub history shows recent ChatViewModel/OpenClawKit build-hygiene work by steipete. (role: recent area contributor; confidence: high; commits: ff7beea3da89, ff45bc1f8875, 482c74b72497; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel.swift, apps/shared/OpenClawKit/Sources/OpenClawKit/JPEGTranscoder.swift, apps/shared/OpenClawKit/Sources/OpenClawKit/PhotoCapture.swift)
  • BunsDev: Recent GitHub history shows BunsDev changed the iOS setup/deep-link parser contract and nearby ChatViewModel behavior that this branch overlaps while rebasing. (role: recent adjacent contributor; confidence: high; commits: 36df0d93b93a, b2efd1964800, 9ffe290a170b; files: apps/shared/OpenClawKit/Sources/OpenClawKit/DeepLinks.swift, apps/ios/Sources/Settings/SettingsTab.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel.swift)
  • ngutman: SettingsTab history shows several recent iOS gateway Settings and onboarding changes by ngutman, relevant to reviewing the unrelated Settings/Onboarding drift in this branch. (role: iOS settings/onboarding feature-history owner; confidence: medium; commits: 00a0858fd9dc, 6380c872bcc2, 69fe999373fd; files: apps/ios/Sources/Settings/SettingsTab.swift, apps/ios/Sources/Onboarding/GatewayOnboardingView.swift)

Remaining risk / open question:

Codex review notes: model gpt-5.5, reasoning high; reviewed against 23edebbaed76.

@devicemanager devicemanager force-pushed the pr/ios-chat-image-resize-exif branch from 1eaeeae to 08b837c Compare April 28, 2026 18:19
@devicemanager devicemanager force-pushed the pr/ios-chat-image-resize-exif branch from 08b837c to a2497a8 Compare April 28, 2026 18:31
@devicemanager

Copy link
Copy Markdown
Author

Thanks for the deep review. Two notes:

1. Heads-up on snapshot freshness. This review was made against 3f780bb27da1 and the current PR head is now a2497a8fe281d4aca2ab1063d6b7a8418a8f8b20. The "test file claimed in PR body but not in files API" finding was a snapshot artifact — Tests/OpenClawKitTests/ChatImageProcessorTests.swift has been in the PR head since the initial filing (1eaeeae782). It's now the third file in gh api repos/openclaw/openclaw/pulls/73710/files. Same story for addImageAttachment wiring — OpenClawChatViewModel.addImageAttachment is modified in this PR to invoke ChatImageProcessor.processForUpload on a detached Task off the MainActor, with the 5 MB hard gate kept as the final safety net.

2. Acted on the consolidation recommendation. You're right that a chat-specific re-implementation of ImageIO logic alongside an existing JPEGTranscoder is wrong shape. As of a2497a8fe281d4aca2ab1063d6b7a8418a8f8b20, ChatImageProcessor is now a ~70-line policy adapter that delegates to JPEGTranscoder.transcodeToJPEG, mapping JPEGTranscodeError to a smaller chat-facing ProcessError surface. Net diff dropped ~180 lines.

While doing the consolidation I added one strictly-additive correctness fix to JPEGTranscoder itself: alpha flattening against opaque white before each encode pass. Without this, JPEG-encoding a source with alpha (PNG screenshot, HEIC with transparent UI overlay) composites against black via ImageIO's default. Sources with no alpha (the camera-capture path) skip the flatten step entirely, so PhotoCapture is unaffected; existing JPEGTranscoderTests (4 tests) continue to pass.

Re: the EXIF/orientation contract — the privacy contract is "no source-derived sensitive values survive into the output bytes," verified by test_outputContainsNoLeakedSensitiveStrings which scans the raw output for planted needle strings ("Leaky Lens", "LeakCorp", "Privacy-Leaker", a date). ImageIO does still emit a minimal format-default APP1 EXIF segment (orientation/version, no source data) on JPEG output regardless of input properties — discovered while writing the bytes-level test. Documented in the encoder doc comment.

PR body updated to match the current shape.

@devicemanager

Copy link
Copy Markdown
Author

Status update — marking back to Draft

I want to be straight with reviewers: this PR isn't ready yet, and an earlier revision of the PR body overclaimed the verification I'd actually done. Apologies for the noise.

Where it actually stands:

  • ✅ Code change is in and unit tests pass on macOS via swift test against apps/shared/OpenClawKit/.
  • ✅ Greptile and GitHub Codex review feedback addressed in code.
  • The post-review code (current head) has not yet been verified on a real iPhone. I tried to produce a Debug IPA on the maintainer's local rig tonight; the iOS app archive failed for reasons unrelated to this PR (missing upstream symbols on the feature branch — branch base is stale). No signed IPA, no on-device test.
  • 🔄 Plan: rebase the branch onto current main, rebuild the IPA, run the test plan on a physical device, then attach before/after screenshots and re-request review. Will move back to "Ready for review" only after that.

Marking as Draft so this doesn't sit in a maintainer's queue while it's still in flux. Sorry for the back-and-forth.

@devicemanager

Copy link
Copy Markdown
Author

Codex / Greptile bots — thanks for the read.

Closing the loop with on-device evidence from 2026-04-29 ~04:43 CEST. Built a Debug IPA from the rebased branch (head 5ae00595), installed via OTA on a real iPhone running iOS 26, and sent 4 fresh JPEGs straight from the camera roll through the chat composer to the OpenClaw inbound channel. On the receiving side (so what actually traveled in the attachment bytes, not what the sender thinks it sent):

$ for f in photo-31FF6848*.jpg photo-A68FEFA5*.jpg \
           photo-20D41B15*.jpg photo-F8C37234*.jpg; do
    sips -g pixelWidth -g pixelHeight "$f" | grep pixel | awk '{print $2}'
    exiftool -s -GPS:all -Make -Model -DateTimeOriginal -Software "$f"
  done

photo-31FF6848…jpg  1600×2133   2227 KB   exif=''
photo-A68FEFA5…jpg  1600×2133   2052 KB   exif=''
photo-20D41B15…jpg  1600×2133   2113 KB   exif=''
photo-F8C37234…jpg  1600×2133   1964 KB   exif=''

All four:

  • Long edge clamped to 1600 px.
  • Payload ≤ 2.3 MB (well under the 3.5 MB cap).
  • exiftool -GPS:all -Make -Model -DateTimeOriginal -Software returns empty for every file — none of the source-derived sensitive EXIF made it onto the wire.

That's the privacy promise actually shipping, verified end-to-end on a real device, on real camera-roll photos (not synthetic test fixtures). The 7 unit tests in ChatImageProcessorTests cover the same contract programmatically — including a raw-bytes scan against planted needles ("Leaky Lens", "LeakCorp", "Privacy-Leaker", ISO date string) so the assertion isn't "the EXIF parser sees nothing", it's "those bytes are not in the file at all".

The Codex feedback to consolidate the second ImageIO path onto JPEGTranscoder was the right call — the alpha-flatten step now lives in one place and PhotoCapture's no-alpha source path is unaffected (the flatten branch only runs on inputs with an alpha channel).

Force-push of the rebased branch pending René's morning sign-off (no surprise force-pushes to the fork). Updated PR body covers this evidence; will move to Ready-for-review together with #73711 once René confirms the round-2 device install is good.

@devicemanager devicemanager force-pushed the pr/ios-chat-image-resize-exif branch from a2497a8 to 5ae0059 Compare April 29, 2026 04:44
@devicemanager devicemanager marked this pull request as ready for review April 29, 2026 04:45
@devicemanager

Copy link
Copy Markdown
Author

Pushed rebased branch onto latest upstream/main (head 5ae00595). Marked Ready-for-review.

End-to-end verified on iPhone (iOS 26) again post-rebase: 16 distinct camera-roll JPEGs across two send sessions, all clamped to ≤1600 px width, all under 600 KB, exiftool -GPS:all -Make -Model -DateTimeOriginal -Software returns empty for every file. SwiftFormat lint via xcodebuild archive path passes (the --unexclude set covering OpenClawKit).

Diff vs the previous PR head: rebase-only conflict resolution + cosmetic SwiftFormat fixes the lint pass surfaced (elseOnSameLine, wrapMultilineStatementBraces, docComments). Zero functional change to the resize / EXIF strip / alpha-flatten logic.

@devicemanager

Copy link
Copy Markdown
Author

I might soon lose access to corp AI, and wanted this for myself and thought this was great for the community. I haven't been able to test this on android, since I don't have an android phone. So I need to rely on the emulator tests.

@devicemanager

Copy link
Copy Markdown
Author

Trying to address this further in #67031. The PR works (on my phone), but needs to be configurable.

@openclaw-barnacle openclaw-barnacle Bot added app: ios App: ios gateway Gateway runtime agents Agent runtime and tooling labels May 7, 2026
devicemanager added 3 commits May 9, 2026 15:06
Wires client-side resize and JPEG re-encode into the chat composer
attachment path. Implementation builds on the existing JPEGTranscoder
in OpenClawKit (which is also used by PhotoCapture) rather than
duplicating ImageIO logic — addressing maintainer review feedback to
consolidate the two paths.

Changes:

- JPEGTranscoder: alpha-flatten step added before each encode pass.
  JPEG cannot store an alpha channel; without flattening, ImageIO
  composites transparent regions against black on encode, which is
  almost never what a chat or capture flow wants. Source images with
  no alpha (the camera-capture case) skip the flatten path entirely,
  so PhotoCapture is unaffected. White is the safer default for chat
  backgrounds; documented in the helper.

- ChatImageProcessor (new, ~70 lines): chat-specific adapter over
  JPEGTranscoder. Holds the policy that's specific to chat:
  maxDimension=1600, jpegQuality=0.8, maxPayloadBytes=3.5 MB. Maps
  JPEGTranscodeError to a smaller chat-facing ProcessError surface
  (notAnImage / decodeFailed / encodeFailed) with LocalizedError
  conformance for usable error messages. JPEGTranscoder's progressive
  search handles the size-limit fallback; the adapter just sets the
  policy and translates errors.

- ChatViewModel.addImageAttachment: validates input is an image, runs
  ChatImageProcessor.processForUpload off the MainActor on a detached
  Task, replaces the original bytes with the JPEG-encoded result
  before the existing 5 MB hard gate. The hard gate remains the final
  safety net for inputs that even q≈0.2 at ~256px can't fit.

- Privacy contract: ImageIO is not given any source EXIF/GPS/IPTC/
  TIFF dictionaries on the destination, so source-derived sensitive
  values do not survive into the output bytes. ImageIO will still
  emit a minimal format-default APP1 EXIF segment (orientation/
  version) — that's the format default, not source data.

Tests (7 unit tests in ChatImageProcessorTests):
- resizesLongEdgeTo1600
- smallImageStaysSmall (no upscale)
- outputIsUnderPayloadBudget
- rejectsNonImageData
- stripsEXIFAndGPSAndTIFF (parsed-dictionary check)
- outputContainsNoLeakedSensitiveStrings (raw-bytes scan against
  planted needles "Leaky Lens", "LeakCorp", "Privacy-Leaker",
  date string)
- flattensAlphaToOpaqueJPEG (synthetic transparent PNG → opaque JPEG)

Existing JPEGTranscoderTests (4 tests) continue to pass; the alpha
flatten is additive and only triggers on inputs with alpha.
… changelog

Address review findings from clawsweeper:

- [P2] JPEGTranscoder now accepts maxLongEdgePx parameter that caps the
  longest edge (not just width). ChatImageProcessor uses this so portrait
  and narrow-tall images are properly constrained to 1600px on the long edge.
- [P3] Remove unreachable 5 MB post-check in ChatViewModel — the processor
  already throws on sizeLimitExceeded before returning.
- [P3] Add CHANGELOG.md entry for this user-facing iOS change.
- Add test coverage for portrait (3000x4000) and narrow-tall (1080x2400)
  images to verify long-edge clamping.

All 13 tests pass (9 ChatImageProcessor + 4 JPEGTranscoder).
Fixes #68524.
@devicemanager devicemanager force-pushed the pr/ios-chat-image-resize-exif branch from 55b417b to e043925 Compare May 9, 2026 14:32
@openclaw-barnacle openclaw-barnacle Bot added size: L and removed gateway Gateway runtime agents Agent runtime and tooling size: XL labels May 9, 2026
BunsDev added a commit that referenced this pull request May 14, 2026
Resize iOS chat PhotosPicker image attachments through the shared JPEG transcoder before staging/sending. Cap long edge and payload bytes, strip source metadata, preserve previews from processed data, and add focused processor/view-model regression tests.\n\nFixes #68524.\nSupersedes #73710.
@BunsDev BunsDev added the duplicate This issue or pull request already exists label May 14, 2026
@BunsDev

BunsDev commented May 14, 2026

Copy link
Copy Markdown
Member

Closing this as superseded by the focused maintainer replacement that has now landed on main: #81608 (merge commit https://github.com/openclaw/openclaw/commit/faa443a45220ec2df124008cd805e9af99f461eb).\n\nThe landed patch keeps the same core fix direction for #68524: iOS chat PhotosPicker images are processed through before staging/sending, capped to a JPEG upload budget, and covered by focused / tests. It intentionally leaves the unrelated Settings/Onboarding drift from this PR out of the replacement branch.\n\nThanks for the original implementation and device proof; it was useful context for the final scoped fix.

@BunsDev BunsDev closed this May 14, 2026
@BunsDev

BunsDev commented May 14, 2026

Copy link
Copy Markdown
Member

Correction to the close note above: shell interpolation stripped the inline code identifiers.

Closing this as superseded by the focused maintainer replacement that has now landed on main: #81608 (merge commit faa443a).

The landed patch keeps the same core fix direction for #68524: iOS chat PhotosPicker images are processed through JPEGTranscoder before staging/sending, capped to a JPEG upload budget, and covered by focused ChatImageProcessor / ChatViewModelAttachmentTests tests. It intentionally leaves the unrelated Settings/Onboarding drift from this PR out of the replacement branch.

Thanks for the original implementation and device proof; it was useful context for the final scoped fix.

@devicemanager

devicemanager commented May 14, 2026 via email

Copy link
Copy Markdown
Author

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
Resize iOS chat PhotosPicker image attachments through the shared JPEG transcoder before staging/sending. Cap long edge and payload bytes, strip source metadata, preserve previews from processed data, and add focused processor/view-model regression tests.\n\nFixes openclaw#68524.\nSupersedes openclaw#73710.
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
Resize iOS chat PhotosPicker image attachments through the shared JPEG transcoder before staging/sending. Cap long edge and payload bytes, strip source metadata, preserve previews from processed data, and add focused processor/view-model regression tests.\n\nFixes openclaw#68524.\nSupersedes openclaw#73710.
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Resize iOS chat PhotosPicker image attachments through the shared JPEG transcoder before staging/sending. Cap long edge and payload bytes, strip source metadata, preserve previews from processed data, and add focused processor/view-model regression tests.\n\nFixes openclaw#68524.\nSupersedes openclaw#73710.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: ios App: ios duplicate This issue or pull request already exists size: XL triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] iOS: large chat image attachments fail or blow up memory (48MP frames, no client-side resize)

2 participants