diff --git a/scripts/backfill/__tests__/migrateRoom.test.ts b/scripts/backfill/__tests__/migrateRoom.test.ts index 7ee86fdb8..40bd58eac 100644 --- a/scripts/backfill/__tests__/migrateRoom.test.ts +++ b/scripts/backfill/__tests__/migrateRoom.test.ts @@ -80,8 +80,15 @@ describe("migrateRoom", () => { expect(insertChat).toHaveBeenCalledTimes(1); // one batch call containing only the well-formed message expect(upsertChatMessages).toHaveBeenCalledTimes(1); + // `parts` stores the full UIMessage (matching the workflow's native + // persist path), not the bare parts array. expect(upsertChatMessages).toHaveBeenCalledWith([ - expect.objectContaining({ id: "m1", chat_id: "room-1", role: "user" }), + expect.objectContaining({ + id: "m1", + chat_id: "room-1", + role: "user", + parts: { id: "m1", role: "user", parts: [{ type: "text", text: "hi" }] }, + }), ]); expect(stats.messagesWritten).toBe(1); expect(stats.messagesMalformed).toBe(1); diff --git a/scripts/backfill/migrateRoom.ts b/scripts/backfill/migrateRoom.ts index c95209ec2..79f8c192f 100644 --- a/scripts/backfill/migrateRoom.ts +++ b/scripts/backfill/migrateRoom.ts @@ -57,11 +57,16 @@ async function migrateMessages( malformed++; continue; } + // The workflow persists the FULL UIMessage in the `parts` column + // (see lib/chat/persistLatestUserMessage / persistAssistantMessage), + // and the read path (getSessionChatHandler) returns `parts` verbatim + // expecting a UIMessage. Store the same shape so migrated history + // deserializes identically to natively-written chats. rows.push({ id: memory.id, chat_id: roomId, role: content.role, - parts: content.parts, + parts: { id: memory.id, role: content.role, parts: content.parts }, created_at: memory.updated_at, }); }