From 5209b64fac90b55253918f98f7918952cc2ae796 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 16 Jul 2026 10:53:31 +0200 Subject: [PATCH 1/2] fix(mobile): Send composer images in upload wire format Co-authored-by: codex --- apps/mobile/src/lib/composerImages.test.ts | 32 ++++++++++++++++++- apps/mobile/src/lib/composerImages.ts | 13 ++++++++ apps/mobile/src/lib/projectThreadStartTurn.ts | 4 +-- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/apps/mobile/src/lib/composerImages.test.ts b/apps/mobile/src/lib/composerImages.test.ts index 40e00a271f7..21f2edaf52f 100644 --- a/apps/mobile/src/lib/composerImages.test.ts +++ b/apps/mobile/src/lib/composerImages.test.ts @@ -36,7 +36,37 @@ vi.mock("./uuid", () => ({ uuidv4: () => "attachment-id", })); -import { convertPastedImagesToAttachments, isOwnedPastedImageUri } from "./composerImages"; +import { + convertPastedImagesToAttachments, + isOwnedPastedImageUri, + toUploadChatImageAttachments, +} from "./composerImages"; + +describe("toUploadChatImageAttachments", () => { + it("strips client draft id and previewUri for the startTurn wire shape", () => { + expect( + toUploadChatImageAttachments([ + { + id: "client-draft-id", + type: "image", + name: "pasted-image.png", + mimeType: "image/png", + sizeBytes: 12, + dataUrl: "data:image/png;base64,AA==", + previewUri: "file:///tmp/preview.png", + }, + ]), + ).toEqual([ + { + type: "image", + name: "pasted-image.png", + mimeType: "image/png", + sizeBytes: 12, + dataUrl: "data:image/png;base64,AA==", + }, + ]); + }); +}); describe("native pasted image cleanup", () => { beforeEach(() => { diff --git a/apps/mobile/src/lib/composerImages.ts b/apps/mobile/src/lib/composerImages.ts index 13b53af724e..8aa6d2a3fa8 100644 --- a/apps/mobile/src/lib/composerImages.ts +++ b/apps/mobile/src/lib/composerImages.ts @@ -10,6 +10,19 @@ export interface DraftComposerImageAttachment extends UploadChatImageAttachment readonly previewUri: string; } +/** Wire shape for startTurn: pure uploads without client draft id / previewUri. */ +export function toUploadChatImageAttachments( + attachments: ReadonlyArray, +): ReadonlyArray { + return attachments.map((attachment) => ({ + type: attachment.type, + name: attachment.name, + mimeType: attachment.mimeType, + sizeBytes: attachment.sizeBytes, + dataUrl: attachment.dataUrl, + })); +} + const OWNED_PASTED_IMAGE_DIRECTORY = "t3-composer-paste"; function estimateBase64ByteSize(base64: string): number { diff --git a/apps/mobile/src/lib/projectThreadStartTurn.ts b/apps/mobile/src/lib/projectThreadStartTurn.ts index e3c2f744ada..85523175a2f 100644 --- a/apps/mobile/src/lib/projectThreadStartTurn.ts +++ b/apps/mobile/src/lib/projectThreadStartTurn.ts @@ -8,7 +8,7 @@ import { type RuntimeMode, } from "@t3tools/contracts"; -import type { DraftComposerImageAttachment } from "./composerImages"; +import { toUploadChatImageAttachments, type DraftComposerImageAttachment } from "./composerImages"; export function deriveThreadTitleFromPrompt(value: string): string { const trimmed = value.trim(); @@ -55,7 +55,7 @@ export function buildProjectThreadStartTurnInput(spec: ProjectThreadStartTurnSpe messageId: MessageId.make(spec.messageId), role: "user" as const, text: spec.text, - attachments: spec.attachments, + attachments: toUploadChatImageAttachments(spec.attachments), }, modelSelection: spec.modelSelection, titleSeed: title, From 519da2dfc5a4c69b5c66520f9d408a0051ccd918 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 16 Jul 2026 11:28:46 +0200 Subject: [PATCH 2/2] fix(mobile): Convert outbox image attachments to wire format Co-authored-by: codex --- apps/mobile/src/state/use-thread-outbox-drain.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/mobile/src/state/use-thread-outbox-drain.ts b/apps/mobile/src/state/use-thread-outbox-drain.ts index f4de9c39492..3559fa140fe 100644 --- a/apps/mobile/src/state/use-thread-outbox-drain.ts +++ b/apps/mobile/src/state/use-thread-outbox-drain.ts @@ -17,6 +17,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { scopedThreadKey } from "../lib/scopedEntities"; import { buildProjectThreadStartTurnInput } from "../lib/projectThreadStartTurn"; +import { toUploadChatImageAttachments } from "../lib/composerImages"; import { randomHex } from "../lib/uuid"; import { appAtomRegistry } from "./atom-registry"; import { useProjects, useThreadShells } from "./entities"; @@ -221,7 +222,7 @@ export function useThreadOutboxDrain(): void { messageId: queuedMessage.messageId, role: "user", text: queuedMessage.text, - attachments: queuedMessage.attachments, + attachments: toUploadChatImageAttachments(queuedMessage.attachments), }, modelSelection: settings.modelSelection, runtimeMode: settings.runtimeMode,