feat(chat): robust thread support (complex)

This commit is contained in:
Shivering Ocelot committed 2026-09-25 19:56:55 -05:00
1 parent c588a12160
commit 73b789b586
3 files changed
+81 -20

No files matched your search

+58 -10
View File
@@ -48,6 +48,19 @@ function selectEarliest (messages) {
return selected
}
function messageCopy (it) {
return {
id: it.id,
type: it.kind,
format: it.format,
subkind: it.subkind,
sender: it.sender,
content: it.content,
threadId: it.threadId,
time: it.time,
};
}
export class Thread {
constructor (obj, session) {
Object.assign(this, {
@@ -159,6 +172,7 @@ export class ChatSession {
this._messageNonce = 1;
this._unsubscribeChat = null;
this._unsubscribeRuns = null;
this._unsubscribeThreads = null;
this._runListener = null;
this._rescroll = null;
}
@@ -211,6 +225,14 @@ export class ChatSession {
this._unsubscribeChat = backend.onChatMessages(chatId, it => this.receiveMessage(it));
this._unsubscribeRuns = runs.onChatMessage(chatId, event => this.trackRun(event));
this._unsubscribeThreads = backend.onAction('thread-update', event => {
let thread = this.threadMap[event.id];
if (thread) {
let data = {...event};
delete data.action;
thread.update(data)
};
});
// Load threads
let {threads} = await backend.sendAction("get-chat-threads", {chatId});
@@ -241,6 +263,8 @@ export class ChatSession {
this._unsubscribeChat = null;
if (this._unsubscribeRuns) this._unsubscribeRuns();
this._unsubscribeRuns = null;
if (this._unsubscribeThreads) this._unsubscribeThreads();
this._unsubscribeThreads = null;
if (this._runListener) this._runListener.cancel();
this._runListener = null;
}
@@ -435,15 +459,17 @@ export class ChatSession {
if (this.creatingThread.value) {
// Send the target message to a fork
let res = await backend.sendAction("fork-chat-thread", {
chatId: this.chatId,
threadId: previousThreadId,
messageId,
});
// Send the target message to a fork if it doesn't start its thread
if (previousThreadId == messageThreadId) {
await backend.sendAction("fork-chat-thread", {
chatId: this.chatId,
threadId: previousThreadId,
messageId,
});
}
// Then create a new fork again for the new message
res = await backend.sendAction("fork-chat-thread", {
// Create a new fork for the new message
let res = await backend.sendAction("fork-chat-thread", {
chatId: this.chatId,
threadId: previousThreadId,
});
@@ -598,8 +624,28 @@ export class ChatSession {
this.currentThreadId.value = threadId;
this.activeThreadIds = [];
// Activate and load all threads in the ancestry
let thread = this.getThread(threadId);
// If the thread has children, find the most recent leaf
let stack = [thread]
let lastMessage = null;
while (stack.length) {
let next = stack.pop();
let last = next.getLastMessage();
if (last && (!lastMessage || last.id > lastMessage.id)) {
lastMessage = last;
}
stack = stack.concat(thread.children);
}
// Invariably activate the thread with the most recent message
if (lastMessage) {
thread = this.getThread(lastMessage.threadId);
}
// Activate and load all threads in the ancestry
while (true) {
this.activeThreadIds.push(thread.id);
@@ -624,6 +670,8 @@ export class ChatSession {
thread = this.getThread(thread.parentId);
}
this.computeChatEntries();
this.rescroll();
}
@@ -732,7 +780,7 @@ export class ChatSession {
firstMessage ? {id: firstMessage.id, position: 'before'} :
null;
if (!attach) return;
if (!attach) continue;
headers.push({
// claim just before id