Skip to content

Commit 63e3855

Browse files
committed
sync
1 parent f40685a commit 63e3855

9 files changed

Lines changed: 83 additions & 66 deletions

File tree

packages/opencode/migration/20260127173238_melted_union_jack/migration.sql

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ CREATE TABLE `project` (
1414
CREATE TABLE `message` (
1515
`id` text PRIMARY KEY,
1616
`session_id` text NOT NULL,
17-
`created_at` integer NOT NULL,
17+
`time_created` integer NOT NULL,
18+
`time_updated` integer NOT NULL,
1819
`data` text NOT NULL,
1920
CONSTRAINT `fk_message_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
2021
);
@@ -23,12 +24,16 @@ CREATE TABLE `part` (
2324
`id` text PRIMARY KEY,
2425
`message_id` text NOT NULL,
2526
`session_id` text NOT NULL,
27+
`time_created` integer NOT NULL,
28+
`time_updated` integer NOT NULL,
2629
`data` text NOT NULL,
2730
CONSTRAINT `fk_part_message_id_message_id_fk` FOREIGN KEY (`message_id`) REFERENCES `message`(`id`) ON DELETE CASCADE
2831
);
2932
--> statement-breakpoint
3033
CREATE TABLE `permission` (
3134
`project_id` text PRIMARY KEY,
35+
`time_created` integer NOT NULL,
36+
`time_updated` integer NOT NULL,
3237
`data` text NOT NULL,
3338
CONSTRAINT `fk_permission_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
3439
);
@@ -46,10 +51,7 @@ CREATE TABLE `session` (
4651
`summary_deletions` integer,
4752
`summary_files` integer,
4853
`summary_diffs` text,
49-
`revert_message_id` text,
50-
`revert_part_id` text,
51-
`revert_snapshot` text,
52-
`revert_diff` text,
54+
`revert` text,
5355
`permission` text,
5456
`time_created` integer NOT NULL,
5557
`time_updated` integer NOT NULL,
@@ -65,6 +67,8 @@ CREATE TABLE `todo` (
6567
`status` text NOT NULL,
6668
`priority` text NOT NULL,
6769
`position` integer NOT NULL,
70+
`time_created` integer NOT NULL,
71+
`time_updated` integer NOT NULL,
6872
CONSTRAINT `todo_pk` PRIMARY KEY(`session_id`, `id`),
6973
CONSTRAINT `fk_todo_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
7074
);
@@ -74,6 +78,8 @@ CREATE TABLE `session_share` (
7478
`id` text NOT NULL,
7579
`secret` text NOT NULL,
7680
`url` text NOT NULL,
81+
`time_created` integer NOT NULL,
82+
`time_updated` integer NOT NULL,
7783
CONSTRAINT `fk_session_share_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
7884
);
7985
--> statement-breakpoint
@@ -82,4 +88,4 @@ CREATE INDEX `part_message_idx` ON `part` (`message_id`);--> statement-breakpoin
8288
CREATE INDEX `part_session_idx` ON `part` (`session_id`);--> statement-breakpoint
8389
CREATE INDEX `session_project_idx` ON `session` (`project_id`);--> statement-breakpoint
8490
CREATE INDEX `session_parent_idx` ON `session` (`parent_id`);--> statement-breakpoint
85-
CREATE INDEX `todo_session_idx` ON `todo` (`session_id`);
91+
CREATE INDEX `todo_session_idx` ON `todo` (`session_id`);
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
1+
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"
2+
import { Database } from "@/storage/db"
23

34
export const ProjectTable = sqliteTable("project", {
45
id: text().primaryKey(),
@@ -7,8 +8,7 @@ export const ProjectTable = sqliteTable("project", {
78
name: text(),
89
icon_url: text(),
910
icon_color: text(),
10-
time_created: integer().notNull(),
11-
time_updated: integer().notNull(),
11+
...Database.Timestamps,
1212
time_initialized: integer(),
1313
sandboxes: text({ mode: "json" }).notNull().$type<string[]>(),
1414
})

packages/opencode/src/session/index.ts

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,7 @@ export namespace Session {
5454
}
5555
: undefined
5656
const share = row.share_url ? { url: row.share_url } : undefined
57-
const revert =
58-
row.revert_message_id !== null
59-
? {
60-
messageID: row.revert_message_id,
61-
partID: row.revert_part_id ?? undefined,
62-
snapshot: row.revert_snapshot ?? undefined,
63-
diff: row.revert_diff ?? undefined,
64-
}
65-
: undefined
57+
const revert = row.revert ?? undefined
6658
return {
6759
id: row.id,
6860
slug: row.slug,
@@ -98,10 +90,7 @@ export namespace Session {
9890
summary_deletions: info.summary?.deletions,
9991
summary_files: info.summary?.files,
10092
summary_diffs: info.summary?.diffs,
101-
revert_message_id: info.revert?.messageID ?? null,
102-
revert_part_id: info.revert?.partID ?? null,
103-
revert_snapshot: info.revert?.snapshot ?? null,
104-
revert_diff: info.revert?.diff ?? null,
93+
revert: info.revert ?? null,
10594
permission: info.permission,
10695
time_created: info.time.created,
10796
time_updated: info.time.updated,
@@ -415,10 +404,7 @@ export namespace Session {
415404
const row = db
416405
.update(SessionTable)
417406
.set({
418-
revert_message_id: input.revert?.messageID ?? null,
419-
revert_part_id: input.revert?.partID ?? null,
420-
revert_snapshot: input.revert?.snapshot ?? null,
421-
revert_diff: input.revert?.diff ?? null,
407+
revert: input.revert ?? null,
422408
summary_additions: input.summary?.additions,
423409
summary_deletions: input.summary?.deletions,
424410
summary_files: input.summary?.files,
@@ -440,10 +426,7 @@ export namespace Session {
440426
const row = db
441427
.update(SessionTable)
442428
.set({
443-
revert_message_id: null,
444-
revert_part_id: null,
445-
revert_snapshot: null,
446-
revert_diff: null,
429+
revert: null,
447430
time_updated: Date.now(),
448431
})
449432
.where(eq(SessionTable.id, sessionID))
@@ -544,16 +527,17 @@ export namespace Session {
544527
})
545528

546529
export const updateMessage = fn(MessageV2.Info, async (msg) => {
547-
const created_at = msg.role === "user" ? msg.time.created : msg.time.created
530+
const time_created = msg.role === "user" ? msg.time.created : msg.time.created
531+
const { id, sessionID, ...data } = msg
548532
Database.use((db) => {
549533
db.insert(MessageTable)
550534
.values({
551-
id: msg.id,
552-
session_id: msg.sessionID,
553-
created_at,
554-
data: msg,
535+
id,
536+
session_id: sessionID,
537+
time_created,
538+
data,
555539
})
556-
.onConflictDoUpdate({ target: MessageTable.id, set: { data: msg } })
540+
.onConflictDoUpdate({ target: MessageTable.id, set: { data } })
557541
.run()
558542
Database.effect(() =>
559543
Bus.publish(MessageV2.Event.Updated, {
@@ -620,15 +604,18 @@ export namespace Session {
620604
export const updatePart = fn(UpdatePartInput, async (input) => {
621605
const part = "delta" in input ? input.part : input
622606
const delta = "delta" in input ? input.delta : undefined
607+
const { id, messageID, sessionID, ...data } = part
608+
const time = Date.now()
623609
Database.use((db) => {
624610
db.insert(PartTable)
625611
.values({
626-
id: part.id,
627-
message_id: part.messageID,
628-
session_id: part.sessionID,
629-
data: part,
612+
id,
613+
message_id: messageID,
614+
session_id: sessionID,
615+
time_created: time,
616+
data,
630617
})
631-
.onConflictDoUpdate({ target: PartTable.id, set: { data: part } })
618+
.onConflictDoUpdate({ target: PartTable.id, set: { data } })
632619
.run()
633620
Database.effect(() =>
634621
Bus.publish(MessageV2.Event.PartUpdated, {

packages/opencode/src/session/message-v2.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ export namespace MessageV2 {
616616
.select()
617617
.from(MessageTable)
618618
.where(eq(MessageTable.session_id, sessionID))
619-
.orderBy(desc(MessageTable.created_at))
619+
.orderBy(desc(MessageTable.time_created))
620620
.limit(size)
621621
.offset(offset)
622622
.all(),
@@ -635,15 +635,22 @@ export namespace MessageV2 {
635635
.all(),
636636
)
637637
for (const row of partRows) {
638+
const part = {
639+
...row.data,
640+
id: row.id,
641+
sessionID: row.session_id,
642+
messageID: row.message_id,
643+
} as MessageV2.Part
638644
const list = partsByMessage.get(row.message_id)
639-
if (list) list.push(row.data)
640-
else partsByMessage.set(row.message_id, [row.data])
645+
if (list) list.push(part)
646+
else partsByMessage.set(row.message_id, [part])
641647
}
642648
}
643649

644650
for (const row of rows) {
651+
const info = { ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info
645652
yield {
646-
info: row.data,
653+
info,
647654
parts: partsByMessage.get(row.id) ?? [],
648655
}
649656
}
@@ -657,7 +664,9 @@ export namespace MessageV2 {
657664
const rows = Database.use((db) =>
658665
db.select().from(PartTable).where(eq(PartTable.message_id, message_id)).orderBy(PartTable.id).all(),
659666
)
660-
return rows.map((row) => row.data)
667+
return rows.map(
668+
(row) => ({ ...row.data, id: row.id, sessionID: row.session_id, messageID: row.message_id }) as MessageV2.Part,
669+
)
661670
})
662671

663672
export const get = fn(
@@ -668,8 +677,9 @@ export namespace MessageV2 {
668677
async (input) => {
669678
const row = Database.use((db) => db.select().from(MessageTable).where(eq(MessageTable.id, input.messageID)).get())
670679
if (!row) throw new Error(`Message not found: ${input.messageID}`)
680+
const info = { ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info
671681
return {
672-
info: row.data,
682+
info,
673683
parts: await parts(input.messageID),
674684
}
675685
},

packages/opencode/src/session/session.sql.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { ProjectTable } from "../project/project.sql"
33
import type { MessageV2 } from "./message-v2"
44
import type { Snapshot } from "@/snapshot"
55
import type { PermissionNext } from "@/permission/next"
6+
import { Database } from "@/storage/db"
7+
8+
type PartData = Omit<MessageV2.Part, "id" | "sessionID" | "messageID">
9+
type InfoData = Omit<MessageV2.Info, "id" | "sessionID">
610

711
export const SessionTable = sqliteTable(
812
"session",
@@ -21,13 +25,9 @@ export const SessionTable = sqliteTable(
2125
summary_deletions: integer(),
2226
summary_files: integer(),
2327
summary_diffs: text({ mode: "json" }).$type<Snapshot.FileDiff[]>(),
24-
revert_message_id: text(),
25-
revert_part_id: text(),
26-
revert_snapshot: text(),
27-
revert_diff: text(),
28+
revert: text({ mode: "json" }).$type<{ messageID: string; partID?: string; snapshot?: string; diff?: string }>(),
2829
permission: text({ mode: "json" }).$type<PermissionNext.Ruleset>(),
29-
time_created: integer().notNull(),
30-
time_updated: integer().notNull(),
30+
...Database.Timestamps,
3131
time_compacting: integer(),
3232
time_archived: integer(),
3333
},
@@ -41,8 +41,8 @@ export const MessageTable = sqliteTable(
4141
session_id: text()
4242
.notNull()
4343
.references(() => SessionTable.id, { onDelete: "cascade" }),
44-
created_at: integer().notNull(),
45-
data: text({ mode: "json" }).notNull().$type<MessageV2.Info>(),
44+
...Database.Timestamps,
45+
data: text({ mode: "json" }).notNull().$type<InfoData>(),
4646
},
4747
(table) => [index("message_session_idx").on(table.session_id)],
4848
)
@@ -55,7 +55,8 @@ export const PartTable = sqliteTable(
5555
.notNull()
5656
.references(() => MessageTable.id, { onDelete: "cascade" }),
5757
session_id: text().notNull(),
58-
data: text({ mode: "json" }).notNull().$type<MessageV2.Part>(),
58+
...Database.Timestamps,
59+
data: text({ mode: "json" }).notNull().$type<PartData>(),
5960
},
6061
(table) => [index("part_message_idx").on(table.message_id), index("part_session_idx").on(table.session_id)],
6162
)
@@ -71,6 +72,7 @@ export const TodoTable = sqliteTable(
7172
status: text().notNull(),
7273
priority: text().notNull(),
7374
position: integer().notNull(),
75+
...Database.Timestamps,
7476
},
7577
(table) => [primaryKey({ columns: [table.session_id, table.id] }), index("todo_session_idx").on(table.session_id)],
7678
)
@@ -79,5 +81,6 @@ export const PermissionTable = sqliteTable("permission", {
7981
project_id: text()
8082
.primaryKey()
8183
.references(() => ProjectTable.id, { onDelete: "cascade" }),
84+
...Database.Timestamps,
8285
data: text({ mode: "json" }).notNull().$type<PermissionNext.Ruleset>(),
8386
})

packages/opencode/src/share/share.sql.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { sqliteTable, text } from "drizzle-orm/sqlite-core"
22
import { SessionTable } from "../session/session.sql"
3+
import { Database } from "@/storage/db"
34

45
export const SessionShareTable = sqliteTable("session_share", {
56
session_id: text()
@@ -8,4 +9,5 @@ export const SessionShareTable = sqliteTable("session_share", {
89
id: text().notNull(),
910
secret: text().notNull(),
1011
url: text().notNull(),
12+
...Database.Timestamps,
1113
})

packages/opencode/src/storage/db.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Database as BunDatabase } from "bun:sqlite"
22
import { drizzle, type SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
33
import { migrate } from "drizzle-orm/bun-sqlite/migrator"
4-
import type { SQLiteTransaction } from "drizzle-orm/sqlite-core"
4+
import { integer, type SQLiteTransaction } from "drizzle-orm/sqlite-core"
55
export * from "drizzle-orm"
66
import { Context } from "../util/context"
77
import { lazy } from "../util/lazy"
@@ -137,4 +137,11 @@ export namespace Database {
137137
throw err
138138
}
139139
}
140+
141+
export const Timestamps = {
142+
time_created: integer().notNull(),
143+
time_updated: integer()
144+
.notNull()
145+
.$onUpdate(() => Date.now()),
146+
}
140147
}

packages/opencode/src/storage/json-migration.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,7 @@ export namespace JsonMigration {
118118
summary_deletions: data.summary?.deletions ?? null,
119119
summary_files: data.summary?.files ?? null,
120120
summary_diffs: data.summary?.diffs ?? null,
121-
revert_message_id: data.revert?.messageID ?? null,
122-
revert_part_id: data.revert?.partID ?? null,
123-
revert_snapshot: data.revert?.snapshot ?? null,
124-
revert_diff: data.revert?.diff ?? null,
121+
revert: data.revert ?? null,
125122
permission: data.permission ?? null,
126123
time_created: data.time?.created ?? Date.now(),
127124
time_updated: data.time?.updated ?? Date.now(),
@@ -159,11 +156,13 @@ export namespace JsonMigration {
159156
stats.errors.push(`message missing id: ${item.file}`)
160157
continue
161158
}
159+
const { id, sessionID: _, ...rest } = data
162160
values.push({
163161
id: data.id,
164162
session_id: sessionID,
165-
created_at: data.time?.created ?? Date.now(),
166-
data,
163+
time_created: data.time?.created ?? Date.now(),
164+
time_updated: data.time?.updated ?? Date.now(),
165+
data: rest,
167166
})
168167
messageIds.add(data.id)
169168
}
@@ -191,11 +190,14 @@ export namespace JsonMigration {
191190
stats.errors.push(`part missing id or messageID: ${item.file}`)
192191
continue
193192
}
193+
const { id, messageID, sessionID: _, ...rest } = data
194194
values.push({
195195
id: data.id,
196196
message_id: data.messageID,
197197
session_id: sessionID,
198-
data,
198+
time_created: data.time?.created ?? Date.now(),
199+
time_updated: data.time?.updated ?? Date.now(),
200+
data: rest,
199201
})
200202
}
201203
if (values.length === 0) continue

packages/opencode/test/storage/json-migration.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ describe("JSON to SQLite migration", () => {
196196
const db = drizzle({ client: sqlite })
197197
const messages = db.select().from(MessageTable).all()
198198
expect(messages.length).toBe(1)
199-
expect(messages[0].data.id).toBe("msg_test789ghi")
199+
expect(messages[0].id).toBe("msg_test789ghi")
200200

201201
const parts = db.select().from(PartTable).all()
202202
expect(parts.length).toBe(1)
203-
expect(parts[0].data.id).toBe("prt_testabc123")
203+
expect(parts[0].id).toBe("prt_testabc123")
204204
})
205205

206206
test("skips orphaned sessions (no parent project)", async () => {

0 commit comments

Comments
 (0)