feat(ghostwriter): branching conversations with edit and per-message regenerate (#290)

Turn Ghostwriter's flat message list into a tree structure, enabling
Claude.ai/ChatGPT-style branching conversations.

**Data model**: Add `parentMessageId` and `activeChildId` to messages,
`activeRootMessageId` to threads. Migration backfills existing messages
into a linear chain and links regenerated messages as siblings.

**Backend**: Tree-walking queries (getActivePathFromRoot, getAncestorPath,
getSiblingsOf), rewritten history builder that follows the ancestor path,
new editMessage and switchBranch services, regenerate now works on any
assistant message (not just the latest).

**Frontend**: BranchNavigator component (← 2/3 → arrows), inline edit on
user messages, per-message regenerate on assistant messages, regenerate
button removed from composer (now per-message).

**Infra**: Pin Node 22 via Volta to prevent ABI mismatches with
better-sqlite3 across environments.
This commit is contained in:
0x1355
2026-03-19 11:25:00 +00:00
committed by GitHub
parent f19471ab58
commit 4787f4d151
14 changed files with 1085 additions and 181 deletions
+12
View File
@@ -29,6 +29,7 @@ export interface JobChatThread {
createdAt: string;
updatedAt: string;
lastMessageAt: string | null;
activeRootMessageId: string | null;
}
export interface JobChatMessage {
@@ -42,10 +43,21 @@ export interface JobChatMessage {
tokensOut: number | null;
version: number;
replacesMessageId: string | null;
parentMessageId: string | null;
activeChildId: string | null;
createdAt: string;
updatedAt: string;
}
export interface BranchInfo {
/** The message ID this branch info belongs to (the currently active sibling). */
messageId: string;
/** Ordered sibling IDs at this branch point (by createdAt). */
siblingIds: string[];
/** 0-based index of the active sibling within siblingIds. */
activeIndex: number;
}
export interface JobChatRun {
id: string;
threadId: string;