+ instanceId={props.instanceId}
+ sessionId={activeSession.id}
+ loading={messagesLoading()}
+ onRevert={handleRevert}
+ onFork={handleFork}
+ registerScrollToBottom={(fn) => {
+ scrollToBottomHandle = fn
+ }}
+ />
+
value + 1)
+ }
}
function attachPermissionToToolPart(instanceId: string, permission: Permission, active: boolean): void {
diff --git a/packages/ui/src/stores/message-v2/instance-store.ts b/packages/ui/src/stores/message-v2/instance-store.ts
index 5aace3d0..111d256b 100644
--- a/packages/ui/src/stores/message-v2/instance-store.ts
+++ b/packages/ui/src/stores/message-v2/instance-store.ts
@@ -439,10 +439,10 @@ export function createInstanceMessageStore(instanceId: string): InstanceMessageS
bufferPendingPart({ messageId: input.messageId, part: input.part, receivedAt: Date.now() })
return
}
-
+
const partId = ensurePartId(input.messageId, input.part, message.partIds.length)
const cloned = clonePart(input.part)
-
+
setState(
"messages",
input.messageId,
@@ -463,8 +463,13 @@ export function createInstanceMessageStore(instanceId: string): InstanceMessageS
}
}),
)
+
+ // Any part update can change the rendered height of the message
+ // list, so we treat it as a session revision for scroll purposes.
+ bumpSessionRevision(message.sessionId)
}
+
function flushPendingParts(messageId: string) {
const pending = state.pendingParts[messageId]
if (!pending || pending.length === 0) {
diff --git a/packages/ui/src/stores/session-events.ts b/packages/ui/src/stores/session-events.ts
index 5c6962ff..5c23cb85 100644
--- a/packages/ui/src/stores/session-events.ts
+++ b/packages/ui/src/stores/session-events.ts
@@ -81,20 +81,25 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
if (event.type === "message.part.updated") {
const rawPart = event.properties?.part
if (!rawPart) return
-
+
const part = normalizeMessagePart(rawPart)
- const sessionId = typeof part.sessionID === "string" ? part.sessionID : undefined
- const messageId = typeof part.messageID === "string" ? part.messageID : undefined
+ const messageInfo = (event as any)?.properties?.message as MessageInfo | undefined
+
+ const fallbackSessionId = typeof messageInfo?.sessionID === "string" ? messageInfo.sessionID : undefined
+ const fallbackMessageId = typeof messageInfo?.id === "string" ? messageInfo.id : undefined
+
+ const sessionId = typeof part.sessionID === "string" ? part.sessionID : fallbackSessionId
+ const messageId = typeof part.messageID === "string" ? part.messageID : fallbackMessageId
if (!sessionId || !messageId) return
-
+
const session = instanceSessions.get(sessionId)
if (!session) return
-
+
const store = messageStoreBus.getOrCreate(instanceId)
- const messageInfo = (event as any)?.properties?.message as MessageInfo | undefined
const role: MessageRole = resolveMessageRole(messageInfo)
const createdAt = typeof messageInfo?.time?.created === "number" ? messageInfo.time.created : Date.now()
+
let record = store.getMessage(messageId)
if (!record) {
const pendingId = findPendingMessageId(store, sessionId, role)
@@ -119,8 +124,9 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
if (messageInfo) {
upsertMessageInfoV2(instanceId, messageInfo, { status: "streaming" })
}
+
+ applyPartUpdateV2(instanceId, { ...part, sessionID: sessionId, messageID: messageId })
- applyPartUpdateV2(instanceId, part)
updateSessionInfo(instanceId, sessionId)
refreshPermissionsForSession(instanceId, sessionId)