| @@ -73,6 +73,13 @@ import { · 72 unmodified lines |
| 73 | 73 | upsertDocument as upsertDocumentEffect, |
| 74 | 74 | type DocumentRow, |
| 75 | 75 | } from "./persistence/documentsRepo"; |
| 76 | +import { |
| 77 | + insertProviderEvent as insertProviderEventEffect, |
| 78 | + listCompletedItemEventsBySessionTurn as listCompletedItemEventsBySessionTurnEffect, |
| 79 | + listCompletedItemEventsByThreadTurn as listCompletedItemEventsByThreadTurnEffect, |
| 80 | + listCompletedItemEventsByTurn as listCompletedItemEventsByTurnEffect, |
| 81 | + type CompletedProviderItemRow, |
| 82 | +} from "./persistence/providerEventsRepo"; |
| 76 | 83 | import { |
| 77 | 84 | appendStateEvent as appendStateEventEffect, |
| 78 | 85 | listStateEventsAfterSeq as listStateEventsAfterSeqEffect, |
| @@ -1570,38 +1577,55 @@ export class PersistenceService extends EventEmitter<PersistenceServiceEvents> { · 1491 unmodified lines |
| 1570 | 1577 | sessionId?: string; |
| 1571 | 1578 | runtimeThreadId?: string | null; |
| 1572 | 1579 | }): string | undefined { |
| 1573 | | − const queries: Array<{ sql: string; params: unknown[] }> = []; |
| 1580 | + const rowGroups: CompletedProviderItemRow[][] = []; |
| 1574 | 1581 | if (input.sessionId) { |
| 1575 | | − queries.push({ |
| 1576 | | − sql: `SELECT item_id, payload_json |
| 1577 | | − FROM provider_events |
| 1578 | | − WHERE session_id = ? AND turn_id = ? AND method = 'item/completed' |
| 1579 | | − ORDER BY created_at DESC;`, |
| 1580 | | − params: [input.sessionId, input.turnId], |
| 1581 | | − }); |
| 1582 | + rowGroups.push( |
| 1583 | + this.runWithEffectSql( |
| 1584 | + listCompletedItemEventsBySessionTurnEffect({ |
| 1585 | + sessionId: input.sessionId, |
| 1586 | + turnId: input.turnId, |
| 1587 | + }), |
| 1588 | + () => |
| 1589 | + this.db.prepare( |
| 1590 | + `SELECT item_id, payload_json |
| 1591 | + FROM provider_events |
| 1592 | + WHERE session_id = ? AND turn_id = ? AND method = 'item/completed' |
| 1593 | + ORDER BY created_at DESC;`, |
| 1594 | + ).all(input.sessionId, input.turnId) as CompletedProviderItemRow[], |
| 1595 | + ), |
| 1596 | + ); |
| 1582 | 1597 | } |
| 1583 | 1598 | if (input.runtimeThreadId) { |
| 1584 | | − queries.push({ |
| 1585 | | − sql: `SELECT item_id, payload_json |
| 1586 | | − FROM provider_events |
| 1587 | | − WHERE thread_id = ? AND turn_id = ? AND method = 'item/completed' |
| 1588 | | − ORDER BY created_at DESC;`, |
| 1589 | | − params: [input.runtimeThreadId, input.turnId], |
| 1590 | | − }); |
| 1599 | + rowGroups.push( |
| 1600 | + this.runWithEffectSql( |
| 1601 | + listCompletedItemEventsByThreadTurnEffect({ |
| 1602 | + runtimeThreadId: input.runtimeThreadId, |
| 1603 | + turnId: input.turnId, |
| 1604 | + }), |
| 1605 | + () => |
| 1606 | + this.db.prepare( |
| 1607 | + `SELECT item_id, payload_json |
| 1608 | + FROM provider_events |
| 1609 | + WHERE thread_id = ? AND turn_id = ? AND method = 'item/completed' |
| 1610 | + ORDER BY created_at DESC;`, |
| 1611 | + ).all(input.runtimeThreadId, input.turnId) as CompletedProviderItemRow[], |
| 1612 | + ), |
| 1613 | + ); |
| 1591 | 1614 | } |
| 1592 | | − queries.push({ |
| 1593 | | − sql: `SELECT item_id, payload_json |
| 1594 | | − FROM provider_events |
| 1595 | | − WHERE turn_id = ? AND method = 'item/completed' |
| 1596 | | − ORDER BY created_at DESC;`, |
| 1597 | | − params: [input.turnId], |
| 1598 | | − }); |
| 1615 | + rowGroups.push( |
| 1616 | + this.runWithEffectSql( |
| 1617 | + listCompletedItemEventsByTurnEffect(input.turnId), |
| 1618 | + () => |
| 1619 | + this.db.prepare( |
| 1620 | + `SELECT item_id, payload_json |
| 1621 | + FROM provider_events |
| 1622 | + WHERE turn_id = ? AND method = 'item/completed' |
| 1623 | + ORDER BY created_at DESC;`, |
| 1624 | + ).all(input.turnId) as CompletedProviderItemRow[], |
| 1625 | + ), |
| 1626 | + ); |
| 1599 | 1627 | |
| 1600 | | − for (const query of queries) { |
| 1601 | | − const rows = this.db.prepare(query.sql).all(...query.params) as Array<{ |
| 1602 | | − item_id: string | null; |
| 1603 | | − payload_json: string | null; |
| 1604 | | − }>; |
| 1628 | + for (const rows of rowGroups) { |
| 1605 | 1629 | for (const row of rows) { |
| 1606 | 1630 | const payload = row.payload_json ? this.tryParseJson(row.payload_json) : null; |
| 1607 | 1631 | const item = asObject(asObject(payload)?.item); |
| @@ -1774,43 +1798,64 @@ export class PersistenceService extends EventEmitter<PersistenceServiceEvents> { · 166 unmodified lines |
| 1774 | 1798 | private insertProviderEvent(event: ProviderEvent): ProviderEventInsertResult { |
| 1775 | 1799 | const runtimeThreadId = event.threadId ?? parseThreadIdFromEventPayload(event.payload); |
| 1776 | 1800 | const payloadJson = event.payload === undefined ? null : JSON.stringify(event.payload); |
| 1777 | | − const result = this.db |
| 1778 | | − .prepare( |
| 1779 | | − `INSERT OR IGNORE INTO provider_events ( |
| 1780 | | − id, |
| 1781 | | − session_id, |
| 1782 | | − provider, |
| 1783 | | − kind, |
| 1784 | | − method, |
| 1785 | | − thread_id, |
| 1786 | | − turn_id, |
| 1787 | | − item_id, |
| 1788 | | − request_id, |
| 1789 | | − request_kind, |
| 1790 | | − text_delta, |
| 1791 | | − message, |
| 1792 | | − payload_json, |
| 1793 | | − created_at |
| 1794 | | − ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`, |
| 1795 | | − ) |
| 1796 | | − .run( |
| 1797 | | − event.id, |
| 1798 | | − event.sessionId, |
| 1799 | | − event.provider, |
| 1800 | | − event.kind, |
| 1801 | | − event.method, |
| 1802 | | − runtimeThreadId, |
| 1803 | | − event.turnId ?? null, |
| 1804 | | − event.itemId ?? null, |
| 1805 | | − event.requestId ?? null, |
| 1806 | | − event.requestKind ?? null, |
| 1807 | | − event.textDelta ?? null, |
| 1808 | | − event.message ?? null, |
| 1801 | + const changes = this.runWithEffectSql( |
| 1802 | + insertProviderEventEffect({ |
| 1803 | + id: event.id, |
| 1804 | + sessionId: event.sessionId, |
| 1805 | + provider: event.provider, |
| 1806 | + kind: event.kind, |
| 1807 | + method: event.method, |
| 1808 | + runtimeThreadId: runtimeThreadId ?? null, |
| 1809 | + turnId: event.turnId ?? null, |
| 1810 | + itemId: event.itemId ?? null, |
| 1811 | + requestId: event.requestId ?? null, |
| 1812 | + requestKind: event.requestKind ?? null, |
| 1813 | + textDelta: event.textDelta ?? null, |
| 1814 | + message: event.message ?? null, |
| 1809 | 1815 | payloadJson, |
| 1810 | | − event.createdAt, |
| 1811 | | − ) as { changes?: number | bigint }; |
| 1816 | + createdAt: event.createdAt, |
| 1817 | + }), |
| 1818 | + () => { |
| 1819 | + const result = this.db |
| 1820 | + .prepare( |
| 1821 | + `INSERT OR IGNORE INTO provider_events ( |
| 1822 | + id, |
| 1823 | + session_id, |
| 1824 | + provider, |
| 1825 | + kind, |
| 1826 | + method, |
| 1827 | + thread_id, |
| 1828 | + turn_id, |
| 1829 | + item_id, |
| 1830 | + request_id, |
| 1831 | + request_kind, |
| 1832 | + text_delta, |
| 1833 | + message, |
| 1834 | + payload_json, |
| 1835 | + created_at |
| 1836 | + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`, |
| 1837 | + ) |
| 1838 | + .run( |
| 1839 | + event.id, |
| 1840 | + event.sessionId, |
| 1841 | + event.provider, |
| 1842 | + event.kind, |
| 1843 | + event.method, |
| 1844 | + runtimeThreadId, |
| 1845 | + event.turnId ?? null, |
| 1846 | + event.itemId ?? null, |
| 1847 | + event.requestId ?? null, |
| 1848 | + event.requestKind ?? null, |
| 1849 | + event.textDelta ?? null, |
| 1850 | + event.message ?? null, |
| 1851 | + payloadJson, |
| 1852 | + event.createdAt, |
| 1853 | + ) as { changes?: number | bigint }; |
| 1854 | + return toSafeInteger(result.changes, 0); |
| 1855 | + }, |
| 1856 | + ); |
| 1812 | 1857 | return { |
| 1813 | | − inserted: toSafeInteger(result.changes, 0) > 0, |
| 1858 | + inserted: changes > 0, |
| 1814 | 1859 | runtimeThreadId: runtimeThreadId ?? null, |
| 1815 | 1860 | }; |
| 1816 | 1861 | } |