summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/db
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-27 18:35:18 +0900
committerAdam Malczewski <[email protected]>2026-05-27 18:35:18 +0900
commitca6ee91c5e1167b1929eedbb96c76dfa24e7d026 (patch)
treebc23acac2e7caaf2e59eacbc21bfc9b41f3c1458 /packages/core/src/db
parentda57842686ebfd157396551fc76d0c18f7676335 (diff)
downloaddispatch-ca6ee91c5e1167b1929eedbb96c76dfa24e7d026.tar.gz
dispatch-ca6ee91c5e1167b1929eedbb96c76dfa24e7d026.zip
refactor: ChatMessage.chunks[] union — interleaved thinking, tool batching, error/system chunks
Diffstat (limited to 'packages/core/src/db')
-rw-r--r--packages/core/src/db/index.ts1
-rw-r--r--packages/core/src/db/messages.ts68
2 files changed, 47 insertions, 22 deletions
diff --git a/packages/core/src/db/index.ts b/packages/core/src/db/index.ts
index 81b474e..e63b266 100644
--- a/packages/core/src/db/index.ts
+++ b/packages/core/src/db/index.ts
@@ -99,7 +99,6 @@ export function getDatabase(): Database {
seq INTEGER NOT NULL,
role TEXT NOT NULL,
content_json TEXT NOT NULL,
- thinking TEXT,
created_at INTEGER NOT NULL
)`);
diff --git a/packages/core/src/db/messages.ts b/packages/core/src/db/messages.ts
index 80a1f22..34b69e5 100644
--- a/packages/core/src/db/messages.ts
+++ b/packages/core/src/db/messages.ts
@@ -1,21 +1,29 @@
+import type { Chunk, MessageRole } from "../types/index.js";
import { getDatabase } from "./index.js";
+/**
+ * A persisted message row, with `content_json` already parsed into a `Chunk[]`.
+ * Mirrors the new schema (no `thinking` column — that lived under the old
+ * `content + toolCalls + toolResults + thinking` model).
+ */
export interface MessageRow {
id: string;
tabId: string;
seq: number;
- role: string;
- contentJson: string;
- thinking: string | null;
+ role: MessageRole;
+ chunks: Chunk[];
createdAt: number;
}
+/**
+ * Append a new message to the tab. Caller passes the already-serialized
+ * chunk list as `contentJson` (i.e. `JSON.stringify(chunks)`).
+ */
export function appendMessage(
tabId: string,
id: string,
- role: string,
+ role: MessageRole,
contentJson: string,
- thinking?: string,
): void {
const db = getDatabase();
const maxSeq = db
@@ -23,40 +31,58 @@ export function appendMessage(
.get({ $tabId: tabId }) as { max_seq: number };
const seq = (maxSeq?.max_seq ?? -1) + 1;
db.query(
- `INSERT INTO messages (id, tab_id, seq, role, content_json, thinking, created_at)
- VALUES ($id, $tabId, $seq, $role, $contentJson, $thinking, $now)`,
+ `INSERT INTO messages (id, tab_id, seq, role, content_json, created_at)
+ VALUES ($id, $tabId, $seq, $role, $contentJson, $now)`,
).run({
$id: id,
$tabId: tabId,
$seq: seq,
$role: role,
$contentJson: contentJson,
- $thinking: thinking ?? null,
$now: Date.now(),
});
}
-export function updateMessage(id: string, contentJson: string, thinking?: string): void {
+/**
+ * Replace the persisted chunks for an existing message. `contentJson` is
+ * the already-serialized chunk list.
+ */
+export function updateMessage(id: string, contentJson: string): void {
const db = getDatabase();
- db.query(
- "UPDATE messages SET content_json = $contentJson, thinking = $thinking WHERE id = $id",
- ).run({ $id: id, $contentJson: contentJson, $thinking: thinking ?? null });
+ db.query("UPDATE messages SET content_json = $contentJson WHERE id = $id").run({
+ $id: id,
+ $contentJson: contentJson,
+ });
}
+/**
+ * Read all messages for a tab in seq order. `content_json` is parsed into
+ * `Chunk[]` here so callers don't have to. If a row's JSON is malformed,
+ * the message is returned with an empty chunk list rather than throwing.
+ */
export function getMessagesForTab(tabId: string): MessageRow[] {
const db = getDatabase();
const rows = db
.query("SELECT * FROM messages WHERE tab_id = $tabId ORDER BY seq ASC")
.all({ $tabId: tabId }) as Array<Record<string, unknown>>;
- return rows.map((row) => ({
- id: row.id as string,
- tabId: row.tab_id as string,
- seq: row.seq as number,
- role: row.role as string,
- contentJson: row.content_json as string,
- thinking: row.thinking as string | null,
- createdAt: row.created_at as number,
- }));
+ return rows.map((row) => {
+ const rawJson = row.content_json as string;
+ let chunks: Chunk[];
+ try {
+ const parsed = JSON.parse(rawJson);
+ chunks = Array.isArray(parsed) ? (parsed as Chunk[]) : [];
+ } catch {
+ chunks = [];
+ }
+ return {
+ id: row.id as string,
+ tabId: row.tab_id as string,
+ seq: row.seq as number,
+ role: row.role as MessageRole,
+ chunks,
+ createdAt: row.created_at as number,
+ };
+ });
}
export function clearMessagesForTab(tabId: string): void {