import { sqliteTable, text, integer, index, primaryKey, real, uniqueIndex } from "drizzle-orm/sqlite-core" import { sql } from "drizzle-orm" import { directoryColumn, pathColumn } from "../database/path" import { ProjectTable } from "../project/sql" import type { SessionMessage } from "./message" import type { SessionPending } from "./pending" import type { FileDiff } from "@opencode-ai/schema/file-diff" import { PermissionV1 } from "../v1/permission" import { ProjectV2 } from "../project" import type { SessionSchema } from "./schema" import type { MessageID, PartID, SessionV1 } from "../v1/session" import { WorkspaceV2 } from "../workspace" import { Timestamps } from "../database/schema.sql" import type { Instruction } from "@opencode-ai/schema/instruction" import type { Session } from "@opencode-ai/schema/session" import type { SyntheticData, UserData } from "@opencode-ai/schema/session-pending" import type { RevertV1 } from "@opencode-ai/schema/session-revert" import type { Schema } from "effect" type SessionMessageData = Omit<(typeof SessionMessage.Info)["Encoded"], "type" | "id"> type V1MessageData = Omit type V1PartData = Omit export const SessionTable = sqliteTable( "session", { id: text().$type().primaryKey(), project_id: text() .$type() .notNull() .references(() => ProjectTable.id, { onDelete: "cascade" }), workspace_id: text().$type(), parent_id: text().$type(), fork_session_id: text().$type(), fork_message_id: text().$type(), fork_seq: integer(), slug: text().notNull(), directory: directoryColumn().notNull(), path: pathColumn(), title: text().notNull(), version: text().notNull(), share_url: text(), summary_additions: integer(), summary_deletions: integer(), summary_files: integer(), summary_diffs: text({ mode: "json" }).$type(), metadata: text({ mode: "json" }).$type>(), cost: real().notNull().default(0), tokens_input: integer().notNull().default(0), tokens_output: integer().notNull().default(0), tokens_reasoning: integer().notNull().default(0), tokens_cache_read: integer().notNull().default(0), tokens_cache_write: integer().notNull().default(0), revert: text({ mode: "json" }).$type(), permission: text({ mode: "json" }).$type(), agent: text(), model: text({ mode: "json" }).$type<{ id: string providerID: string variant?: string }>(), ...Timestamps, time_compacting: integer(), time_archived: integer(), time_suspended: integer(), }, (table) => [ index("session_project_idx").on(table.project_id), index("session_workspace_idx").on(table.workspace_id), index("session_parent_idx").on(table.parent_id), index("session_time_suspended_idx") .on(table.time_suspended) .where(sql`${table.time_suspended} is not null`), ], ) export const MessageTable = sqliteTable( "message", { id: text().$type().primaryKey(), session_id: text() .$type() .notNull() .references(() => SessionTable.id, { onDelete: "cascade" }), ...Timestamps, data: text({ mode: "json" }).notNull().$type(), }, (table) => [index("message_session_time_created_id_idx").on(table.session_id, table.time_created, table.id)], ) export const PartTable = sqliteTable( "part", { id: text().$type().primaryKey(), message_id: text() .$type() .notNull() .references(() => MessageTable.id, { onDelete: "cascade" }), session_id: text().$type().notNull(), ...Timestamps, data: text({ mode: "json" }).notNull().$type(), }, (table) => [ index("part_message_id_id_idx").on(table.message_id, table.id), index("part_session_idx").on(table.session_id), ], ) export const SessionMessageTable = sqliteTable( "session_message", { id: text().$type().primaryKey(), session_id: text() .$type() .notNull() .references(() => SessionTable.id, { onDelete: "cascade" }), type: text().$type().notNull(), seq: integer().notNull(), ...Timestamps, data: text({ mode: "json" }).notNull().$type(), }, (table) => [ uniqueIndex("session_message_session_seq_idx").on(table.session_id, table.seq), index("session_message_session_type_seq_idx").on(table.session_id, table.type, table.seq), index("session_message_session_time_created_id_idx").on(table.session_id, table.time_created, table.id), index("session_message_time_created_idx").on(table.time_created), ], ) export const SessionPendingTable = sqliteTable( "session_pending", { id: text().$type().primaryKey(), session_id: text() .$type() .notNull() .references(() => SessionTable.id, { onDelete: "cascade" }), type: text().$type().notNull(), data: text({ mode: "json" }).$type>().notNull(), delivery: text().$type(), admitted_seq: integer().notNull(), time_created: integer() .notNull() .$default(() => Date.now()), }, (table) => [ index("session_pending_session_delivery_seq_idx").on(table.session_id, table.delivery, table.admitted_seq), uniqueIndex("session_pending_session_compaction_idx") .on(table.session_id) .where(sql`${table.type} = 'compaction'`), uniqueIndex("session_pending_session_admitted_seq_idx").on(table.session_id, table.admitted_seq), ], ) export const InstructionEntryTable = sqliteTable( "instruction_entry", { session_id: text() .$type() .notNull() .references(() => SessionTable.id, { onDelete: "cascade" }), key: text().notNull(), value: text({ mode: "json" }).$type(), removed: integer({ mode: "boolean" }).notNull().default(false), ...Timestamps, }, (table) => [primaryKey({ columns: [table.session_id, table.key] })], ) export const InstructionBlobTable = sqliteTable("instruction_blob", { hash: text().$type().primaryKey(), value: text({ mode: "json" }).$type(), }) export const InstructionStateTable = sqliteTable("instruction_state", { session_id: text() .$type() .primaryKey() .references(() => SessionTable.id, { onDelete: "cascade" }), epoch_start: integer().notNull(), through_seq: integer().notNull(), initial_values: text({ mode: "json" }).notNull().$type(), current_values: text({ mode: "json" }).notNull().$type(), })