Skip to content

Commit cdce325

Browse files
sethconvexclaude
andcommitted
Fix #188: Support userId in UIMessage for multi-user threads
When converting MessageDoc to UIMessage via toUIMessages(), the userId field was being lost. This made it difficult to identify which user sent each message in multi-user thread conversations. Changes: - Add userId?: string to UIMessage type definition - Include userId in createSystemUIMessage() - Include userId in createUserUIMessage() - Include userId in createAssistantUIMessage() (from first message) - Add tests for userId preservation across all message types Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 74cbf01 commit cdce325

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

src/UIMessages.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export type UIMessage<
4242
stepOrder: number;
4343
status: UIStatus;
4444
agentName?: string;
45+
userId?: string;
4546
text: string;
4647
_creationTime: number;
4748
};
@@ -288,6 +289,7 @@ function createSystemUIMessage<
288289
text,
289290
role: "system",
290291
agentName: message.agentName,
292+
userId: message.userId,
291293
parts: [{ type: "text", text, ...partCommon } satisfies TextUIPart],
292294
metadata: message.metadata,
293295
};
@@ -347,6 +349,7 @@ function createUserUIMessage<
347349
key: `${message.threadId}-${message.order}-${message.stepOrder}`,
348350
text,
349351
role: "user",
352+
userId: message.userId,
350353
parts,
351354
metadata: message.metadata,
352355
};
@@ -370,6 +373,7 @@ function createAssistantUIMessage<
370373
stepOrder: firstMessage.stepOrder,
371374
key: `${firstMessage.threadId}-${firstMessage.order}-${firstMessage.stepOrder}`,
372375
agentName: firstMessage.agentName,
376+
userId: firstMessage.userId,
373377
};
374378

375379
// Get status from last message

src/toUIMessages.test.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,4 +728,131 @@ describe("toUIMessages", () => {
728728
expect(textParts).toHaveLength(1);
729729
expect(textParts[0].text).toBe("The result is 42.");
730730
});
731+
732+
describe("userId preservation", () => {
733+
it("preserves userId in user messages", () => {
734+
const messages = [
735+
baseMessageDoc({
736+
userId: "user123",
737+
message: {
738+
role: "user",
739+
content: "Hello!",
740+
},
741+
text: "Hello!",
742+
}),
743+
];
744+
const uiMessages = toUIMessages(messages);
745+
expect(uiMessages).toHaveLength(1);
746+
expect(uiMessages[0].role).toBe("user");
747+
expect(uiMessages[0].userId).toBe("user123");
748+
});
749+
750+
it("preserves userId in system messages", () => {
751+
const messages = [
752+
baseMessageDoc({
753+
userId: "user456",
754+
message: {
755+
role: "system",
756+
content: "System prompt",
757+
},
758+
text: "System prompt",
759+
}),
760+
];
761+
const uiMessages = toUIMessages(messages);
762+
expect(uiMessages).toHaveLength(1);
763+
expect(uiMessages[0].role).toBe("system");
764+
expect(uiMessages[0].userId).toBe("user456");
765+
});
766+
767+
it("preserves userId in assistant messages", () => {
768+
const messages = [
769+
baseMessageDoc({
770+
userId: "user789",
771+
message: {
772+
role: "assistant",
773+
content: "Hi there!",
774+
},
775+
text: "Hi there!",
776+
}),
777+
];
778+
const uiMessages = toUIMessages(messages);
779+
expect(uiMessages).toHaveLength(1);
780+
expect(uiMessages[0].role).toBe("assistant");
781+
expect(uiMessages[0].userId).toBe("user789");
782+
});
783+
784+
it("preserves userId from first message in grouped assistant messages", () => {
785+
const messages = [
786+
baseMessageDoc({
787+
_id: "msg1",
788+
userId: "userA",
789+
order: 1,
790+
stepOrder: 1,
791+
tool: true,
792+
message: {
793+
role: "assistant",
794+
content: [
795+
{
796+
type: "tool-call",
797+
toolName: "myTool",
798+
toolCallId: "call1",
799+
args: {},
800+
},
801+
],
802+
},
803+
text: "",
804+
}),
805+
baseMessageDoc({
806+
_id: "msg2",
807+
userId: "userA",
808+
order: 1,
809+
stepOrder: 2,
810+
tool: true,
811+
message: {
812+
role: "tool",
813+
content: [
814+
{
815+
type: "tool-result",
816+
toolCallId: "call1",
817+
toolName: "myTool",
818+
output: { type: "text", value: "result" },
819+
},
820+
],
821+
},
822+
text: "",
823+
}),
824+
baseMessageDoc({
825+
_id: "msg3",
826+
userId: "userA",
827+
order: 1,
828+
stepOrder: 3,
829+
message: {
830+
role: "assistant",
831+
content: "Done!",
832+
},
833+
text: "Done!",
834+
}),
835+
];
836+
const uiMessages = toUIMessages(messages);
837+
expect(uiMessages).toHaveLength(1);
838+
expect(uiMessages[0].role).toBe("assistant");
839+
expect(uiMessages[0].userId).toBe("userA");
840+
});
841+
842+
it("handles undefined userId gracefully", () => {
843+
const messages = [
844+
baseMessageDoc({
845+
// No userId provided
846+
message: {
847+
role: "user",
848+
content: "Hello!",
849+
},
850+
text: "Hello!",
851+
}),
852+
];
853+
const uiMessages = toUIMessages(messages);
854+
expect(uiMessages).toHaveLength(1);
855+
expect(uiMessages[0].userId).toBeUndefined();
856+
});
857+
});
731858
});

0 commit comments

Comments
 (0)