(sourceAIChatId: string, upToMessageId: string, title?: string)
| 676 | } |
| 677 | |
| 678 | forkAIChat(sourceAIChatId: string, upToMessageId: string, title?: string): AIChat { |
| 679 | const db = this.getDb() |
| 680 | const source = this.getAIChat(sourceAIChatId) |
| 681 | if (!source) { |
| 682 | throw new Error('Source AI chat not found') |
| 683 | } |
| 684 | |
| 685 | const activePath = this.getActivePathRows(sourceAIChatId) |
| 686 | const cutIndex = activePath.findIndex((row) => row.id === upToMessageId) |
| 687 | if (cutIndex < 0) { |
| 688 | throw new Error('Message not on active path') |
| 689 | } |
| 690 | |
| 691 | const messagesToCopy = activePath.slice(0, cutIndex + 1) |
| 692 | const now = Math.floor(Date.now() / 1000) |
| 693 | const newConvId = this.generateId('conv') |
| 694 | const forkTitle = title || `${source.title || 'Untitled'} (fork)` |
| 695 | |
| 696 | db.prepare( |
| 697 | `INSERT INTO ai_chat (id, session_id, title, assistant_id, active_message_id, created_at, updated_at) |
| 698 | VALUES (?, ?, ?, ?, NULL, ?, ?)` |
| 699 | ).run(newConvId, source.sessionId, forkTitle, source.assistantId, now, now) |
| 700 | |
| 701 | const idMap = new Map<string, string>() |
| 702 | let lastNewId: string | null = null |
| 703 | |
| 704 | for (const row of messagesToCopy) { |
| 705 | const newMsgId = this.generateId('msg') |
| 706 | idMap.set(row.id, newMsgId) |
| 707 | const newParentId = row.parentId ? (idMap.get(row.parentId) ?? null) : null |
| 708 | |
| 709 | db.prepare( |
| 710 | `INSERT INTO ai_message ( |
| 711 | id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count, |
| 712 | content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index |
| 713 | ) |
| 714 | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, 0)` |
| 715 | ).run( |
| 716 | newMsgId, |
| 717 | newConvId, |
| 718 | row.role, |
| 719 | row.content, |
| 720 | row.timestamp, |
| 721 | row.dataKeywords, |
| 722 | row.dataMessageCount, |
| 723 | row.contentBlocks, |
| 724 | row.tokenUsage, |
| 725 | newParentId, |
| 726 | newMsgId |
| 727 | ) |
| 728 | lastNewId = newMsgId |
| 729 | } |
| 730 | |
| 731 | if (lastNewId) { |
| 732 | db.prepare('UPDATE ai_chat SET active_message_id = ? WHERE id = ?').run(lastNewId, newConvId) |
| 733 | } |
| 734 | |
| 735 | return { |
nothing calls this directly
no test coverage detected