diff --git a/packages/opencode/src/session/session.sql.ts b/packages/opencode/src/session/session.sql.ts index 119ebeda37..35ed8fdda4 100644 --- a/packages/opencode/src/session/session.sql.ts +++ b/packages/opencode/src/session/session.sql.ts @@ -11,7 +11,6 @@ import { Timestamps } from "../storage/schema.sql" type PartData = Omit type InfoData = Omit -type EntryData = Omit export const SessionTable = sqliteTable( "session", @@ -104,7 +103,7 @@ export const SessionEntryTable = sqliteTable( .$type() .notNull() .references(() => SessionTable.id, { onDelete: "cascade" }), - type: text().notNull(), + type: text().$type().notNull(), ...Timestamps, data: text({ mode: "json" }).notNull().$type>(), }, diff --git a/packages/opencode/src/v2/session-entry.ts b/packages/opencode/src/v2/session-entry.ts index b931a4c494..03c8a85b07 100644 --- a/packages/opencode/src/v2/session-entry.ts +++ b/packages/opencode/src/v2/session-entry.ts @@ -1,6 +1,10 @@ import { Identifier } from "@/id/id" +import { Database } from "@/node" +import type { SessionID } from "@/session/schema" +import { SessionEntryTable } from "@/session/session.sql" import { withStatics } from "@/util/schema" -import { DateTime, Effect, Schema } from "effect" +import { Context, DateTime, Effect, Layer, Schema } from "effect" +import { eq } from "../storage/db" export namespace SessionEntry { export const ID = Schema.String.pipe(Schema.brand("Session.Entry.ID")).pipe( @@ -181,6 +185,43 @@ export namespace SessionEntry { overflow: Schema.Boolean.pipe(Schema.optional), }) {} - export const Entry = Schema.Union([User, Synthetic, Request, Tool, Text, Reasoning, Complete, Retry, Compaction]) + export const Entry = Schema.Union([User, Synthetic, Request, Tool, Text, Reasoning, Complete, Retry, Compaction], { + mode: "oneOf", + }) export type Entry = Schema.Schema.Type + + export type Type = Entry["type"] + + export interface Interface { + readonly decode: (row: typeof SessionEntryTable.$inferSelect) => Entry + readonly fromSession: (sessionID: SessionID) => Effect.Effect + } + + export class Service extends Context.Service()("@opencode/SessionEntry") {} + + export const layer: Layer.Layer = Layer.effect( + Service, + Effect.gen(function* () { + const decodeEntry = Schema.decodeUnknownSync(Entry) + + const decode: (typeof Service.Service)["decode"] = (row) => decodeEntry({ ...row, id: row.id, type: row.type }) + + const fromSession = Effect.fn("SessionEntry.fromSession")(function* (sessionID: SessionID) { + return Database.use((db) => + db + .select() + .from(SessionEntryTable) + .where(eq(SessionEntryTable.session_id, sessionID)) + .orderBy(SessionEntryTable.id) + .all() + .map((row) => decode(row)), + ) + }) + + return Service.of({ + decode, + fromSession, + }) + }), + ) }