Skip to content

fix(mobile): render staged chat image attachments in the timeline - #5382

Open
nabettu wants to merge 1 commit into
Untrivial-ai:mainfrom
nabettu:ao/agent-orchestrator-2/mobile-chat-attachment-images
Open

nabettu wants to merge 1 commit into
Untrivial-ai:mainfrom
nabettu:ao/agent-orchestrator-2/mobile-chat-attachment-images

Conversation

@nabettu

@nabettu nabettu commented Sep 14, 2026

Copy link
Copy Markdown

What

The mobile Chat timeline now shows images the human attached. Before this change, a message sent with an image from the desktop composer appeared on mobile as a raw .ao/attachments/... path list, with no image.

Why

Fixes #5380.

How

  • New lib/chat/messageAttachments.ts mirrors desktop's stagedAttachmentParts. It removes the AO-owned suffix only when every listed path is AO-staged (.ao/attachments/(attachment|image)-*), so prose that happens to quote the same wording stays as written. It accepts every wording AO has shipped: desktop composer, spawn (… for context), legacy Attached images …, and the older mobile Attached files are available in the worktree:.
  • ChatTimeline: human bubbles and steered bubbles show the body text plus the staged attachments. Images load from GET /api/v1/sessions/{id}/preview/files/<path> through RN <Image source={{ uri, headers }}> with the connection's existing Bearer header (authHeaders(config), the same credential every mobile API call uses). Non-image files, and images that fail to load, show as a name chip.
  • Mobile now writes desktop's suffix wording when it stages attachments, so images sent from the phone also render on desktop. The parser still accepts the old mobile wording, so existing history keeps rendering.
  • The conversation map titles a turn with the human's text instead of the path list (or "N attachments" when the message has no text).
  • No backend change: the preview-files route already serves staged attachments via attachmentstore, and the LAN listener allows it behind authMiddleware.

Intentional omissions:

  • Native image content blocks (content[].type == "image") are not rendered. The daemon returns only a summary without bytes, and desktop's human message bubble also relies on the staged paths.
  • Relative-path images in agent Markdown (markdownBlocks.ts only accepts ![](https?://…)) are a separate issue and are out of scope here.

Testing

  • TDD: lib/chat/messageAttachments.test.ts was written first and failed because the module did not exist; after the change it passes. Added a marker-title case to timelineModel.test.ts.

  • packages/mobile: npm ci, npm run typecheck (clean), npm test (80 files / 796 tests passing). These are the Mobile workflow's steps. Ran on Node 22.18.0 locally; CI uses Node 20. The local Node 20.17 is below rolldown's ^20.19 engine floor, so it could not run vitest.

  • Not run locally: gitleaks (Docker unavailable on this machine), so I'm relying on the CI run.

  • Verified on iOS simulator (iPhone 17 Pro, iOS 26.3; local expo run:ios build with Xcode 27 RC; paired to a running daemon over the Connect Mobile listener), using an existing chat message whose PNG was attached from the desktop composer:

    • main (baseline): the bubble shows the raw Attached files (read these files in the workspace): line plus the .ao/attachments/attachment-….png path, and no image.
    • This branch: the bubble shows only the typed text, and the attached PNG renders inline below it (loaded from preview/files with the Bearer header). Fetching the same URL over the LAN listener returns 200 image/png with the header and 401 without it.
  • Screenshots (iOS simulator; a neutral solid-color test PNG sent to an empty scratch chat in the desktop composer's format):

    main (before) This branch (after)
    main: raw attachment path text, no image branch: attached image rendered inline

Checklist

  • Branched from main (or continuing an existing PR branch)
  • One focused change; links the related issue when applicable
  • Follows AGENTS.md conventions and PR hygiene
  • Tests added/updated for user-visible behavior where it makes sense
  • Relevant CI checks pass for the area touched (go, frontend, etc.)

🤖 Generated with Claude Code

Human messages carrying AO's staged-attachment suffix (as written by the
desktop composer, spawn, legacy image prompts, and earlier mobile builds)
showed the raw ".ao/attachments/..." path list on mobile and no image.
Parse the suffix like desktop does, render staged images through the
daemon's preview-files route with the connection's Bearer header, and
fall back to a name chip for non-images or failed loads. Mobile now
writes desktop's wording so its own attachments render on desktop too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mobile chat doesn't show images attached from the desktop chat

1 participant