feat(webui): add assistant reply fork-from-here

This commit is contained in:
Bayern4ever-dot
2026-06-10 04:26:06 +08:00
committed by Xubin Ren
parent 4a58b83acc
commit 03bca4c0a9
30 changed files with 1358 additions and 36 deletions
+31
View File
@@ -348,6 +348,29 @@ export class NanobotClient {
});
}
/** Ask the server to create a non-destructive fork before a user-message index. */
forkChat(
sourceChatId: string,
beforeUserIndex: number,
timeoutMs: number = 5_000,
): Promise<string> {
if (this.pendingNewChat) {
return Promise.reject(new Error("newChat already in flight"));
}
return new Promise<string>((resolve, reject) => {
const timer = setTimeout(() => {
this.pendingNewChat = null;
reject(new Error("forkChat timed out"));
}, timeoutMs);
this.pendingNewChat = { resolve, reject, timer };
this.queueSend({
type: "fork_chat",
source_chat_id: sourceChatId,
before_user_index: beforeUserIndex,
});
});
}
attach(chatId: string): void {
this.knownChats.add(chatId);
if (this.socket?.readyState === WS_OPEN) {
@@ -481,6 +504,14 @@ export class NanobotClient {
}
}
if (parsed.event === "error" && this.pendingNewChat) {
clearTimeout(this.pendingNewChat.timer);
const detail = typeof parsed.detail === "string" ? parsed.detail : "server error";
const reason = typeof parsed.reason === "string" && parsed.reason ? `:${parsed.reason}` : "";
this.pendingNewChat.reject(new Error(`${detail}${reason}`));
this.pendingNewChat = null;
}
const chatId = (parsed as { chat_id?: string }).chat_id;
if (chatId) {
this.recordGoalStatusForRunStrip(chatId, parsed);
+1
View File
@@ -877,6 +877,7 @@ export interface FilePreviewPayload {
export type Outbound =
| { type: "new_chat"; workspace_scope?: WorkspaceScopePayload }
| { type: "fork_chat"; source_chat_id: string; before_user_index: number }
| { type: "attach"; chat_id: string }
| { type: "set_workspace_scope"; chat_id: string; workspace_scope: WorkspaceScopePayload }
| { type: "transcribe_audio"; request_id: string; data_url: string; duration_ms?: number }