From 38f735bfc681bde50a569247994e5dd3bfe6865d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 16 Jan 2026 15:13:00 -0500 Subject: [PATCH] sync --- bun.lock | 3 - ...ary.sql => 0000_vengeful_the_watchers.sql} | 22 ++- .../migration/meta/0000_snapshot.json | 146 +++++++++++++++--- .../opencode/migration/meta/_journal.json | 4 +- packages/opencode/script/check-migrations.ts | 0 .../opencode/script/generate-migrations.ts | 2 +- packages/opencode/script/postinstall.mjs | 0 .../opencode/script/publish-registries.ts | 0 packages/opencode/src/cli/cmd/import.ts | 34 ++-- packages/opencode/src/project/project.ts | 11 +- packages/opencode/src/session/index.ts | 64 +++----- packages/opencode/src/session/prompt.ts | 11 +- packages/opencode/src/session/revert.ts | 48 ++++-- packages/opencode/src/session/summary.ts | 22 ++- .../opencode/src/storage/json-migration.ts | 1 - .../src/storage/migrations.generated.ts | 6 +- packages/util/src/context.ts | 21 +++ 17 files changed, 277 insertions(+), 118 deletions(-) rename packages/opencode/migration/{0000_easy_albert_cleary.sql => 0000_vengeful_the_watchers.sql} (83%) mode change 100644 => 100755 packages/opencode/script/check-migrations.ts mode change 100644 => 100755 packages/opencode/script/generate-migrations.ts mode change 100644 => 100755 packages/opencode/script/postinstall.mjs mode change 100644 => 100755 packages/opencode/script/publish-registries.ts create mode 100644 packages/util/src/context.ts diff --git a/bun.lock b/bun.lock index b8defb2de1..3ffdb7eaa7 100644 --- a/bun.lock +++ b/bun.lock @@ -326,7 +326,6 @@ "solid-js": "catalog:", "strip-ansi": "7.1.2", "tree-sitter-bash": "0.25.0", - "trust": "0.1.0", "turndown": "7.2.0", "ulid": "catalog:", "vscode-jsonrpc": "8.2.1", @@ -3780,8 +3779,6 @@ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], - "trust": ["trust@0.1.0", "", {}, "sha512-BzU8tL0AD8ftb9008U9Wv6ww+ha5Z9fwQMmU0ICTTMKlazW3vGZkNCwX5L6t3Mf08vSA+aDCGsGjDT9nxl0vig=="], - "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], diff --git a/packages/opencode/migration/0000_easy_albert_cleary.sql b/packages/opencode/migration/0000_vengeful_the_watchers.sql similarity index 83% rename from packages/opencode/migration/0000_easy_albert_cleary.sql rename to packages/opencode/migration/0000_vengeful_the_watchers.sql index fc78cb242f..02f74fad87 100644 --- a/packages/opencode/migration/0000_easy_albert_cleary.sql +++ b/packages/opencode/migration/0000_vengeful_the_watchers.sql @@ -14,7 +14,6 @@ CREATE TABLE `project` ( CREATE TABLE `message` ( `id` text PRIMARY KEY NOT NULL, `session_id` text NOT NULL, - `created_at` integer NOT NULL, `data` text NOT NULL, FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON UPDATE no action ON DELETE cascade ); @@ -46,9 +45,24 @@ CREATE TABLE `session` ( `id` text PRIMARY KEY NOT NULL, `project_id` text NOT NULL, `parent_id` text, - `created_at` integer NOT NULL, - `updated_at` integer NOT NULL, - `data` text NOT NULL, + `slug` text NOT NULL, + `directory` text NOT NULL, + `title` text NOT NULL, + `version` text NOT NULL, + `share_url` text, + `summary_additions` integer, + `summary_deletions` integer, + `summary_files` integer, + `summary_diffs` text, + `revert_message_id` text, + `revert_part_id` text, + `revert_snapshot` text, + `revert_diff` text, + `permission` text, + `time_created` integer NOT NULL, + `time_updated` integer NOT NULL, + `time_compacting` integer, + `time_archived` integer, FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON UPDATE no action ON DELETE cascade ); --> statement-breakpoint diff --git a/packages/opencode/migration/meta/0000_snapshot.json b/packages/opencode/migration/meta/0000_snapshot.json index cd3d8392e3..5138104994 100644 --- a/packages/opencode/migration/meta/0000_snapshot.json +++ b/packages/opencode/migration/meta/0000_snapshot.json @@ -1,7 +1,7 @@ { "version": "6", "dialect": "sqlite", - "id": "f79c82ae-4de1-4a4c-a5f3-857bc3ee97f2", + "id": "86d0107e-84d7-4b08-8411-afab7d6b1ee2", "prevId": "00000000-0000-0000-0000-000000000000", "tables": { "project": { @@ -101,13 +101,6 @@ "notNull": true, "autoincrement": false }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, "data": { "name": "data", "type": "text", @@ -311,26 +304,131 @@ "notNull": false, "autoincrement": false }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "updated_at": { - "name": "updated_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "data": { - "name": "data", + "slug": { + "name": "slug", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false + }, + "directory": { + "name": "directory", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "share_url": { + "name": "share_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "summary_additions": { + "name": "summary_additions", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "summary_deletions": { + "name": "summary_deletions", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "summary_files": { + "name": "summary_files", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "summary_diffs": { + "name": "summary_diffs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revert_message_id": { + "name": "revert_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revert_part_id": { + "name": "revert_part_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revert_snapshot": { + "name": "revert_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revert_diff": { + "name": "revert_diff", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_updated": { + "name": "time_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_compacting": { + "name": "time_compacting", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_archived": { + "name": "time_archived", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false } }, "indexes": { diff --git a/packages/opencode/migration/meta/_journal.json b/packages/opencode/migration/meta/_journal.json index 599eb0671c..2fcbe6739a 100644 --- a/packages/opencode/migration/meta/_journal.json +++ b/packages/opencode/migration/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "6", - "when": 1768501411495, - "tag": "0000_easy_albert_cleary", + "when": 1768518709430, + "tag": "0000_vengeful_the_watchers", "breakpoints": true } ] diff --git a/packages/opencode/script/check-migrations.ts b/packages/opencode/script/check-migrations.ts old mode 100644 new mode 100755 diff --git a/packages/opencode/script/generate-migrations.ts b/packages/opencode/script/generate-migrations.ts old mode 100644 new mode 100755 index 74c472f06c..47c2e0c5e1 --- a/packages/opencode/script/generate-migrations.ts +++ b/packages/opencode/script/generate-migrations.ts @@ -31,7 +31,7 @@ export const migrations: { name: string; sql: string }[] = [] process.exit(0) } -const imports = files.map((f, i) => `import m${i} from "../../drizzle/${f}" with { type: "text" }`).join("\n") +const imports = files.map((f, i) => `import m${i} from "../../migration/${f}" with { type: "text" }`).join("\n") const entries = files.map((f, i) => ` { name: "${path.basename(f, ".sql")}", sql: m${i} },`).join("\n") diff --git a/packages/opencode/script/postinstall.mjs b/packages/opencode/script/postinstall.mjs old mode 100644 new mode 100755 diff --git a/packages/opencode/script/publish-registries.ts b/packages/opencode/script/publish-registries.ts old mode 100644 new mode 100755 diff --git a/packages/opencode/src/cli/cmd/import.ts b/packages/opencode/src/cli/cmd/import.ts index bcff74b77a..600749602e 100644 --- a/packages/opencode/src/cli/cmd/import.ts +++ b/packages/opencode/src/cli/cmd/import.ts @@ -82,14 +82,31 @@ export const ImportCommand = cmd({ return } - db() - .insert(SessionTable) - .values(Session.toRow({ ...exportData.info, projectID: Instance.project.id })) - .onConflictDoUpdate({ - target: SessionTable.id, - set: Session.toRow({ ...exportData.info, projectID: Instance.project.id }), - }) - .run() + const info = exportData.info + const row = { + id: info.id, + projectID: Instance.project.id, + parentID: info.parentID, + slug: info.slug, + directory: info.directory, + title: info.title, + version: info.version, + share_url: info.share?.url, + summary_additions: info.summary?.additions, + summary_deletions: info.summary?.deletions, + summary_files: info.summary?.files, + summary_diffs: info.summary?.diffs, + revert_messageID: info.revert?.messageID, + revert_partID: info.revert?.partID, + revert_snapshot: info.revert?.snapshot, + revert_diff: info.revert?.diff, + permission: info.permission, + time_created: info.time.created, + time_updated: info.time.updated, + time_compacting: info.time.compacting, + time_archived: info.time.archived, + } + db().insert(SessionTable).values(row).onConflictDoUpdate({ target: SessionTable.id, set: row }).run() for (const msg of exportData.messages) { db() @@ -97,7 +114,6 @@ export const ImportCommand = cmd({ .values({ id: msg.info.id, sessionID: exportData.info.id, - createdAt: msg.info.time?.created ?? Date.now(), data: msg.info, }) .onConflictDoUpdate({ target: MessageTable.id, set: { data: msg.info } }) diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index 699f12353a..c0c9f81d86 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -9,7 +9,7 @@ import { SessionTable } from "../session/session.sql" import { eq } from "drizzle-orm" import { Log } from "../util/log" import { Flag } from "@/flag/flag" -import { Session } from "../session" + import { work } from "../util/queue" import { fn } from "@opencode-ai/util/fn" import { BusEvent } from "@/bus/bus-event" @@ -304,13 +304,10 @@ export namespace Project { log.info("migrating sessions from global", { newProjectID, worktree, count: globalSessions.length }) await work(10, globalSessions, async (row) => { - const session = Session.fromRow(row) - if (!session) return - if (session.directory && session.directory !== worktree) return + if (row.directory && row.directory !== worktree) return - session.projectID = newProjectID - log.info("migrating session", { sessionID: session.id, from: "global", to: newProjectID }) - db().update(SessionTable).set(Session.toRow(session)).where(eq(SessionTable.id, session.id)).run() + log.info("migrating session", { sessionID: row.id, from: "global", to: newProjectID }) + db().update(SessionTable).set({ projectID: newProjectID }).where(eq(SessionTable.id, row.id)).run() }).catch((error) => { log.error("failed to migrate sessions from global to project", { error, projectId: newProjectID }) }) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 495491b8c8..e8c6349de4 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -85,32 +85,6 @@ export namespace Session { } } - export function toRow(info: Info) { - return { - id: info.id, - projectID: info.projectID, - parentID: info.parentID, - slug: info.slug, - directory: info.directory, - title: info.title, - version: info.version, - share_url: info.share?.url, - summary_additions: info.summary?.additions, - summary_deletions: info.summary?.deletions, - summary_files: info.summary?.files, - summary_diffs: info.summary?.diffs, - revert_messageID: info.revert?.messageID, - revert_partID: info.revert?.partID, - revert_snapshot: info.revert?.snapshot, - revert_diff: info.revert?.diff, - permission: info.permission, - time_created: info.time.created, - time_updated: info.time.updated, - time_compacting: info.time.compacting, - time_archived: info.time.archived, - } - } - export const Info = z .object({ id: Identifier.schema("session"), @@ -256,9 +230,10 @@ export namespace Session { ) export const touch = fn(Identifier.schema("session"), async (sessionID) => { - await update(sessionID, (draft) => { - draft.time.updated = Date.now() - }) + const now = Date.now() + db().update(SessionTable).set({ time_updated: now }).where(eq(SessionTable.id, sessionID)).run() + const session = await get(sessionID) + Bus.publish(Event.Updated, { info: session }) }) export async function createNext(input: { @@ -283,21 +258,29 @@ export namespace Session { }, } log.info("created", result) - db().insert(SessionTable).values(toRow(result)).run() + db() + .insert(SessionTable) + .values({ + id: result.id, + projectID: result.projectID, + parentID: result.parentID, + slug: result.slug, + directory: result.directory, + title: result.title, + version: result.version, + permission: result.permission, + time_created: result.time.created, + time_updated: result.time.updated, + }) + .run() Bus.publish(Event.Created, { info: result, }) const cfg = await Config.get() if (!result.parentID && (Flag.OPENCODE_AUTO_SHARE || cfg.share === "auto")) - share(result.id) - .then((share) => { - update(result.id, (draft) => { - draft.share = share - }) - }) - .catch(() => { - // Silently ignore sharing errors during session creation - }) + share(result.id).catch(() => { + // Silently ignore sharing errors during session creation + }) Bus.publish(Event.Updated, { info: result, }) @@ -404,7 +387,6 @@ export namespace Session { }) export const remove = fn(Identifier.schema("session"), async (sessionID) => { - const project = Instance.project try { const session = await get(sessionID) for (const child of await children(sessionID)) { @@ -422,13 +404,11 @@ export namespace Session { }) export const updateMessage = fn(MessageV2.Info, async (msg) => { - const createdAt = msg.role === "user" ? msg.time.created : msg.time.created db() .insert(MessageTable) .values({ id: msg.id, sessionID: msg.sessionID, - createdAt, data: msg, }) .onConflictDoUpdate({ target: MessageTable.id, set: { data: msg } }) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index de62788200..a8085b5831 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -166,10 +166,13 @@ export namespace SessionPrompt { }) } if (permissions.length > 0) { - session.permission = permissions - await Session.update(session.id, (draft) => { - draft.permission = permissions - }) + Session.update( + session.id, + (draft) => { + draft.permission = permissions + }, + { touch: false }, + ) } if (input.noReply === true) { diff --git a/packages/opencode/src/session/revert.ts b/packages/opencode/src/session/revert.ts index 9543e598a1..5239243850 100644 --- a/packages/opencode/src/session/revert.ts +++ b/packages/opencode/src/session/revert.ts @@ -6,7 +6,7 @@ import { Session } from "." import { Log } from "../util/log" import { splitWhen } from "remeda" import { db } from "../storage/db" -import { MessageTable, PartTable } from "./session.sql" +import { MessageTable, PartTable, SessionTable, SessionDiffTable } from "./session.sql" import { eq } from "drizzle-orm" import { Bus } from "../bus" import { SessionPrompt } from "./prompt" @@ -56,13 +56,17 @@ export namespace SessionRevert { } if (revert) { - const session = await Session.get(input.sessionID) - revert.snapshot = session.revert?.snapshot ?? (await Snapshot.track()) + const current = await Session.get(input.sessionID) + revert.snapshot = current.revert?.snapshot ?? (await Snapshot.track()) await Snapshot.revert(patches) if (revert.snapshot) revert.diff = await Snapshot.diff(revert.snapshot) const rangeMessages = all.filter((msg) => msg.info.id >= revert!.messageID) const diffs = await SessionSummary.computeDiff({ messages: rangeMessages }) - await Storage.write(["session_diff", input.sessionID], diffs) + db() + .insert(SessionDiffTable) + .values({ sessionID: input.sessionID, data: diffs }) + .onConflictDoUpdate({ target: SessionDiffTable.sessionID, set: { data: diffs } }) + .run() Bus.publish(Session.Event.Diff, { sessionID: input.sessionID, diff: diffs, @@ -85,10 +89,21 @@ export namespace SessionRevert { const session = await Session.get(input.sessionID) if (!session.revert) return session if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot) - const next = await Session.update(input.sessionID, (draft) => { - draft.revert = undefined - }) - return next + const now = Date.now() + db() + .update(SessionTable) + .set({ + revert_messageID: null, + revert_partID: null, + revert_snapshot: null, + revert_diff: null, + time_updated: now, + }) + .where(eq(SessionTable.id, input.sessionID)) + .run() + const updated = await Session.get(input.sessionID) + Bus.publish(Session.Event.Updated, { info: updated }) + return updated } export async function cleanup(session: Session.Info) { @@ -116,8 +131,19 @@ export namespace SessionRevert { }) } } - await Session.update(sessionID, (draft) => { - draft.revert = undefined - }) + const now = Date.now() + db() + .update(SessionTable) + .set({ + revert_messageID: null, + revert_partID: null, + revert_snapshot: null, + revert_diff: null, + time_updated: now, + }) + .where(eq(SessionTable.id, sessionID)) + .run() + const updated = await Session.get(sessionID) + Bus.publish(Session.Event.Updated, { info: updated }) } } diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index a79850046d..f391e65ec4 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -12,7 +12,7 @@ import { Log } from "@/util/log" import path from "path" import { Instance } from "@/project/instance" import { db } from "@/storage/db" -import { SessionDiffTable } from "./session.sql" +import { SessionDiffTable, SessionTable } from "./session.sql" import { eq } from "drizzle-orm" import { Bus } from "@/bus" @@ -49,13 +49,19 @@ export namespace SessionSummary { return files.has(x.file) }), ) - await Session.update(input.sessionID, (draft) => { - draft.summary = { - additions: diffs.reduce((sum, x) => sum + x.additions, 0), - deletions: diffs.reduce((sum, x) => sum + x.deletions, 0), - files: diffs.length, - } - }) + const now = Date.now() + db() + .update(SessionTable) + .set({ + summary_additions: diffs.reduce((sum, x) => sum + x.additions, 0), + summary_deletions: diffs.reduce((sum, x) => sum + x.deletions, 0), + summary_files: diffs.length, + time_updated: now, + }) + .where(eq(SessionTable.id, input.sessionID)) + .run() + const session = await Session.get(input.sessionID) + Bus.publish(Session.Event.Updated, { info: session }) db() .insert(SessionDiffTable) .values({ sessionID: input.sessionID, data: diffs }) diff --git a/packages/opencode/src/storage/json-migration.ts b/packages/opencode/src/storage/json-migration.ts index 96f3714c2a..6e2c72be95 100644 --- a/packages/opencode/src/storage/json-migration.ts +++ b/packages/opencode/src/storage/json-migration.ts @@ -145,7 +145,6 @@ export async function migrateFromJson(sqlite: Database, customStorageDir?: strin .values({ id: data.id, sessionID: data.sessionID, - createdAt: data.time?.created ?? Date.now(), data, }) .onConflictDoNothing() diff --git a/packages/opencode/src/storage/migrations.generated.ts b/packages/opencode/src/storage/migrations.generated.ts index 9110336e71..eef6fdac9e 100644 --- a/packages/opencode/src/storage/migrations.generated.ts +++ b/packages/opencode/src/storage/migrations.generated.ts @@ -1,4 +1,6 @@ // Auto-generated - do not edit -import m0 from "../../migration/0000_easy_albert_cleary.sql" with { type: "text" } +import m0 from "../../migration/0000_vengeful_the_watchers.sql" with { type: "text" } -export const migrations = [{ name: "0000_easy_albert_cleary", sql: m0 }] +export const migrations = [ + { name: "0000_vengeful_the_watchers", sql: m0 }, +] diff --git a/packages/util/src/context.ts b/packages/util/src/context.ts new file mode 100644 index 0000000000..59594cb4ac --- /dev/null +++ b/packages/util/src/context.ts @@ -0,0 +1,21 @@ +import { AsyncLocalStorage } from "node:async_hooks" + +export namespace Context { + export class NotFound extends Error {} + + export function create() { + const storage = new AsyncLocalStorage() + return { + use() { + const result = storage.getStore() + if (!result) { + throw new NotFound() + } + return result + }, + provide(value: T, fn: () => R) { + return storage.run(value, fn) + }, + } + } +}