feat(chat/ios): inline image resize + EXIF metadata strip for attachments#73710
feat(chat/ios): inline image resize + EXIF metadata strip for attachments#73710devicemanager wants to merge 6 commits into
Conversation
Greptile SummaryThis PR wires client-side image resize, JPEG re-encode, and EXIF/GPS metadata stripping into the chat composer attachment path by adapting the existing
Confidence Score: 5/5Safe 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 AIThis 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 |
|
Codex review: needs real behavior proof before merge. Summary 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 Next step before merge Security Review findings
Review detailsBest 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:
Overall correctness: patch is incorrect Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 23edebbaed76. |
1eaeeae to
08b837c
Compare
08b837c to
a2497a8
Compare
|
Thanks for the deep review. Two notes: 1. Heads-up on snapshot freshness. This review was made against 2. Acted on the consolidation recommendation. You're right that a chat-specific re-implementation of ImageIO logic alongside an existing While doing the consolidation I added one strictly-additive correctness fix to Re: the EXIF/orientation contract — the privacy contract is "no source-derived sensitive values survive into the output bytes," verified by PR body updated to match the current shape. |
Status update — marking back to DraftI 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:
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. |
|
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 All four:
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 The Codex feedback to consolidate the second ImageIO path onto 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. |
a2497a8 to
5ae0059
Compare
|
Pushed rebased branch onto latest 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, Diff vs the previous PR head: rebase-only conflict resolution + cosmetic SwiftFormat fixes the lint pass surfaced ( |
|
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. |
b249ca8 to
b4f6100
Compare
b91a6d7 to
40891d6
Compare
|
Trying to address this further in #67031. The PR works (on my phone), but needs to be configurable. |
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.
…rmat/SwiftLint build scripts
55b417b to
e043925
Compare
|
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. |
|
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 Thanks for the original implementation and device proof; it was useful context for the final scoped fix. |
|
Glad to hear you fixed it. Been struggling with other things that needed focus. Happy to try out your solution soon,
Regard
… On 14 May 2026, at 04:45, Val Alexander ***@***.***> wrote:
BunsDev
left a comment
(openclaw/openclaw#73710)
<#73710 (comment)>
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 <#81608> (merge commit faa443a <faa443a>).
The landed patch keeps the same core fix direction for #68524 <#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.
—
Reply to this email directly, view it on GitHub <#73710 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAOCGZNZHH2XFLZ5BYLYCOT42UXNHAVCNFSM6AAAAACYJW2JFCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DINBWHE4DSNBXHA>.
You are receiving this because you were mentioned.
|
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.
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.
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.
Summary
Wires client-side resize and JPEG re-encode into the chat composer attachment path, built on the existing
JPEGTranscoderin OpenClawKit (also used byPhotoCapture) so we don't duplicate ImageIO logic.Task, source-derived metadata not forwarded to destination.ChatViewModelis kept as final safety net for inputs even q≈0.2 at ~256 px can't fit. PhotoCapture's call intoJPEGTranscoderis 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.Closes #68524.
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Root Cause (if applicable)
addImageAttachmentinOpenClawChatViewModelaccepted 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 predatesJPEGTranscoder.JPEGTranscoderwas added (forPhotoCapture), the chat path was not updated to use it. This PR closes that gap.Regression Test Plan (if applicable)
apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatImageProcessorTests.swift(new, 7 tests).User-visible / Behavior Changes
Diagram (if applicable)
Security Impact (required)
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
Steps
cd apps/shared/OpenClawKitswift buildswift test --filter "ChatImageProcessorTests|JPEGTranscoderTests"Expected
Actual
Evidence
main,processForUploaddoesn't exist)Human Verification (required)
What I (the contributor) personally verified, not just CI:
swift test):"hello") raisesProcessError.notAnImageReview Conversations
Greptile review (3 threads) addressed in code and resolved. GitHub Codex review answered with a top-level comment and the recommended
JPEGTranscoderconsolidation was executed.Compatibility / Migration
Risks and Mitigations
test_outputContainsNoLeakedSensitiveStringsis decoder-independent; would fail before reaching review.