mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-26 10:34:47 +00:00
feat(id): brand SessionID through Drizzle and Zod schemas (#16953)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import type { Argv } from "yargs"
|
import type { Argv } from "yargs"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
|
import { SessionID } from "../../session/schema"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { bootstrap } from "../bootstrap"
|
import { bootstrap } from "../bootstrap"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
@@ -17,7 +18,7 @@ export const ExportCommand = cmd({
|
|||||||
},
|
},
|
||||||
handler: async (args) => {
|
handler: async (args) => {
|
||||||
await bootstrap(process.cwd(), async () => {
|
await bootstrap(process.cwd(), async () => {
|
||||||
let sessionID = args.sessionID
|
let sessionID = args.sessionID ? SessionID.make(args.sessionID) : undefined
|
||||||
process.stderr.write(`Exporting session: ${sessionID ?? "latest"}\n`)
|
process.stderr.write(`Exporting session: ${sessionID ?? "latest"}\n`)
|
||||||
|
|
||||||
if (!sessionID) {
|
if (!sessionID) {
|
||||||
@@ -58,7 +59,7 @@ export const ExportCommand = cmd({
|
|||||||
throw new UI.CancelledError()
|
throw new UI.CancelledError()
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionID = selectedSession as string
|
sessionID = selectedSession
|
||||||
|
|
||||||
prompts.outro("Exporting session...", {
|
prompts.outro("Exporting session...", {
|
||||||
output: process.stderr,
|
output: process.stderr,
|
||||||
@@ -67,7 +68,7 @@ export const ExportCommand = cmd({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const sessionInfo = await Session.get(sessionID!)
|
const sessionInfo = await Session.get(sessionID!)
|
||||||
const messages = await Session.messages({ sessionID: sessionID! })
|
const messages = await Session.messages({ sessionID: sessionInfo.id })
|
||||||
|
|
||||||
const exportData = {
|
const exportData = {
|
||||||
info: sessionInfo,
|
info: sessionInfo,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { ModelsDev } from "../../provider/models"
|
|||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import { bootstrap } from "../bootstrap"
|
import { bootstrap } from "../bootstrap"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
|
import type { SessionID } from "../../session/schema"
|
||||||
import { Identifier } from "../../id/id"
|
import { Identifier } from "../../id/id"
|
||||||
import { Provider } from "../../provider/provider"
|
import { Provider } from "../../provider/provider"
|
||||||
import { Bus } from "../../bus"
|
import { Bus } from "../../bus"
|
||||||
@@ -481,7 +482,7 @@ export const GithubRunCommand = cmd({
|
|||||||
let octoRest: Octokit
|
let octoRest: Octokit
|
||||||
let octoGraph: typeof graphql
|
let octoGraph: typeof graphql
|
||||||
let gitConfig: string
|
let gitConfig: string
|
||||||
let session: { id: string; title: string; version: string }
|
let session: { id: SessionID; title: string; version: string }
|
||||||
let shareId: string | undefined
|
let shareId: string | undefined
|
||||||
let exitCode = 0
|
let exitCode = 0
|
||||||
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
|
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Argv } from "yargs"
|
import type { Argv } from "yargs"
|
||||||
import type { Session as SDKSession, Message, Part } from "@opencode-ai/sdk/v2"
|
import type { Session as SDKSession, Message, Part } from "@opencode-ai/sdk/v2"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
|
import { SessionID } from "../../session/schema"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { bootstrap } from "../bootstrap"
|
import { bootstrap } from "../bootstrap"
|
||||||
import { Database } from "../../storage/db"
|
import { Database } from "../../storage/db"
|
||||||
@@ -152,7 +153,12 @@ export const ImportCommand = cmd({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const row = Session.toRow({ ...exportData.info, projectID: Instance.project.id })
|
const row = Session.toRow({
|
||||||
|
...exportData.info,
|
||||||
|
id: SessionID.make(exportData.info.id),
|
||||||
|
parentID: exportData.info.parentID ? SessionID.make(exportData.info.parentID) : undefined,
|
||||||
|
projectID: Instance.project.id,
|
||||||
|
})
|
||||||
Database.use((db) =>
|
Database.use((db) =>
|
||||||
db
|
db
|
||||||
.insert(SessionTable)
|
.insert(SessionTable)
|
||||||
@@ -167,7 +173,7 @@ export const ImportCommand = cmd({
|
|||||||
.insert(MessageTable)
|
.insert(MessageTable)
|
||||||
.values({
|
.values({
|
||||||
id: msg.info.id,
|
id: msg.info.id,
|
||||||
session_id: exportData.info.id,
|
session_id: row.id,
|
||||||
time_created: msg.info.time?.created ?? Date.now(),
|
time_created: msg.info.time?.created ?? Date.now(),
|
||||||
data: msg.info,
|
data: msg.info,
|
||||||
})
|
})
|
||||||
@@ -182,7 +188,7 @@ export const ImportCommand = cmd({
|
|||||||
.values({
|
.values({
|
||||||
id: part.id,
|
id: part.id,
|
||||||
message_id: msg.info.id,
|
message_id: msg.info.id,
|
||||||
session_id: exportData.info.id,
|
session_id: row.id,
|
||||||
data: part,
|
data: part,
|
||||||
})
|
})
|
||||||
.onConflictDoNothing()
|
.onConflictDoNothing()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Argv } from "yargs"
|
import type { Argv } from "yargs"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
|
import { SessionID } from "../../session/schema"
|
||||||
import { bootstrap } from "../bootstrap"
|
import { bootstrap } from "../bootstrap"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { Locale } from "../../util/locale"
|
import { Locale } from "../../util/locale"
|
||||||
@@ -57,13 +58,14 @@ export const SessionDeleteCommand = cmd({
|
|||||||
},
|
},
|
||||||
handler: async (args) => {
|
handler: async (args) => {
|
||||||
await bootstrap(process.cwd(), async () => {
|
await bootstrap(process.cwd(), async () => {
|
||||||
|
const sessionID = SessionID.make(args.sessionID)
|
||||||
try {
|
try {
|
||||||
await Session.get(args.sessionID)
|
await Session.get(sessionID)
|
||||||
} catch {
|
} catch {
|
||||||
UI.error(`Session not found: ${args.sessionID}`)
|
UI.error(`Session not found: ${args.sessionID}`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
await Session.remove(args.sessionID)
|
await Session.remove(sessionID)
|
||||||
UI.println(UI.Style.TEXT_SUCCESS_BOLD + `Session ${args.sessionID} deleted` + UI.Style.TEXT_NORMAL)
|
UI.println(UI.Style.TEXT_SUCCESS_BOLD + `Session ${args.sessionID} deleted` + UI.Style.TEXT_NORMAL)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
|
|
||||||
export const TuiEvent = {
|
export const TuiEvent = {
|
||||||
@@ -42,7 +43,7 @@ export const TuiEvent = {
|
|||||||
SessionSelect: BusEvent.define(
|
SessionSelect: BusEvent.define(
|
||||||
"tui.session.select",
|
"tui.session.select",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().regex(/^ses/).describe("Session ID to navigate to"),
|
sessionID: SessionID.zod.describe("Session ID to navigate to"),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Config } from "../config/config"
|
import { Config } from "../config/config"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
@@ -14,7 +15,7 @@ export namespace Command {
|
|||||||
"command.executed",
|
"command.executed",
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
arguments: z.string(),
|
arguments: z.string(),
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { Identifier } from "../id/id"
|
import { Identifier } from "../id/id"
|
||||||
@@ -24,7 +25,7 @@ export namespace Permission {
|
|||||||
id: z.string(),
|
id: z.string(),
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
pattern: z.union([z.string(), z.array(z.string())]).optional(),
|
pattern: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string(),
|
messageID: z.string(),
|
||||||
callID: z.string().optional(),
|
callID: z.string().optional(),
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
@@ -43,7 +44,7 @@ export namespace Permission {
|
|||||||
Replied: BusEvent.define(
|
Replied: BusEvent.define(
|
||||||
"permission.replied",
|
"permission.replied",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
permissionID: z.string(),
|
permissionID: z.string(),
|
||||||
response: z.string(),
|
response: z.string(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Bus } from "@/bus"
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
import { Identifier } from "@/id/id"
|
import { Identifier } from "@/id/id"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import { Database, eq } from "@/storage/db"
|
import { Database, eq } from "@/storage/db"
|
||||||
import { PermissionTable } from "@/session/session.sql"
|
import { PermissionTable } from "@/session/session.sql"
|
||||||
@@ -69,7 +70,7 @@ export namespace PermissionNext {
|
|||||||
export const Request = z
|
export const Request = z
|
||||||
.object({
|
.object({
|
||||||
id: Identifier.schema("permission"),
|
id: Identifier.schema("permission"),
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
permission: z.string(),
|
permission: z.string(),
|
||||||
patterns: z.string().array(),
|
patterns: z.string().array(),
|
||||||
metadata: z.record(z.string(), z.any()),
|
metadata: z.record(z.string(), z.any()),
|
||||||
@@ -100,7 +101,7 @@ export namespace PermissionNext {
|
|||||||
Replied: BusEvent.define(
|
Replied: BusEvent.define(
|
||||||
"permission.replied",
|
"permission.replied",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
requestID: z.string(),
|
requestID: z.string(),
|
||||||
reply: Reply,
|
reply: Reply,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Identifier } from "@/id/id"
|
import { Identifier } from "@/id/id"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import { Log } from "@/util/log"
|
import { Log } from "@/util/log"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
@@ -34,7 +35,7 @@ export namespace Question {
|
|||||||
export const Request = z
|
export const Request = z
|
||||||
.object({
|
.object({
|
||||||
id: Identifier.schema("question"),
|
id: Identifier.schema("question"),
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
questions: z.array(Info).describe("Questions to ask"),
|
questions: z.array(Info).describe("Questions to ask"),
|
||||||
tool: z
|
tool: z
|
||||||
.object({
|
.object({
|
||||||
@@ -65,7 +66,7 @@ export namespace Question {
|
|||||||
Replied: BusEvent.define(
|
Replied: BusEvent.define(
|
||||||
"question.replied",
|
"question.replied",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
requestID: z.string(),
|
requestID: z.string(),
|
||||||
answers: z.array(Answer),
|
answers: z.array(Answer),
|
||||||
}),
|
}),
|
||||||
@@ -73,7 +74,7 @@ export namespace Question {
|
|||||||
Rejected: BusEvent.define(
|
Rejected: BusEvent.define(
|
||||||
"question.rejected",
|
"question.rejected",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
requestID: z.string(),
|
requestID: z.string(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -95,7 +96,7 @@ export namespace Question {
|
|||||||
})
|
})
|
||||||
|
|
||||||
export async function ask(input: {
|
export async function ask(input: {
|
||||||
sessionID: string
|
sessionID: SessionID
|
||||||
questions: Info[]
|
questions: Info[]
|
||||||
tool?: { messageID: string; callID: string }
|
tool?: { messageID: string; callID: string }
|
||||||
}): Promise<Answer[]> {
|
}): Promise<Answer[]> {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import { stream } from "hono/streaming"
|
import { stream } from "hono/streaming"
|
||||||
import { describeRoute, validator, resolver } from "hono-openapi"
|
import { describeRoute, validator, resolver } from "hono-openapi"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
import { MessageV2 } from "../../session/message-v2"
|
import { MessageV2 } from "../../session/message-v2"
|
||||||
@@ -173,7 +174,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
@@ -258,7 +259,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator(
|
validator(
|
||||||
@@ -309,7 +310,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator("json", Session.initialize.schema.omit({ sessionID: true })),
|
validator("json", Session.initialize.schema.omit({ sessionID: true })),
|
||||||
@@ -372,7 +373,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
@@ -401,7 +402,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
@@ -502,7 +503,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator(
|
validator(
|
||||||
@@ -561,7 +562,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator(
|
validator(
|
||||||
@@ -605,7 +606,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string().meta({ description: "Message ID" }),
|
messageID: z.string().meta({ description: "Message ID" }),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -640,7 +641,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string().meta({ description: "Message ID" }),
|
messageID: z.string().meta({ description: "Message ID" }),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -674,7 +675,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string().meta({ description: "Message ID" }),
|
messageID: z.string().meta({ description: "Message ID" }),
|
||||||
partID: z.string().meta({ description: "Part ID" }),
|
partID: z.string().meta({ description: "Part ID" }),
|
||||||
}),
|
}),
|
||||||
@@ -709,7 +710,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string().meta({ description: "Message ID" }),
|
messageID: z.string().meta({ description: "Message ID" }),
|
||||||
partID: z.string().meta({ description: "Part ID" }),
|
partID: z.string().meta({ description: "Part ID" }),
|
||||||
}),
|
}),
|
||||||
@@ -753,7 +754,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
|
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
|
||||||
@@ -785,7 +786,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
|
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
|
||||||
@@ -825,7 +826,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator("json", SessionPrompt.CommandInput.omit({ sessionID: true })),
|
validator("json", SessionPrompt.CommandInput.omit({ sessionID: true })),
|
||||||
@@ -857,7 +858,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().meta({ description: "Session ID" }),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
|
validator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
|
||||||
@@ -889,7 +890,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator("json", SessionRevert.RevertInput.omit({ sessionID: true })),
|
validator("json", SessionRevert.RevertInput.omit({ sessionID: true })),
|
||||||
@@ -924,7 +925,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
@@ -955,7 +956,7 @@ export const SessionRoutes = lazy(() =>
|
|||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
permissionID: z.string(),
|
permissionID: z.string(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { BusEvent } from "@/bus/bus-event"
|
|||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { Session } from "."
|
import { Session } from "."
|
||||||
import { Identifier } from "../id/id"
|
import { Identifier } from "../id/id"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { Provider } from "../provider/provider"
|
import { Provider } from "../provider/provider"
|
||||||
import { MessageV2 } from "./message-v2"
|
import { MessageV2 } from "./message-v2"
|
||||||
@@ -22,7 +23,7 @@ export namespace SessionCompaction {
|
|||||||
Compacted: BusEvent.define(
|
Compacted: BusEvent.define(
|
||||||
"session.compacted",
|
"session.compacted",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -55,7 +56,7 @@ export namespace SessionCompaction {
|
|||||||
// goes backwards through parts until there are 40_000 tokens worth of tool
|
// goes backwards through parts until there are 40_000 tokens worth of tool
|
||||||
// calls. then erases output of previous tool calls. idea is to throw away old
|
// calls. then erases output of previous tool calls. idea is to throw away old
|
||||||
// tool calls that are no longer relevant.
|
// tool calls that are no longer relevant.
|
||||||
export async function prune(input: { sessionID: string }) {
|
export async function prune(input: { sessionID: SessionID }) {
|
||||||
const config = await Config.get()
|
const config = await Config.get()
|
||||||
if (config.compaction?.prune === false) return
|
if (config.compaction?.prune === false) return
|
||||||
log.info("pruning")
|
log.info("pruning")
|
||||||
@@ -101,7 +102,7 @@ export namespace SessionCompaction {
|
|||||||
export async function process(input: {
|
export async function process(input: {
|
||||||
parentID: string
|
parentID: string
|
||||||
messages: MessageV2.WithParts[]
|
messages: MessageV2.WithParts[]
|
||||||
sessionID: string
|
sessionID: SessionID
|
||||||
abort: AbortSignal
|
abort: AbortSignal
|
||||||
auto: boolean
|
auto: boolean
|
||||||
overflow?: boolean
|
overflow?: boolean
|
||||||
@@ -295,7 +296,7 @@ When constructing the summary, try to stick to this template:
|
|||||||
|
|
||||||
export const create = fn(
|
export const create = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
agent: z.string(),
|
agent: z.string(),
|
||||||
model: z.object({
|
model: z.object({
|
||||||
providerID: z.string(),
|
providerID: z.string(),
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { Command } from "../command"
|
|||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
import { WorkspaceContext } from "../control-plane/workspace-context"
|
import { WorkspaceContext } from "../control-plane/workspace-context"
|
||||||
import { ProjectID } from "../project/schema"
|
import { ProjectID } from "../project/schema"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
|
|
||||||
import type { Provider } from "@/provider/provider"
|
import type { Provider } from "@/provider/provider"
|
||||||
import { PermissionNext } from "@/permission/next"
|
import { PermissionNext } from "@/permission/next"
|
||||||
@@ -119,12 +120,12 @@ export namespace Session {
|
|||||||
|
|
||||||
export const Info = z
|
export const Info = z
|
||||||
.object({
|
.object({
|
||||||
id: Identifier.schema("session"),
|
id: SessionID.zod,
|
||||||
slug: z.string(),
|
slug: z.string(),
|
||||||
projectID: ProjectID.zod,
|
projectID: ProjectID.zod,
|
||||||
workspaceID: z.string().optional(),
|
workspaceID: z.string().optional(),
|
||||||
directory: z.string(),
|
directory: z.string(),
|
||||||
parentID: Identifier.schema("session").optional(),
|
parentID: SessionID.zod.optional(),
|
||||||
summary: z
|
summary: z
|
||||||
.object({
|
.object({
|
||||||
additions: z.number(),
|
additions: z.number(),
|
||||||
@@ -201,14 +202,14 @@ export namespace Session {
|
|||||||
Diff: BusEvent.define(
|
Diff: BusEvent.define(
|
||||||
"session.diff",
|
"session.diff",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
diff: Snapshot.FileDiff.array(),
|
diff: Snapshot.FileDiff.array(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
Error: BusEvent.define(
|
Error: BusEvent.define(
|
||||||
"session.error",
|
"session.error",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().optional(),
|
sessionID: SessionID.zod.optional(),
|
||||||
error: MessageV2.Assistant.shape.error,
|
error: MessageV2.Assistant.shape.error,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -217,7 +218,7 @@ export namespace Session {
|
|||||||
export const create = fn(
|
export const create = fn(
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
parentID: Identifier.schema("session").optional(),
|
parentID: SessionID.zod.optional(),
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
permission: Info.shape.permission,
|
permission: Info.shape.permission,
|
||||||
workspaceID: Identifier.schema("workspace").optional(),
|
workspaceID: Identifier.schema("workspace").optional(),
|
||||||
@@ -236,7 +237,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const fork = fn(
|
export const fork = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
messageID: Identifier.schema("message").optional(),
|
messageID: Identifier.schema("message").optional(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -277,7 +278,7 @@ export namespace Session {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const touch = fn(Identifier.schema("session"), async (sessionID) => {
|
export const touch = fn(SessionID.zod, async (sessionID) => {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
Database.use((db) => {
|
Database.use((db) => {
|
||||||
const row = db
|
const row = db
|
||||||
@@ -293,15 +294,15 @@ export namespace Session {
|
|||||||
})
|
})
|
||||||
|
|
||||||
export async function createNext(input: {
|
export async function createNext(input: {
|
||||||
id?: string
|
id?: SessionID
|
||||||
title?: string
|
title?: string
|
||||||
parentID?: string
|
parentID?: SessionID
|
||||||
workspaceID?: string
|
workspaceID?: string
|
||||||
directory: string
|
directory: string
|
||||||
permission?: PermissionNext.Ruleset
|
permission?: PermissionNext.Ruleset
|
||||||
}) {
|
}) {
|
||||||
const result: Info = {
|
const result: Info = {
|
||||||
id: Identifier.descending("session", input.id),
|
id: SessionID.descending(input.id),
|
||||||
slug: Slug.create(),
|
slug: Slug.create(),
|
||||||
version: Installation.VERSION,
|
version: Installation.VERSION,
|
||||||
projectID: Instance.project.id,
|
projectID: Instance.project.id,
|
||||||
@@ -342,13 +343,13 @@ export namespace Session {
|
|||||||
return path.join(base, [input.time.created, input.slug].join("-") + ".md")
|
return path.join(base, [input.time.created, input.slug].join("-") + ".md")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const get = fn(Identifier.schema("session"), async (id) => {
|
export const get = fn(SessionID.zod, async (id) => {
|
||||||
const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get())
|
const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get())
|
||||||
if (!row) throw new NotFoundError({ message: `Session not found: ${id}` })
|
if (!row) throw new NotFoundError({ message: `Session not found: ${id}` })
|
||||||
return fromRow(row)
|
return fromRow(row)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const share = fn(Identifier.schema("session"), async (id) => {
|
export const share = fn(SessionID.zod, async (id) => {
|
||||||
const cfg = await Config.get()
|
const cfg = await Config.get()
|
||||||
if (cfg.share === "disabled") {
|
if (cfg.share === "disabled") {
|
||||||
throw new Error("Sharing is disabled in configuration")
|
throw new Error("Sharing is disabled in configuration")
|
||||||
@@ -364,7 +365,7 @@ export namespace Session {
|
|||||||
return share
|
return share
|
||||||
})
|
})
|
||||||
|
|
||||||
export const unshare = fn(Identifier.schema("session"), async (id) => {
|
export const unshare = fn(SessionID.zod, async (id) => {
|
||||||
// Use ShareNext to remove the share (same as share function uses ShareNext to create)
|
// Use ShareNext to remove the share (same as share function uses ShareNext to create)
|
||||||
const { ShareNext } = await import("@/share/share-next")
|
const { ShareNext } = await import("@/share/share-next")
|
||||||
await ShareNext.remove(id)
|
await ShareNext.remove(id)
|
||||||
@@ -378,7 +379,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const setTitle = fn(
|
export const setTitle = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -399,7 +400,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const setArchived = fn(
|
export const setArchived = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
time: z.number().optional(),
|
time: z.number().optional(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -420,7 +421,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const setPermission = fn(
|
export const setPermission = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
permission: PermissionNext.Ruleset,
|
permission: PermissionNext.Ruleset,
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -441,7 +442,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const setRevert = fn(
|
export const setRevert = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
revert: Info.shape.revert,
|
revert: Info.shape.revert,
|
||||||
summary: Info.shape.summary,
|
summary: Info.shape.summary,
|
||||||
}),
|
}),
|
||||||
@@ -467,7 +468,7 @@ export namespace Session {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const clearRevert = fn(Identifier.schema("session"), async (sessionID) => {
|
export const clearRevert = fn(SessionID.zod, async (sessionID) => {
|
||||||
return Database.use((db) => {
|
return Database.use((db) => {
|
||||||
const row = db
|
const row = db
|
||||||
.update(SessionTable)
|
.update(SessionTable)
|
||||||
@@ -487,7 +488,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const setSummary = fn(
|
export const setSummary = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
summary: Info.shape.summary,
|
summary: Info.shape.summary,
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -511,7 +512,7 @@ export namespace Session {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const diff = fn(Identifier.schema("session"), async (sessionID) => {
|
export const diff = fn(SessionID.zod, async (sessionID) => {
|
||||||
try {
|
try {
|
||||||
return await Storage.read<Snapshot.FileDiff[]>(["session_diff", sessionID])
|
return await Storage.read<Snapshot.FileDiff[]>(["session_diff", sessionID])
|
||||||
} catch {
|
} catch {
|
||||||
@@ -521,7 +522,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const messages = fn(
|
export const messages = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
limit: z.number().optional(),
|
limit: z.number().optional(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -647,7 +648,7 @@ export namespace Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const children = fn(Identifier.schema("session"), async (parentID) => {
|
export const children = fn(SessionID.zod, async (parentID) => {
|
||||||
const project = Instance.project
|
const project = Instance.project
|
||||||
const rows = Database.use((db) =>
|
const rows = Database.use((db) =>
|
||||||
db
|
db
|
||||||
@@ -659,7 +660,7 @@ export namespace Session {
|
|||||||
return rows.map(fromRow)
|
return rows.map(fromRow)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const remove = fn(Identifier.schema("session"), async (sessionID) => {
|
export const remove = fn(SessionID.zod, async (sessionID) => {
|
||||||
const project = Instance.project
|
const project = Instance.project
|
||||||
try {
|
try {
|
||||||
const session = await get(sessionID)
|
const session = await get(sessionID)
|
||||||
@@ -705,7 +706,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const removeMessage = fn(
|
export const removeMessage = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -727,7 +728,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const removePart = fn(
|
export const removePart = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
partID: Identifier.schema("part"),
|
partID: Identifier.schema("part"),
|
||||||
}),
|
}),
|
||||||
@@ -775,7 +776,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const updatePartDelta = fn(
|
export const updatePartDelta = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string(),
|
messageID: z.string(),
|
||||||
partID: z.string(),
|
partID: z.string(),
|
||||||
field: z.string(),
|
field: z.string(),
|
||||||
@@ -873,7 +874,7 @@ export namespace Session {
|
|||||||
|
|
||||||
export const initialize = fn(
|
export const initialize = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
modelID: z.string(),
|
modelID: z.string(),
|
||||||
providerID: z.string(),
|
providerID: z.string(),
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { NamedError } from "@opencode-ai/util/error"
|
import { NamedError } from "@opencode-ai/util/error"
|
||||||
import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai"
|
import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai"
|
||||||
@@ -79,7 +80,7 @@ export namespace MessageV2 {
|
|||||||
|
|
||||||
const PartBase = z.object({
|
const PartBase = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string(),
|
messageID: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -344,7 +345,7 @@ export namespace MessageV2 {
|
|||||||
|
|
||||||
const Base = z.object({
|
const Base = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const User = Base.extend({
|
export const User = Base.extend({
|
||||||
@@ -457,7 +458,7 @@ export namespace MessageV2 {
|
|||||||
Removed: BusEvent.define(
|
Removed: BusEvent.define(
|
||||||
"message.removed",
|
"message.removed",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string(),
|
messageID: z.string(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -470,7 +471,7 @@ export namespace MessageV2 {
|
|||||||
PartDelta: BusEvent.define(
|
PartDelta: BusEvent.define(
|
||||||
"message.part.delta",
|
"message.part.delta",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string(),
|
messageID: z.string(),
|
||||||
partID: z.string(),
|
partID: z.string(),
|
||||||
field: z.string(),
|
field: z.string(),
|
||||||
@@ -480,7 +481,7 @@ export namespace MessageV2 {
|
|||||||
PartRemoved: BusEvent.define(
|
PartRemoved: BusEvent.define(
|
||||||
"message.part.removed",
|
"message.part.removed",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string(),
|
messageID: z.string(),
|
||||||
partID: z.string(),
|
partID: z.string(),
|
||||||
}),
|
}),
|
||||||
@@ -728,7 +729,7 @@ export namespace MessageV2 {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stream = fn(Identifier.schema("session"), async function* (sessionID) {
|
export const stream = fn(SessionID.zod, async function* (sessionID) {
|
||||||
const size = 50
|
const size = 50
|
||||||
let offset = 0
|
let offset = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -792,7 +793,7 @@ export namespace MessageV2 {
|
|||||||
|
|
||||||
export const get = fn(
|
export const get = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
}),
|
}),
|
||||||
async (input): Promise<WithParts> => {
|
async (input): Promise<WithParts> => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import z from "zod"
|
import z from "zod"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import { NamedError } from "@opencode-ai/util/error"
|
import { NamedError } from "@opencode-ai/util/error"
|
||||||
|
|
||||||
export namespace Message {
|
export namespace Message {
|
||||||
@@ -142,7 +143,7 @@ export namespace Message {
|
|||||||
error: z
|
error: z
|
||||||
.discriminatedUnion("name", [AuthError.Schema, NamedError.Unknown.Schema, OutputLengthError.Schema])
|
.discriminatedUnion("name", [AuthError.Schema, NamedError.Unknown.Schema, OutputLengthError.Schema])
|
||||||
.optional(),
|
.optional(),
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
tool: z.record(
|
tool: z.record(
|
||||||
z.string(),
|
z.string(),
|
||||||
z
|
z
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { Config } from "@/config/config"
|
|||||||
import { SessionCompaction } from "./compaction"
|
import { SessionCompaction } from "./compaction"
|
||||||
import { PermissionNext } from "@/permission/next"
|
import { PermissionNext } from "@/permission/next"
|
||||||
import { Question } from "@/question"
|
import { Question } from "@/question"
|
||||||
|
import type { SessionID } from "./schema"
|
||||||
|
|
||||||
export namespace SessionProcessor {
|
export namespace SessionProcessor {
|
||||||
const DOOM_LOOP_THRESHOLD = 3
|
const DOOM_LOOP_THRESHOLD = 3
|
||||||
@@ -25,7 +26,7 @@ export namespace SessionProcessor {
|
|||||||
|
|
||||||
export function create(input: {
|
export function create(input: {
|
||||||
assistantMessage: MessageV2.Assistant
|
assistantMessage: MessageV2.Assistant
|
||||||
sessionID: string
|
sessionID: SessionID
|
||||||
model: Provider.Model
|
model: Provider.Model
|
||||||
abort: AbortSignal
|
abort: AbortSignal
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import fs from "fs/promises"
|
|||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Filesystem } from "../util/filesystem"
|
import { Filesystem } from "../util/filesystem"
|
||||||
import { Identifier } from "../id/id"
|
import { Identifier } from "../id/id"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import { MessageV2 } from "./message-v2"
|
import { MessageV2 } from "./message-v2"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { SessionRevert } from "./revert"
|
import { SessionRevert } from "./revert"
|
||||||
@@ -84,13 +85,13 @@ export namespace SessionPrompt {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export function assertNotBusy(sessionID: string) {
|
export function assertNotBusy(sessionID: SessionID) {
|
||||||
const match = state()[sessionID]
|
const match = state()[sessionID]
|
||||||
if (match) throw new Session.BusyError(sessionID)
|
if (match) throw new Session.BusyError(sessionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PromptInput = z.object({
|
export const PromptInput = z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
messageID: Identifier.schema("message").optional(),
|
messageID: Identifier.schema("message").optional(),
|
||||||
model: z
|
model: z
|
||||||
.object({
|
.object({
|
||||||
@@ -254,7 +255,7 @@ export namespace SessionPrompt {
|
|||||||
return s[sessionID].abort.signal
|
return s[sessionID].abort.signal
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cancel(sessionID: string) {
|
export function cancel(sessionID: SessionID) {
|
||||||
log.info("cancel", { sessionID })
|
log.info("cancel", { sessionID })
|
||||||
const s = state()
|
const s = state()
|
||||||
const match = s[sessionID]
|
const match = s[sessionID]
|
||||||
@@ -269,7 +270,7 @@ export namespace SessionPrompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const LoopInput = z.object({
|
export const LoopInput = z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
resume_existing: z.boolean().optional(),
|
resume_existing: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
export const loop = fn(LoopInput, async (input) => {
|
export const loop = fn(LoopInput, async (input) => {
|
||||||
@@ -731,7 +732,7 @@ export namespace SessionPrompt {
|
|||||||
throw new Error("Impossible")
|
throw new Error("Impossible")
|
||||||
})
|
})
|
||||||
|
|
||||||
async function lastModel(sessionID: string) {
|
async function lastModel(sessionID: SessionID) {
|
||||||
for await (const item of MessageV2.stream(sessionID)) {
|
for await (const item of MessageV2.stream(sessionID)) {
|
||||||
if (item.info.role === "user" && item.info.model) return item.info.model
|
if (item.info.role === "user" && item.info.model) return item.info.model
|
||||||
}
|
}
|
||||||
@@ -1467,7 +1468,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ShellInput = z.object({
|
export const ShellInput = z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
agent: z.string(),
|
agent: z.string(),
|
||||||
model: z
|
model: z
|
||||||
.object({
|
.object({
|
||||||
@@ -1719,7 +1720,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
|||||||
|
|
||||||
export const CommandInput = z.object({
|
export const CommandInput = z.object({
|
||||||
messageID: Identifier.schema("message").optional(),
|
messageID: Identifier.schema("message").optional(),
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
agent: z.string().optional(),
|
agent: z.string().optional(),
|
||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
arguments: z.string(),
|
arguments: z.string(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Identifier } from "../id/id"
|
import { Identifier } from "../id/id"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import { Snapshot } from "../snapshot"
|
import { Snapshot } from "../snapshot"
|
||||||
import { MessageV2 } from "./message-v2"
|
import { MessageV2 } from "./message-v2"
|
||||||
import { Session } from "."
|
import { Session } from "."
|
||||||
@@ -15,7 +16,7 @@ export namespace SessionRevert {
|
|||||||
const log = Log.create({ service: "session.revert" })
|
const log = Log.create({ service: "session.revert" })
|
||||||
|
|
||||||
export const RevertInput = z.object({
|
export const RevertInput = z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
partID: Identifier.schema("part").optional(),
|
partID: Identifier.schema("part").optional(),
|
||||||
})
|
})
|
||||||
@@ -79,7 +80,7 @@ export namespace SessionRevert {
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unrevert(input: { sessionID: string }) {
|
export async function unrevert(input: { sessionID: SessionID }) {
|
||||||
log.info("unreverting", input)
|
log.info("unreverting", input)
|
||||||
SessionPrompt.assertNotBusy(input.sessionID)
|
SessionPrompt.assertNotBusy(input.sessionID)
|
||||||
const session = await Session.get(input.sessionID)
|
const session = await Session.get(input.sessionID)
|
||||||
|
|||||||
17
packages/opencode/src/session/schema.ts
Normal file
17
packages/opencode/src/session/schema.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Schema } from "effect"
|
||||||
|
import z from "zod"
|
||||||
|
|
||||||
|
import { withStatics } from "@/util/schema"
|
||||||
|
import { Identifier } from "@/id/id"
|
||||||
|
|
||||||
|
const sessionIdSchema = Schema.String.pipe(Schema.brand("SessionId"))
|
||||||
|
|
||||||
|
export type SessionID = typeof sessionIdSchema.Type
|
||||||
|
|
||||||
|
export const SessionID = sessionIdSchema.pipe(
|
||||||
|
withStatics((schema: typeof sessionIdSchema) => ({
|
||||||
|
make: (id: string) => schema.makeUnsafe(id),
|
||||||
|
descending: (id?: string) => schema.makeUnsafe(Identifier.descending("session", id)),
|
||||||
|
zod: z.string().startsWith("ses").pipe(z.custom<SessionID>()),
|
||||||
|
})),
|
||||||
|
)
|
||||||
@@ -4,6 +4,7 @@ import type { MessageV2 } from "./message-v2"
|
|||||||
import type { Snapshot } from "../snapshot"
|
import type { Snapshot } from "../snapshot"
|
||||||
import type { PermissionNext } from "../permission/next"
|
import type { PermissionNext } from "../permission/next"
|
||||||
import type { ProjectID } from "../project/schema"
|
import type { ProjectID } from "../project/schema"
|
||||||
|
import type { SessionID } from "./schema"
|
||||||
import { Timestamps } from "../storage/schema.sql"
|
import { Timestamps } from "../storage/schema.sql"
|
||||||
|
|
||||||
type PartData = Omit<MessageV2.Part, "id" | "sessionID" | "messageID">
|
type PartData = Omit<MessageV2.Part, "id" | "sessionID" | "messageID">
|
||||||
@@ -12,13 +13,13 @@ type InfoData = Omit<MessageV2.Info, "id" | "sessionID">
|
|||||||
export const SessionTable = sqliteTable(
|
export const SessionTable = sqliteTable(
|
||||||
"session",
|
"session",
|
||||||
{
|
{
|
||||||
id: text().primaryKey(),
|
id: text().$type<SessionID>().primaryKey(),
|
||||||
project_id: text()
|
project_id: text()
|
||||||
.$type<ProjectID>()
|
.$type<ProjectID>()
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => ProjectTable.id, { onDelete: "cascade" }),
|
.references(() => ProjectTable.id, { onDelete: "cascade" }),
|
||||||
workspace_id: text(),
|
workspace_id: text(),
|
||||||
parent_id: text(),
|
parent_id: text().$type<SessionID>(),
|
||||||
slug: text().notNull(),
|
slug: text().notNull(),
|
||||||
directory: text().notNull(),
|
directory: text().notNull(),
|
||||||
title: text().notNull(),
|
title: text().notNull(),
|
||||||
@@ -46,6 +47,7 @@ export const MessageTable = sqliteTable(
|
|||||||
{
|
{
|
||||||
id: text().primaryKey(),
|
id: text().primaryKey(),
|
||||||
session_id: text()
|
session_id: text()
|
||||||
|
.$type<SessionID>()
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => SessionTable.id, { onDelete: "cascade" }),
|
.references(() => SessionTable.id, { onDelete: "cascade" }),
|
||||||
...Timestamps,
|
...Timestamps,
|
||||||
@@ -61,7 +63,7 @@ export const PartTable = sqliteTable(
|
|||||||
message_id: text()
|
message_id: text()
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => MessageTable.id, { onDelete: "cascade" }),
|
.references(() => MessageTable.id, { onDelete: "cascade" }),
|
||||||
session_id: text().notNull(),
|
session_id: text().$type<SessionID>().notNull(),
|
||||||
...Timestamps,
|
...Timestamps,
|
||||||
data: text({ mode: "json" }).notNull().$type<PartData>(),
|
data: text({ mode: "json" }).notNull().$type<PartData>(),
|
||||||
},
|
},
|
||||||
@@ -72,6 +74,7 @@ export const TodoTable = sqliteTable(
|
|||||||
"todo",
|
"todo",
|
||||||
{
|
{
|
||||||
session_id: text()
|
session_id: text()
|
||||||
|
.$type<SessionID>()
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => SessionTable.id, { onDelete: "cascade" }),
|
.references(() => SessionTable.id, { onDelete: "cascade" }),
|
||||||
content: text().notNull(),
|
content: text().notNull(),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
|
|
||||||
export namespace SessionStatus {
|
export namespace SessionStatus {
|
||||||
@@ -28,7 +29,7 @@ export namespace SessionStatus {
|
|||||||
Status: BusEvent.define(
|
Status: BusEvent.define(
|
||||||
"session.status",
|
"session.status",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
status: Info,
|
status: Info,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -36,7 +37,7 @@ export namespace SessionStatus {
|
|||||||
Idle: BusEvent.define(
|
Idle: BusEvent.define(
|
||||||
"session.idle",
|
"session.idle",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ export namespace SessionStatus {
|
|||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
|
|
||||||
export function get(sessionID: string) {
|
export function get(sessionID: SessionID) {
|
||||||
return (
|
return (
|
||||||
state()[sessionID] ?? {
|
state()[sessionID] ?? {
|
||||||
type: "idle",
|
type: "idle",
|
||||||
@@ -58,7 +59,7 @@ export namespace SessionStatus {
|
|||||||
return state()
|
return state()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function set(sessionID: string, status: Info) {
|
export function set(sessionID: SessionID, status: Info) {
|
||||||
Bus.publish(Event.Status, {
|
Bus.publish(Event.Status, {
|
||||||
sessionID,
|
sessionID,
|
||||||
status,
|
status,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Session } from "."
|
|||||||
|
|
||||||
import { MessageV2 } from "./message-v2"
|
import { MessageV2 } from "./message-v2"
|
||||||
import { Identifier } from "@/id/id"
|
import { Identifier } from "@/id/id"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
|
|
||||||
import { Storage } from "@/storage/storage"
|
import { Storage } from "@/storage/storage"
|
||||||
@@ -68,7 +69,7 @@ export namespace SessionSummary {
|
|||||||
|
|
||||||
export const summarize = fn(
|
export const summarize = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
messageID: z.string(),
|
messageID: z.string(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
@@ -80,7 +81,7 @@ export namespace SessionSummary {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async function summarizeSession(input: { sessionID: string; messages: MessageV2.WithParts[] }) {
|
async function summarizeSession(input: { sessionID: SessionID; messages: MessageV2.WithParts[] }) {
|
||||||
const diffs = await computeDiff({ messages: input.messages })
|
const diffs = await computeDiff({ messages: input.messages })
|
||||||
await Session.setSummary({
|
await Session.setSummary({
|
||||||
sessionID: input.sessionID,
|
sessionID: input.sessionID,
|
||||||
@@ -113,7 +114,7 @@ export namespace SessionSummary {
|
|||||||
|
|
||||||
export const diff = fn(
|
export const diff = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: SessionID.zod,
|
||||||
messageID: Identifier.schema("message").optional(),
|
messageID: Identifier.schema("message").optional(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
|
import { SessionID } from "./schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Database, eq, asc } from "../storage/db"
|
import { Database, eq, asc } from "../storage/db"
|
||||||
import { TodoTable } from "./session.sql"
|
import { TodoTable } from "./session.sql"
|
||||||
@@ -18,13 +19,13 @@ export namespace Todo {
|
|||||||
Updated: BusEvent.define(
|
Updated: BusEvent.define(
|
||||||
"todo.updated",
|
"todo.updated",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: SessionID.zod,
|
||||||
todos: z.array(Info),
|
todos: z.array(Info),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update(input: { sessionID: string; todos: Info[] }) {
|
export function update(input: { sessionID: SessionID; todos: Info[] }) {
|
||||||
Database.transaction((db) => {
|
Database.transaction((db) => {
|
||||||
db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run()
|
db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run()
|
||||||
if (input.todos.length === 0) return
|
if (input.todos.length === 0) return
|
||||||
@@ -43,7 +44,7 @@ export namespace Todo {
|
|||||||
Bus.publish(Event.Updated, input)
|
Bus.publish(Event.Updated, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get(sessionID: string) {
|
export function get(sessionID: SessionID) {
|
||||||
const rows = Database.use((db) =>
|
const rows = Database.use((db) =>
|
||||||
db.select().from(TodoTable).where(eq(TodoTable.session_id, sessionID)).orderBy(asc(TodoTable.position)).all(),
|
db.select().from(TodoTable).where(eq(TodoTable.session_id, sessionID)).orderBy(asc(TodoTable.position)).all(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Account } from "@/account"
|
|||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
import { Provider } from "@/provider/provider"
|
import { Provider } from "@/provider/provider"
|
||||||
import { Session } from "@/session"
|
import { Session } from "@/session"
|
||||||
|
import type { SessionID } from "@/session/schema"
|
||||||
import { MessageV2 } from "@/session/message-v2"
|
import { MessageV2 } from "@/session/message-v2"
|
||||||
import { Database, eq } from "@/storage/db"
|
import { Database, eq } from "@/storage/db"
|
||||||
import { SessionShareTable } from "./share.sql"
|
import { SessionShareTable } from "./share.sql"
|
||||||
@@ -109,7 +110,7 @@ export namespace ShareNext {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(sessionID: string) {
|
export async function create(sessionID: SessionID) {
|
||||||
if (disabled) return { id: "", url: "", secret: "" }
|
if (disabled) return { id: "", url: "", secret: "" }
|
||||||
log.info("creating share", { sessionID })
|
log.info("creating share", { sessionID })
|
||||||
const req = await request()
|
const req = await request()
|
||||||
@@ -140,7 +141,7 @@ export namespace ShareNext {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(sessionID: string) {
|
function get(sessionID: SessionID) {
|
||||||
const row = Database.use((db) =>
|
const row = Database.use((db) =>
|
||||||
db.select().from(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).get(),
|
db.select().from(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).get(),
|
||||||
)
|
)
|
||||||
@@ -186,7 +187,7 @@ export namespace ShareNext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queue = new Map<string, { timeout: NodeJS.Timeout; data: Map<string, Data> }>()
|
const queue = new Map<string, { timeout: NodeJS.Timeout; data: Map<string, Data> }>()
|
||||||
async function sync(sessionID: string, data: Data[]) {
|
async function sync(sessionID: SessionID, data: Data[]) {
|
||||||
if (disabled) return
|
if (disabled) return
|
||||||
const existing = queue.get(sessionID)
|
const existing = queue.get(sessionID)
|
||||||
if (existing) {
|
if (existing) {
|
||||||
@@ -225,7 +226,7 @@ export namespace ShareNext {
|
|||||||
queue.set(sessionID, { timeout, data: dataMap })
|
queue.set(sessionID, { timeout, data: dataMap })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove(sessionID: string) {
|
export async function remove(sessionID: SessionID) {
|
||||||
if (disabled) return
|
if (disabled) return
|
||||||
log.info("removing share", { sessionID })
|
log.info("removing share", { sessionID })
|
||||||
const share = get(sessionID)
|
const share = get(sessionID)
|
||||||
@@ -248,7 +249,7 @@ export namespace ShareNext {
|
|||||||
Database.use((db) => db.delete(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).run())
|
Database.use((db) => db.delete(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).run())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fullSync(sessionID: string) {
|
async function fullSync(sessionID: SessionID) {
|
||||||
log.info("full sync", { sessionID })
|
log.info("full sync", { sessionID })
|
||||||
const session = await Session.get(sessionID)
|
const session = await Session.get(sessionID)
|
||||||
const diffs = await Session.diff(sessionID)
|
const diffs = await Session.diff(sessionID)
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import { MessageV2 } from "../session/message-v2"
|
|||||||
import { Identifier } from "../id/id"
|
import { Identifier } from "../id/id"
|
||||||
import { Provider } from "../provider/provider"
|
import { Provider } from "../provider/provider"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
|
import type { SessionID } from "../session/schema"
|
||||||
import EXIT_DESCRIPTION from "./plan-exit.txt"
|
import EXIT_DESCRIPTION from "./plan-exit.txt"
|
||||||
|
|
||||||
async function getLastModel(sessionID: string) {
|
async function getLastModel(sessionID: SessionID) {
|
||||||
for await (const item of MessageV2.stream(sessionID)) {
|
for await (const item of MessageV2.stream(sessionID)) {
|
||||||
if (item.info.role === "user" && item.info.model) return item.info.model
|
if (item.info.role === "user" && item.info.model) return item.info.model
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Tool } from "./tool"
|
|||||||
import DESCRIPTION from "./task.txt"
|
import DESCRIPTION from "./task.txt"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Session } from "../session"
|
import { Session } from "../session"
|
||||||
|
import { SessionID } from "../session/schema"
|
||||||
import { MessageV2 } from "../session/message-v2"
|
import { MessageV2 } from "../session/message-v2"
|
||||||
import { Identifier } from "../id/id"
|
import { Identifier } from "../id/id"
|
||||||
import { Agent } from "../agent/agent"
|
import { Agent } from "../agent/agent"
|
||||||
@@ -65,7 +66,7 @@ export const TaskTool = Tool.define("task", async (ctx) => {
|
|||||||
|
|
||||||
const session = await iife(async () => {
|
const session = await iife(async () => {
|
||||||
if (params.task_id) {
|
if (params.task_id) {
|
||||||
const found = await Session.get(params.task_id).catch(() => {})
|
const found = await Session.get(SessionID.make(params.task_id)).catch(() => {})
|
||||||
if (found) return found
|
if (found) return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import z from "zod"
|
|||||||
import type { MessageV2 } from "../session/message-v2"
|
import type { MessageV2 } from "../session/message-v2"
|
||||||
import type { Agent } from "../agent/agent"
|
import type { Agent } from "../agent/agent"
|
||||||
import type { PermissionNext } from "../permission/next"
|
import type { PermissionNext } from "../permission/next"
|
||||||
|
import type { SessionID } from "../session/schema"
|
||||||
import { Truncate } from "./truncation"
|
import { Truncate } from "./truncation"
|
||||||
|
|
||||||
export namespace Tool {
|
export namespace Tool {
|
||||||
@@ -14,7 +15,7 @@ export namespace Tool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Context<M extends Metadata = Metadata> = {
|
export type Context<M extends Metadata = Metadata> = {
|
||||||
sessionID: string
|
sessionID: SessionID
|
||||||
messageID: string
|
messageID: string
|
||||||
agent: string
|
agent: string
|
||||||
abort: AbortSignal
|
abort: AbortSignal
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { test, expect, describe } from "bun:test"
|
import { test, expect, describe } from "bun:test"
|
||||||
import { extractResponseText, formatPromptTooLargeError } from "../../src/cli/cmd/github"
|
import { extractResponseText, formatPromptTooLargeError } from "../../src/cli/cmd/github"
|
||||||
import type { MessageV2 } from "../../src/session/message-v2"
|
import type { MessageV2 } from "../../src/session/message-v2"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
// Helper to create minimal valid parts
|
// Helper to create minimal valid parts
|
||||||
function createTextPart(text: string): MessageV2.Part {
|
function createTextPart(text: string): MessageV2.Part {
|
||||||
return {
|
return {
|
||||||
id: "1",
|
id: "1",
|
||||||
sessionID: "s",
|
sessionID: SessionID.make("s"),
|
||||||
messageID: "m",
|
messageID: "m",
|
||||||
type: "text" as const,
|
type: "text" as const,
|
||||||
text,
|
text,
|
||||||
@@ -16,7 +17,7 @@ function createTextPart(text: string): MessageV2.Part {
|
|||||||
function createReasoningPart(text: string): MessageV2.Part {
|
function createReasoningPart(text: string): MessageV2.Part {
|
||||||
return {
|
return {
|
||||||
id: "1",
|
id: "1",
|
||||||
sessionID: "s",
|
sessionID: SessionID.make("s"),
|
||||||
messageID: "m",
|
messageID: "m",
|
||||||
type: "reasoning" as const,
|
type: "reasoning" as const,
|
||||||
text,
|
text,
|
||||||
@@ -28,7 +29,7 @@ function createToolPart(tool: string, title: string, status: "completed" | "runn
|
|||||||
if (status === "completed") {
|
if (status === "completed") {
|
||||||
return {
|
return {
|
||||||
id: "1",
|
id: "1",
|
||||||
sessionID: "s",
|
sessionID: SessionID.make("s"),
|
||||||
messageID: "m",
|
messageID: "m",
|
||||||
type: "tool" as const,
|
type: "tool" as const,
|
||||||
callID: "c1",
|
callID: "c1",
|
||||||
@@ -45,7 +46,7 @@ function createToolPart(tool: string, title: string, status: "completed" | "runn
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: "1",
|
id: "1",
|
||||||
sessionID: "s",
|
sessionID: SessionID.make("s"),
|
||||||
messageID: "m",
|
messageID: "m",
|
||||||
type: "tool" as const,
|
type: "tool" as const,
|
||||||
callID: "c1",
|
callID: "c1",
|
||||||
@@ -61,7 +62,7 @@ function createToolPart(tool: string, title: string, status: "completed" | "runn
|
|||||||
function createStepStartPart(): MessageV2.Part {
|
function createStepStartPart(): MessageV2.Part {
|
||||||
return {
|
return {
|
||||||
id: "1",
|
id: "1",
|
||||||
sessionID: "s",
|
sessionID: SessionID.make("s"),
|
||||||
messageID: "m",
|
messageID: "m",
|
||||||
type: "step-start" as const,
|
type: "step-start" as const,
|
||||||
}
|
}
|
||||||
@@ -70,7 +71,7 @@ function createStepStartPart(): MessageV2.Part {
|
|||||||
function createStepFinishPart(): MessageV2.Part {
|
function createStepFinishPart(): MessageV2.Part {
|
||||||
return {
|
return {
|
||||||
id: "1",
|
id: "1",
|
||||||
sessionID: "s",
|
sessionID: SessionID.make("s"),
|
||||||
messageID: "m",
|
messageID: "m",
|
||||||
type: "step-finish" as const,
|
type: "step-finish" as const,
|
||||||
reason: "done",
|
reason: "done",
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import { describe, test, expect } from "bun:test"
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { WebFetchTool } from "../../src/tool/webfetch"
|
import { WebFetchTool } from "../../src/tool/webfetch"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const projectRoot = path.join(__dirname, "../..")
|
const projectRoot = path.join(__dirname, "../..")
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import os from "os"
|
|||||||
import { PermissionNext } from "../../src/permission/next"
|
import { PermissionNext } from "../../src/permission/next"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
// fromConfig tests
|
// fromConfig tests
|
||||||
|
|
||||||
@@ -462,7 +463,7 @@ test("ask - resolves immediately when action is allow", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const result = await PermissionNext.ask({
|
const result = await PermissionNext.ask({
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -481,7 +482,7 @@ test("ask - throws RejectedError when action is deny", async () => {
|
|||||||
fn: async () => {
|
fn: async () => {
|
||||||
await expect(
|
await expect(
|
||||||
PermissionNext.ask({
|
PermissionNext.ask({
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["rm -rf /"],
|
patterns: ["rm -rf /"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -499,7 +500,7 @@ test("ask - returns pending promise when action is ask", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const promise = PermissionNext.ask({
|
const promise = PermissionNext.ask({
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -522,7 +523,7 @@ test("reply - once resolves the pending ask", async () => {
|
|||||||
fn: async () => {
|
fn: async () => {
|
||||||
const askPromise = PermissionNext.ask({
|
const askPromise = PermissionNext.ask({
|
||||||
id: "permission_test1",
|
id: "permission_test1",
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -547,7 +548,7 @@ test("reply - reject throws RejectedError", async () => {
|
|||||||
fn: async () => {
|
fn: async () => {
|
||||||
const askPromise = PermissionNext.ask({
|
const askPromise = PermissionNext.ask({
|
||||||
id: "permission_test2",
|
id: "permission_test2",
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -572,7 +573,7 @@ test("reply - always persists approval and resolves", async () => {
|
|||||||
fn: async () => {
|
fn: async () => {
|
||||||
const askPromise = PermissionNext.ask({
|
const askPromise = PermissionNext.ask({
|
||||||
id: "permission_test3",
|
id: "permission_test3",
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -594,7 +595,7 @@ test("reply - always persists approval and resolves", async () => {
|
|||||||
fn: async () => {
|
fn: async () => {
|
||||||
// Stored approval should allow without asking
|
// Stored approval should allow without asking
|
||||||
const result = await PermissionNext.ask({
|
const result = await PermissionNext.ask({
|
||||||
sessionID: "session_test2",
|
sessionID: SessionID.make("session_test2"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -613,7 +614,7 @@ test("reply - reject cancels all pending for same session", async () => {
|
|||||||
fn: async () => {
|
fn: async () => {
|
||||||
const askPromise1 = PermissionNext.ask({
|
const askPromise1 = PermissionNext.ask({
|
||||||
id: "permission_test4a",
|
id: "permission_test4a",
|
||||||
sessionID: "session_same",
|
sessionID: SessionID.make("session_same"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -623,7 +624,7 @@ test("reply - reject cancels all pending for same session", async () => {
|
|||||||
|
|
||||||
const askPromise2 = PermissionNext.ask({
|
const askPromise2 = PermissionNext.ask({
|
||||||
id: "permission_test4b",
|
id: "permission_test4b",
|
||||||
sessionID: "session_same",
|
sessionID: SessionID.make("session_same"),
|
||||||
permission: "edit",
|
permission: "edit",
|
||||||
patterns: ["foo.ts"],
|
patterns: ["foo.ts"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -655,7 +656,7 @@ test("ask - checks all patterns and stops on first deny", async () => {
|
|||||||
fn: async () => {
|
fn: async () => {
|
||||||
await expect(
|
await expect(
|
||||||
PermissionNext.ask({
|
PermissionNext.ask({
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["echo hello", "rm -rf /"],
|
patterns: ["echo hello", "rm -rf /"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
@@ -676,7 +677,7 @@ test("ask - allows all patterns when all match allow rules", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const result = await PermissionNext.ask({
|
const result = await PermissionNext.ask({
|
||||||
sessionID: "session_test",
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["echo hello", "ls -la", "pwd"],
|
patterns: ["echo hello", "ls -la", "pwd"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { test, expect } from "bun:test"
|
|||||||
import { Question } from "../../src/question"
|
import { Question } from "../../src/question"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
test("ask - returns pending promise", async () => {
|
test("ask - returns pending promise", async () => {
|
||||||
await using tmp = await tmpdir({ git: true })
|
await using tmp = await tmpdir({ git: true })
|
||||||
@@ -9,7 +10,7 @@ test("ask - returns pending promise", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const promise = Question.ask({
|
const promise = Question.ask({
|
||||||
sessionID: "ses_test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: "What would you like to do?",
|
question: "What would you like to do?",
|
||||||
@@ -43,7 +44,7 @@ test("ask - adds to pending list", async () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
Question.ask({
|
Question.ask({
|
||||||
sessionID: "ses_test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
questions,
|
questions,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ test("reply - resolves the pending ask with answers", async () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
const askPromise = Question.ask({
|
const askPromise = Question.ask({
|
||||||
sessionID: "ses_test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
questions,
|
questions,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ test("reply - removes from pending list", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
Question.ask({
|
Question.ask({
|
||||||
sessionID: "ses_test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: "What would you like to do?",
|
question: "What would you like to do?",
|
||||||
@@ -146,7 +147,7 @@ test("reject - throws RejectedError", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const askPromise = Question.ask({
|
const askPromise = Question.ask({
|
||||||
sessionID: "ses_test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: "What would you like to do?",
|
question: "What would you like to do?",
|
||||||
@@ -173,7 +174,7 @@ test("reject - removes from pending list", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const askPromise = Question.ask({
|
const askPromise = Question.ask({
|
||||||
sessionID: "ses_test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: "What would you like to do?",
|
question: "What would you like to do?",
|
||||||
@@ -236,7 +237,7 @@ test("ask - handles multiple questions", async () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
const askPromise = Question.ask({
|
const askPromise = Question.ask({
|
||||||
sessionID: "ses_test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
questions,
|
questions,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -261,7 +262,7 @@ test("list - returns all pending requests", async () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
Question.ask({
|
Question.ask({
|
||||||
sessionID: "ses_test1",
|
sessionID: SessionID.make("ses_test1"),
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: "Question 1?",
|
question: "Question 1?",
|
||||||
@@ -272,7 +273,7 @@ test("list - returns all pending requests", async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Question.ask({
|
Question.ask({
|
||||||
sessionID: "ses_test2",
|
sessionID: SessionID.make("ses_test2"),
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: "Question 2?",
|
question: "Question 2?",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Filesystem } from "../../src/util/filesystem"
|
|||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import type { Agent } from "../../src/agent/agent"
|
import type { Agent } from "../../src/agent/agent"
|
||||||
import type { MessageV2 } from "../../src/session/message-v2"
|
import type { MessageV2 } from "../../src/session/message-v2"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
describe("session.llm.hasToolCalls", () => {
|
describe("session.llm.hasToolCalls", () => {
|
||||||
test("returns false for empty messages array", () => {
|
test("returns false for empty messages array", () => {
|
||||||
@@ -265,7 +266,7 @@ describe("session.llm.stream", () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const resolved = await Provider.getModel(providerID, model.id)
|
const resolved = await Provider.getModel(providerID, model.id)
|
||||||
const sessionID = "session-test-1"
|
const sessionID = SessionID.make("session-test-1")
|
||||||
const agent = {
|
const agent = {
|
||||||
name: "test",
|
name: "test",
|
||||||
mode: "primary",
|
mode: "primary",
|
||||||
@@ -395,7 +396,7 @@ describe("session.llm.stream", () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const resolved = await Provider.getModel("openai", model.id)
|
const resolved = await Provider.getModel("openai", model.id)
|
||||||
const sessionID = "session-test-2"
|
const sessionID = SessionID.make("session-test-2")
|
||||||
const agent = {
|
const agent = {
|
||||||
name: "test",
|
name: "test",
|
||||||
mode: "primary",
|
mode: "primary",
|
||||||
@@ -517,7 +518,7 @@ describe("session.llm.stream", () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const resolved = await Provider.getModel(providerID, model.id)
|
const resolved = await Provider.getModel(providerID, model.id)
|
||||||
const sessionID = "session-test-3"
|
const sessionID = SessionID.make("session-test-3")
|
||||||
const agent = {
|
const agent = {
|
||||||
name: "test",
|
name: "test",
|
||||||
mode: "primary",
|
mode: "primary",
|
||||||
@@ -618,7 +619,7 @@ describe("session.llm.stream", () => {
|
|||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const resolved = await Provider.getModel(providerID, model.id)
|
const resolved = await Provider.getModel(providerID, model.id)
|
||||||
const sessionID = "session-test-4"
|
const sessionID = SessionID.make("session-test-4")
|
||||||
const agent = {
|
const agent = {
|
||||||
name: "test",
|
name: "test",
|
||||||
mode: "primary",
|
mode: "primary",
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import { describe, expect, test } from "bun:test"
|
|||||||
import { APICallError } from "ai"
|
import { APICallError } from "ai"
|
||||||
import { MessageV2 } from "../../src/session/message-v2"
|
import { MessageV2 } from "../../src/session/message-v2"
|
||||||
import type { Provider } from "../../src/provider/provider"
|
import type { Provider } from "../../src/provider/provider"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const sessionID = "session"
|
const sessionID = SessionID.make("session")
|
||||||
const model: Provider.Model = {
|
const model: Provider.Model = {
|
||||||
id: "test-model",
|
id: "test-model",
|
||||||
providerID: "test",
|
providerID: "test",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { MessageV2 } from "../../src/session/message-v2"
|
import { MessageV2 } from "../../src/session/message-v2"
|
||||||
import { SessionPrompt } from "../../src/session/prompt"
|
import { SessionPrompt } from "../../src/session/prompt"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
describe("structured-output.OutputFormat", () => {
|
describe("structured-output.OutputFormat", () => {
|
||||||
test("parses text format", () => {
|
test("parses text format", () => {
|
||||||
@@ -96,7 +97,7 @@ describe("structured-output.UserMessage", () => {
|
|||||||
test("user message accepts outputFormat", () => {
|
test("user message accepts outputFormat", () => {
|
||||||
const result = MessageV2.User.safeParse({
|
const result = MessageV2.User.safeParse({
|
||||||
id: "test-id",
|
id: "test-id",
|
||||||
sessionID: "test-session",
|
sessionID: SessionID.descending(),
|
||||||
role: "user",
|
role: "user",
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
agent: "default",
|
agent: "default",
|
||||||
@@ -112,7 +113,7 @@ describe("structured-output.UserMessage", () => {
|
|||||||
test("user message works without outputFormat (optional)", () => {
|
test("user message works without outputFormat (optional)", () => {
|
||||||
const result = MessageV2.User.safeParse({
|
const result = MessageV2.User.safeParse({
|
||||||
id: "test-id",
|
id: "test-id",
|
||||||
sessionID: "test-session",
|
sessionID: SessionID.descending(),
|
||||||
role: "user",
|
role: "user",
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
agent: "default",
|
agent: "default",
|
||||||
@@ -125,7 +126,7 @@ describe("structured-output.UserMessage", () => {
|
|||||||
describe("structured-output.AssistantMessage", () => {
|
describe("structured-output.AssistantMessage", () => {
|
||||||
const baseAssistantMessage = {
|
const baseAssistantMessage = {
|
||||||
id: "test-id",
|
id: "test-id",
|
||||||
sessionID: "test-session",
|
sessionID: SessionID.descending(),
|
||||||
role: "assistant" as const,
|
role: "assistant" as const,
|
||||||
parentID: "parent-id",
|
parentID: "parent-id",
|
||||||
modelID: "claude-3",
|
modelID: "claude-3",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { ProjectTable } from "../../src/project/project.sql"
|
|||||||
import { ProjectID } from "../../src/project/schema"
|
import { ProjectID } from "../../src/project/schema"
|
||||||
import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } from "../../src/session/session.sql"
|
import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } from "../../src/session/session.sql"
|
||||||
import { SessionShareTable } from "../../src/share/share.sql"
|
import { SessionShareTable } from "../../src/share/share.sql"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
// Test fixtures
|
// Test fixtures
|
||||||
const fixtures = {
|
const fixtures = {
|
||||||
@@ -220,7 +221,7 @@ describe("JSON to SQLite migration", () => {
|
|||||||
const db = drizzle({ client: sqlite })
|
const db = drizzle({ client: sqlite })
|
||||||
const sessions = db.select().from(SessionTable).all()
|
const sessions = db.select().from(SessionTable).all()
|
||||||
expect(sessions.length).toBe(1)
|
expect(sessions.length).toBe(1)
|
||||||
expect(sessions[0].id).toBe("ses_test456def")
|
expect(sessions[0].id).toBe(SessionID.make("ses_test456def"))
|
||||||
expect(sessions[0].project_id).toBe(ProjectID.make("proj_test123abc"))
|
expect(sessions[0].project_id).toBe(ProjectID.make("proj_test123abc"))
|
||||||
expect(sessions[0].slug).toBe("test-session")
|
expect(sessions[0].slug).toBe("test-session")
|
||||||
expect(sessions[0].title).toBe("Test Session Title")
|
expect(sessions[0].title).toBe("Test Session Title")
|
||||||
@@ -295,7 +296,7 @@ describe("JSON to SQLite migration", () => {
|
|||||||
const messages = db.select().from(MessageTable).all()
|
const messages = db.select().from(MessageTable).all()
|
||||||
expect(messages.length).toBe(1)
|
expect(messages.length).toBe(1)
|
||||||
expect(messages[0].id).toBe("msg_test789ghi")
|
expect(messages[0].id).toBe("msg_test789ghi")
|
||||||
expect(messages[0].session_id).toBe("ses_test456def")
|
expect(messages[0].session_id).toBe(SessionID.make("ses_test456def"))
|
||||||
expect(messages[0].data).not.toHaveProperty("id")
|
expect(messages[0].data).not.toHaveProperty("id")
|
||||||
expect(messages[0].data).not.toHaveProperty("sessionID")
|
expect(messages[0].data).not.toHaveProperty("sessionID")
|
||||||
|
|
||||||
@@ -303,7 +304,7 @@ describe("JSON to SQLite migration", () => {
|
|||||||
expect(parts.length).toBe(1)
|
expect(parts.length).toBe(1)
|
||||||
expect(parts[0].id).toBe("prt_testabc123")
|
expect(parts[0].id).toBe("prt_testabc123")
|
||||||
expect(parts[0].message_id).toBe("msg_test789ghi")
|
expect(parts[0].message_id).toBe("msg_test789ghi")
|
||||||
expect(parts[0].session_id).toBe("ses_test456def")
|
expect(parts[0].session_id).toBe(SessionID.make("ses_test456def"))
|
||||||
expect(parts[0].data).not.toHaveProperty("id")
|
expect(parts[0].data).not.toHaveProperty("id")
|
||||||
expect(parts[0].data).not.toHaveProperty("messageID")
|
expect(parts[0].data).not.toHaveProperty("messageID")
|
||||||
expect(parts[0].data).not.toHaveProperty("sessionID")
|
expect(parts[0].data).not.toHaveProperty("sessionID")
|
||||||
@@ -336,7 +337,7 @@ describe("JSON to SQLite migration", () => {
|
|||||||
const messages = db.select().from(MessageTable).all()
|
const messages = db.select().from(MessageTable).all()
|
||||||
expect(messages.length).toBe(1)
|
expect(messages.length).toBe(1)
|
||||||
expect(messages[0].id).toBe("msg_from_filename") // Uses filename, not JSON id
|
expect(messages[0].id).toBe("msg_from_filename") // Uses filename, not JSON id
|
||||||
expect(messages[0].session_id).toBe("ses_test456def")
|
expect(messages[0].session_id).toBe(SessionID.make("ses_test456def"))
|
||||||
})
|
})
|
||||||
|
|
||||||
test("uses paths for part id and messageID when JSON has different values", async () => {
|
test("uses paths for part id and messageID when JSON has different values", async () => {
|
||||||
@@ -426,7 +427,7 @@ describe("JSON to SQLite migration", () => {
|
|||||||
const db = drizzle({ client: sqlite })
|
const db = drizzle({ client: sqlite })
|
||||||
const sessions = db.select().from(SessionTable).all()
|
const sessions = db.select().from(SessionTable).all()
|
||||||
expect(sessions.length).toBe(1)
|
expect(sessions.length).toBe(1)
|
||||||
expect(sessions[0].id).toBe("ses_migrated")
|
expect(sessions[0].id).toBe(SessionID.make("ses_migrated"))
|
||||||
expect(sessions[0].project_id).toBe(ProjectID.make(gitBasedProjectID)) // Uses directory, not stale JSON
|
expect(sessions[0].project_id).toBe(ProjectID.make(gitBasedProjectID)) // Uses directory, not stale JSON
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -458,7 +459,7 @@ describe("JSON to SQLite migration", () => {
|
|||||||
const db = drizzle({ client: sqlite })
|
const db = drizzle({ client: sqlite })
|
||||||
const sessions = db.select().from(SessionTable).all()
|
const sessions = db.select().from(SessionTable).all()
|
||||||
expect(sessions.length).toBe(1)
|
expect(sessions.length).toBe(1)
|
||||||
expect(sessions[0].id).toBe("ses_from_filename") // Uses filename, not JSON id
|
expect(sessions[0].id).toBe(SessionID.make("ses_from_filename")) // Uses filename, not JSON id
|
||||||
expect(sessions[0].project_id).toBe(ProjectID.make("proj_test123abc"))
|
expect(sessions[0].project_id).toBe(ProjectID.make("proj_test123abc"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import * as fs from "fs/promises"
|
|||||||
import { ApplyPatchTool } from "../../src/tool/apply_patch"
|
import { ApplyPatchTool } from "../../src/tool/apply_patch"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const baseCtx = {
|
const baseCtx = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import { Filesystem } from "../../src/util/filesystem"
|
|||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import type { PermissionNext } from "../../src/permission/next"
|
import type { PermissionNext } from "../../src/permission/next"
|
||||||
import { Truncate } from "../../src/tool/truncation"
|
import { Truncate } from "../../src/tool/truncation"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import { EditTool } from "../../src/tool/edit"
|
|||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { FileTime } from "../../src/file/time"
|
import { FileTime } from "../../src/file/time"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test-edit-session",
|
sessionID: SessionID.make("ses_test-edit-session"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import type { Tool } from "../../src/tool/tool"
|
|||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { assertExternalDirectory } from "../../src/tool/external-directory"
|
import { assertExternalDirectory } from "../../src/tool/external-directory"
|
||||||
import type { PermissionNext } from "../../src/permission/next"
|
import type { PermissionNext } from "../../src/permission/next"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const baseCtx: Omit<Tool.Context, "ask"> = {
|
const baseCtx: Omit<Tool.Context, "ask"> = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import path from "path"
|
|||||||
import { GrepTool } from "../../src/tool/grep"
|
import { GrepTool } from "../../src/tool/grep"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test"
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { QuestionTool } from "../../src/tool/question"
|
import { QuestionTool } from "../../src/tool/question"
|
||||||
import * as QuestionModule from "../../src/question"
|
import * as QuestionModule from "../../src/question"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test-session",
|
sessionID: SessionID.make("ses_test-session"),
|
||||||
messageID: "test-message",
|
messageID: "test-message",
|
||||||
callID: "test-call",
|
callID: "test-call",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import { Filesystem } from "../../src/util/filesystem"
|
|||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { PermissionNext } from "../../src/permission/next"
|
import { PermissionNext } from "../../src/permission/next"
|
||||||
import { Agent } from "../../src/agent/agent"
|
import { Agent } from "../../src/agent/agent"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const FIXTURES_DIR = path.join(import.meta.dir, "fixtures")
|
const FIXTURES_DIR = path.join(import.meta.dir, "fixtures")
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import type { Tool } from "../../src/tool/tool"
|
|||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { SkillTool } from "../../src/tool/skill"
|
import { SkillTool } from "../../src/tool/skill"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const baseCtx: Omit<Tool.Context, "ask"> = {
|
const baseCtx: Omit<Tool.Context, "ask"> = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import { describe, expect, test } from "bun:test"
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { WebFetchTool } from "../../src/tool/webfetch"
|
import { WebFetchTool } from "../../src/tool/webfetch"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const projectRoot = path.join(import.meta.dir, "../..")
|
const projectRoot = path.join(import.meta.dir, "../..")
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test",
|
sessionID: SessionID.make("ses_test"),
|
||||||
messageID: "message",
|
messageID: "message",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import fs from "fs/promises"
|
|||||||
import { WriteTool } from "../../src/tool/write"
|
import { WriteTool } from "../../src/tool/write"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
import { SessionID } from "../../src/session/schema"
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
sessionID: "test-write-session",
|
sessionID: SessionID.make("ses_test-write-session"),
|
||||||
messageID: "",
|
messageID: "",
|
||||||
callID: "",
|
callID: "",
|
||||||
agent: "build",
|
agent: "build",
|
||||||
|
|||||||
Reference in New Issue
Block a user