refactor(session): remove async facade exports (#22396)
This commit is contained in:
parent
2fc5b00537
commit
4637ea7599
19 changed files with 557 additions and 423 deletions
|
|
@ -33,30 +33,35 @@ const seed = async () => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const session = await Session.create({ title })
|
await AppRuntime.runPromise(
|
||||||
const messageID = MessageID.ascending()
|
Effect.gen(function* () {
|
||||||
const partID = PartID.ascending()
|
const session = yield* Session.Service
|
||||||
const message = {
|
const result = yield* session.create({ title })
|
||||||
id: messageID,
|
const messageID = MessageID.ascending()
|
||||||
sessionID: session.id,
|
const partID = PartID.ascending()
|
||||||
role: "user" as const,
|
const message = {
|
||||||
time: { created: now },
|
id: messageID,
|
||||||
agent: "build",
|
sessionID: result.id,
|
||||||
model: {
|
role: "user" as const,
|
||||||
providerID: ProviderID.make(providerID),
|
time: { created: now },
|
||||||
modelID: ModelID.make(modelID),
|
agent: "build",
|
||||||
},
|
model: {
|
||||||
}
|
providerID: ProviderID.make(providerID),
|
||||||
const part = {
|
modelID: ModelID.make(modelID),
|
||||||
id: partID,
|
},
|
||||||
sessionID: session.id,
|
}
|
||||||
messageID,
|
const part = {
|
||||||
type: "text" as const,
|
id: partID,
|
||||||
text,
|
sessionID: result.id,
|
||||||
time: { start: now },
|
messageID,
|
||||||
}
|
type: "text" as const,
|
||||||
await Session.updateMessage(message)
|
text,
|
||||||
await Session.updatePart(part)
|
time: { start: now },
|
||||||
|
}
|
||||||
|
yield* session.updateMessage(message)
|
||||||
|
yield* session.updatePart(part)
|
||||||
|
}),
|
||||||
|
)
|
||||||
await AppRuntime.runPromise(
|
await AppRuntime.runPromise(
|
||||||
Project.Service.use((svc) => svc.update({ projectID: Instance.project.id, name: "E2E Project" })),
|
Project.Service.use((svc) => svc.update({ projectID: Instance.project.id, name: "E2E Project" })),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -123,45 +123,49 @@ function parseToolParams(input?: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createToolContext(agent: Agent.Info) {
|
async function createToolContext(agent: Agent.Info) {
|
||||||
const session = await Session.create({ title: `Debug tool run (${agent.name})` })
|
const { session, messageID } = await AppRuntime.runPromise(
|
||||||
const messageID = MessageID.ascending()
|
Effect.gen(function* () {
|
||||||
const model =
|
const session = yield* Session.Service
|
||||||
agent.model ??
|
const result = yield* session.create({ title: `Debug tool run (${agent.name})` })
|
||||||
(await AppRuntime.runPromise(
|
const messageID = MessageID.ascending()
|
||||||
Effect.gen(function* () {
|
const model = agent.model
|
||||||
const provider = yield* Provider.Service
|
? agent.model
|
||||||
return yield* provider.defaultModel()
|
: yield* Effect.gen(function* () {
|
||||||
}),
|
const provider = yield* Provider.Service
|
||||||
))
|
return yield* provider.defaultModel()
|
||||||
const now = Date.now()
|
})
|
||||||
const message: MessageV2.Assistant = {
|
const now = Date.now()
|
||||||
id: messageID,
|
const message: MessageV2.Assistant = {
|
||||||
sessionID: session.id,
|
id: messageID,
|
||||||
role: "assistant",
|
sessionID: result.id,
|
||||||
time: {
|
role: "assistant",
|
||||||
created: now,
|
time: {
|
||||||
},
|
created: now,
|
||||||
parentID: messageID,
|
},
|
||||||
modelID: model.modelID,
|
parentID: messageID,
|
||||||
providerID: model.providerID,
|
modelID: model.modelID,
|
||||||
mode: "debug",
|
providerID: model.providerID,
|
||||||
agent: agent.name,
|
mode: "debug",
|
||||||
path: {
|
agent: agent.name,
|
||||||
cwd: Instance.directory,
|
path: {
|
||||||
root: Instance.worktree,
|
cwd: Instance.directory,
|
||||||
},
|
root: Instance.worktree,
|
||||||
cost: 0,
|
},
|
||||||
tokens: {
|
cost: 0,
|
||||||
input: 0,
|
tokens: {
|
||||||
output: 0,
|
input: 0,
|
||||||
reasoning: 0,
|
output: 0,
|
||||||
cache: {
|
reasoning: 0,
|
||||||
read: 0,
|
cache: {
|
||||||
write: 0,
|
read: 0,
|
||||||
},
|
write: 0,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
await Session.updateMessage(message)
|
}
|
||||||
|
yield* session.updateMessage(message)
|
||||||
|
return { session: result, messageID }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const ruleset = Permission.merge(agent.permission, session.permission ?? [])
|
const ruleset = Permission.merge(agent.permission, session.permission ?? [])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import { bootstrap } from "../bootstrap"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import * as prompts from "@clack/prompts"
|
import * as prompts from "@clack/prompts"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
|
import { Effect } from "effect"
|
||||||
|
|
||||||
export const ExportCommand = cmd({
|
export const ExportCommand = cmd({
|
||||||
command: "export [sessionID]",
|
command: "export [sessionID]",
|
||||||
|
|
@ -67,8 +69,16 @@ export const ExportCommand = cmd({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const sessionInfo = await Session.get(sessionID!)
|
const { sessionInfo, messages } = await AppRuntime.runPromise(
|
||||||
const messages = await Session.messages({ sessionID: sessionInfo.id })
|
Effect.gen(function* () {
|
||||||
|
const session = yield* Session.Service
|
||||||
|
const sessionInfo = yield* session.get(sessionID!)
|
||||||
|
return {
|
||||||
|
sessionInfo,
|
||||||
|
messages: yield* session.messages({ sessionID: sessionInfo.id }),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const exportData = {
|
const exportData = {
|
||||||
info: sessionInfo,
|
info: sessionInfo,
|
||||||
|
|
|
||||||
|
|
@ -552,15 +552,19 @@ export const GithubRunCommand = cmd({
|
||||||
|
|
||||||
// Setup opencode session
|
// Setup opencode session
|
||||||
const repoData = await fetchRepo()
|
const repoData = await fetchRepo()
|
||||||
session = await Session.create({
|
session = await AppRuntime.runPromise(
|
||||||
permission: [
|
Session.Service.use((svc) =>
|
||||||
{
|
svc.create({
|
||||||
permission: "question",
|
permission: [
|
||||||
action: "deny",
|
{
|
||||||
pattern: "*",
|
permission: "question",
|
||||||
},
|
action: "deny",
|
||||||
],
|
pattern: "*",
|
||||||
})
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
subscribeSessionEvents()
|
subscribeSessionEvents()
|
||||||
shareId = await (async () => {
|
shareId = await (async () => {
|
||||||
if (share === false) return
|
if (share === false) return
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { Process } from "../../util/process"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { which } from "../../util/which"
|
import { which } from "../../util/which"
|
||||||
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
|
|
||||||
function pagerCmd(): string[] {
|
function pagerCmd(): string[] {
|
||||||
const lessOptions = ["-R", "-S"]
|
const lessOptions = ["-R", "-S"]
|
||||||
|
|
@ -60,12 +61,12 @@ export const SessionDeleteCommand = cmd({
|
||||||
await bootstrap(process.cwd(), async () => {
|
await bootstrap(process.cwd(), async () => {
|
||||||
const sessionID = SessionID.make(args.sessionID)
|
const sessionID = SessionID.make(args.sessionID)
|
||||||
try {
|
try {
|
||||||
await Session.get(sessionID)
|
await AppRuntime.runPromise(Session.Service.use((svc) => svc.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(sessionID)
|
await AppRuntime.runPromise(Session.Service.use((svc) => svc.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)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { Database } from "../../storage/db"
|
||||||
import { SessionTable } from "../../session/session.sql"
|
import { SessionTable } from "../../session/session.sql"
|
||||||
import { Project } from "../../project/project"
|
import { Project } from "../../project/project"
|
||||||
import { Instance } from "../../project/instance"
|
import { Instance } from "../../project/instance"
|
||||||
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
|
|
||||||
interface SessionStats {
|
interface SessionStats {
|
||||||
totalSessions: number
|
totalSessions: number
|
||||||
|
|
@ -167,7 +168,9 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
||||||
const batch = filteredSessions.slice(i, i + BATCH_SIZE)
|
const batch = filteredSessions.slice(i, i + BATCH_SIZE)
|
||||||
|
|
||||||
const batchPromises = batch.map(async (session) => {
|
const batchPromises = batch.map(async (session) => {
|
||||||
const messages = await Session.messages({ sessionID: session.id })
|
const messages = await AppRuntime.runPromise(
|
||||||
|
Session.Service.use((svc) => svc.messages({ sessionID: session.id })),
|
||||||
|
)
|
||||||
|
|
||||||
let sessionCost = 0
|
let sessionCost = 0
|
||||||
let sessionTokens = { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }
|
let sessionTokens = { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ async function getSessionWorkspace(url: URL) {
|
||||||
const id = getSessionID(url)
|
const id = getSessionID(url)
|
||||||
if (!id) return null
|
if (!id) return null
|
||||||
|
|
||||||
const session = await Session.get(id).catch(() => undefined)
|
const session = await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(id))).catch(() => undefined)
|
||||||
return session?.workspaceID
|
return session?.workspaceID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,12 +121,12 @@ export const SessionRoutes = lazy(() =>
|
||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Session.get.schema,
|
sessionID: Session.GetInput,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
const session = await Session.get(sessionID)
|
const session = await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(sessionID)))
|
||||||
return c.json(session)
|
return c.json(session)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -152,12 +152,12 @@ export const SessionRoutes = lazy(() =>
|
||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Session.children.schema,
|
sessionID: Session.ChildrenInput,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
const session = await Session.children(sessionID)
|
const session = await AppRuntime.runPromise(Session.Service.use((svc) => svc.children(sessionID)))
|
||||||
return c.json(session)
|
return c.json(session)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -209,7 +209,7 @@ export const SessionRoutes = lazy(() =>
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator("json", Session.create.schema),
|
validator("json", Session.CreateInput),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const body = c.req.valid("json") ?? {}
|
const body = c.req.valid("json") ?? {}
|
||||||
const session = await AppRuntime.runPromise(SessionShare.Service.use((svc) => svc.create(body)))
|
const session = await AppRuntime.runPromise(SessionShare.Service.use((svc) => svc.create(body)))
|
||||||
|
|
@ -237,12 +237,12 @@ export const SessionRoutes = lazy(() =>
|
||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Session.remove.schema,
|
sessionID: Session.RemoveInput,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
await Session.remove(sessionID)
|
await AppRuntime.runPromise(Session.Service.use((svc) => svc.remove(sessionID)))
|
||||||
return c.json(true)
|
return c.json(true)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -285,22 +285,27 @@ export const SessionRoutes = lazy(() =>
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
const updates = c.req.valid("json")
|
const updates = c.req.valid("json")
|
||||||
const current = await Session.get(sessionID)
|
const session = await AppRuntime.runPromise(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const session = yield* Session.Service
|
||||||
|
const current = yield* session.get(sessionID)
|
||||||
|
|
||||||
if (updates.title !== undefined) {
|
if (updates.title !== undefined) {
|
||||||
await Session.setTitle({ sessionID, title: updates.title })
|
yield* session.setTitle({ sessionID, title: updates.title })
|
||||||
}
|
}
|
||||||
if (updates.permission !== undefined) {
|
if (updates.permission !== undefined) {
|
||||||
await Session.setPermission({
|
yield* session.setPermission({
|
||||||
sessionID,
|
sessionID,
|
||||||
permission: Permission.merge(current.permission ?? [], updates.permission),
|
permission: Permission.merge(current.permission ?? [], updates.permission),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (updates.time?.archived !== undefined) {
|
if (updates.time?.archived !== undefined) {
|
||||||
await Session.setArchived({ sessionID, time: updates.time.archived })
|
yield* session.setArchived({ sessionID, time: updates.time.archived })
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await Session.get(sessionID)
|
return yield* session.get(sessionID)
|
||||||
|
}),
|
||||||
|
)
|
||||||
return c.json(session)
|
return c.json(session)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -375,14 +380,14 @@ export const SessionRoutes = lazy(() =>
|
||||||
validator(
|
validator(
|
||||||
"param",
|
"param",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: Session.fork.schema.shape.sessionID,
|
sessionID: Session.ForkInput.shape.sessionID,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
validator("json", Session.fork.schema.omit({ sessionID: true })),
|
validator("json", Session.ForkInput.omit({ sessionID: true })),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
const body = c.req.valid("json")
|
const body = c.req.valid("json")
|
||||||
const result = await Session.fork({ ...body, sessionID })
|
const result = await AppRuntime.runPromise(Session.Service.use((svc) => svc.fork({ ...body, sessionID })))
|
||||||
return c.json(result)
|
return c.json(result)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -661,15 +666,14 @@ export const SessionRoutes = lazy(() =>
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const query = c.req.valid("query")
|
const query = c.req.valid("query")
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
if (query.limit === undefined) {
|
if (query.limit === undefined || query.limit === 0) {
|
||||||
await Session.get(sessionID)
|
const messages = await AppRuntime.runPromise(
|
||||||
const messages = await Session.messages({ sessionID })
|
Effect.gen(function* () {
|
||||||
return c.json(messages)
|
const session = yield* Session.Service
|
||||||
}
|
yield* session.get(sessionID)
|
||||||
|
return yield* session.messages({ sessionID })
|
||||||
if (query.limit === 0) {
|
}),
|
||||||
await Session.get(sessionID)
|
)
|
||||||
const messages = await Session.messages({ sessionID })
|
|
||||||
return c.json(messages)
|
return c.json(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -797,11 +801,15 @@ export const SessionRoutes = lazy(() =>
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const params = c.req.valid("param")
|
const params = c.req.valid("param")
|
||||||
await Session.removePart({
|
await AppRuntime.runPromise(
|
||||||
sessionID: params.sessionID,
|
Session.Service.use((svc) =>
|
||||||
messageID: params.messageID,
|
svc.removePart({
|
||||||
partID: params.partID,
|
sessionID: params.sessionID,
|
||||||
})
|
messageID: params.messageID,
|
||||||
|
partID: params.partID,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
return c.json(true)
|
return c.json(true)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -839,7 +847,7 @@ export const SessionRoutes = lazy(() =>
|
||||||
`Part mismatch: body.id='${body.id}' vs partID='${params.partID}', body.messageID='${body.messageID}' vs messageID='${params.messageID}', body.sessionID='${body.sessionID}' vs sessionID='${params.sessionID}'`,
|
`Part mismatch: body.id='${body.id}' vs partID='${params.partID}', body.messageID='${body.messageID}' vs messageID='${params.messageID}', body.sessionID='${body.sessionID}' vs sessionID='${params.sessionID}'`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const part = await Session.updatePart(body)
|
const part = await AppRuntime.runPromise(Session.Service.use((svc) => svc.updatePart(body)))
|
||||||
return c.json(part)
|
return c.json(part)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import z from "zod"
|
||||||
import { Bus } from "../../bus"
|
import { Bus } from "../../bus"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
import { TuiEvent } from "@/cli/cmd/tui/event"
|
import { TuiEvent } from "@/cli/cmd/tui/event"
|
||||||
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
import { AsyncQueue } from "../../util/queue"
|
import { AsyncQueue } from "../../util/queue"
|
||||||
import { errors } from "../error"
|
import { errors } from "../error"
|
||||||
import { lazy } from "../../util/lazy"
|
import { lazy } from "../../util/lazy"
|
||||||
|
|
@ -370,7 +371,7 @@ export const TuiRoutes = lazy(() =>
|
||||||
validator("json", TuiEvent.SessionSelect.properties),
|
validator("json", TuiEvent.SessionSelect.properties),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const { sessionID } = c.req.valid("json")
|
const { sessionID } = c.req.valid("json")
|
||||||
await Session.get(sessionID)
|
await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(sessionID)))
|
||||||
await Bus.publish(TuiEvent.SessionSelect, { sessionID })
|
await Bus.publish(TuiEvent.SessionSelect, { sessionID })
|
||||||
return c.json(true)
|
return c.json(true)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import { updateSchema } from "../util/update-schema"
|
||||||
import { MessageV2 } from "./message-v2"
|
import { MessageV2 } from "./message-v2"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
import { fn } from "@/util/fn"
|
|
||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
import { ProjectID } from "../project/schema"
|
import { ProjectID } from "../project/schema"
|
||||||
import { WorkspaceID } from "../control-plane/schema"
|
import { WorkspaceID } from "../control-plane/schema"
|
||||||
|
|
@ -29,7 +28,6 @@ import type { Provider } from "@/provider/provider"
|
||||||
import { Permission } from "@/permission"
|
import { Permission } from "@/permission"
|
||||||
import { Global } from "@/global"
|
import { Global } from "@/global"
|
||||||
import { Effect, Layer, Option, Context } from "effect"
|
import { Effect, Layer, Option, Context } from "effect"
|
||||||
import { makeRuntime } from "@/effect/run-service"
|
|
||||||
|
|
||||||
export namespace Session {
|
export namespace Session {
|
||||||
const log = Log.create({ service: "session" })
|
const log = Log.create({ service: "session" })
|
||||||
|
|
@ -179,6 +177,30 @@ export namespace Session {
|
||||||
})
|
})
|
||||||
export type GlobalInfo = z.output<typeof GlobalInfo>
|
export type GlobalInfo = z.output<typeof GlobalInfo>
|
||||||
|
|
||||||
|
export const CreateInput = z
|
||||||
|
.object({
|
||||||
|
parentID: SessionID.zod.optional(),
|
||||||
|
title: z.string().optional(),
|
||||||
|
permission: Info.shape.permission,
|
||||||
|
workspaceID: WorkspaceID.zod.optional(),
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
export type CreateInput = z.output<typeof CreateInput>
|
||||||
|
|
||||||
|
export const ForkInput = z.object({ sessionID: SessionID.zod, messageID: MessageID.zod.optional() })
|
||||||
|
export const GetInput = SessionID.zod
|
||||||
|
export const ChildrenInput = SessionID.zod
|
||||||
|
export const RemoveInput = SessionID.zod
|
||||||
|
export const SetTitleInput = z.object({ sessionID: SessionID.zod, title: z.string() })
|
||||||
|
export const SetArchivedInput = z.object({ sessionID: SessionID.zod, time: z.number().optional() })
|
||||||
|
export const SetPermissionInput = z.object({ sessionID: SessionID.zod, permission: Permission.Ruleset })
|
||||||
|
export const SetRevertInput = z.object({
|
||||||
|
sessionID: SessionID.zod,
|
||||||
|
revert: Info.shape.revert,
|
||||||
|
summary: Info.shape.summary,
|
||||||
|
})
|
||||||
|
export const MessagesInput = z.object({ sessionID: SessionID.zod, limit: z.number().optional() })
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Created: SyncEvent.define({
|
Created: SyncEvent.define({
|
||||||
type: "session.created",
|
type: "session.created",
|
||||||
|
|
@ -682,48 +704,6 @@ export namespace Session {
|
||||||
|
|
||||||
export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(Storage.defaultLayer))
|
export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(Storage.defaultLayer))
|
||||||
|
|
||||||
const { runPromise } = makeRuntime(Service, defaultLayer)
|
|
||||||
|
|
||||||
export const create = fn(
|
|
||||||
z
|
|
||||||
.object({
|
|
||||||
parentID: SessionID.zod.optional(),
|
|
||||||
title: z.string().optional(),
|
|
||||||
permission: Info.shape.permission,
|
|
||||||
workspaceID: WorkspaceID.zod.optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
(input) => runPromise((svc) => svc.create(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const fork = fn(z.object({ sessionID: SessionID.zod, messageID: MessageID.zod.optional() }), (input) =>
|
|
||||||
runPromise((svc) => svc.fork(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const get = fn(SessionID.zod, (id) => runPromise((svc) => svc.get(id)))
|
|
||||||
|
|
||||||
export const setTitle = fn(z.object({ sessionID: SessionID.zod, title: z.string() }), (input) =>
|
|
||||||
runPromise((svc) => svc.setTitle(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const setArchived = fn(z.object({ sessionID: SessionID.zod, time: z.number().optional() }), (input) =>
|
|
||||||
runPromise((svc) => svc.setArchived(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const setPermission = fn(z.object({ sessionID: SessionID.zod, permission: Permission.Ruleset }), (input) =>
|
|
||||||
runPromise((svc) => svc.setPermission(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const setRevert = fn(
|
|
||||||
z.object({ sessionID: SessionID.zod, revert: Info.shape.revert, summary: Info.shape.summary }),
|
|
||||||
(input) =>
|
|
||||||
runPromise((svc) => svc.setRevert({ sessionID: input.sessionID, revert: input.revert, summary: input.summary })),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const messages = fn(z.object({ sessionID: SessionID.zod, limit: z.number().optional() }), (input) =>
|
|
||||||
runPromise((svc) => svc.messages(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export function* list(input?: {
|
export function* list(input?: {
|
||||||
directory?: string
|
directory?: string
|
||||||
workspaceID?: WorkspaceID
|
workspaceID?: WorkspaceID
|
||||||
|
|
@ -835,25 +815,4 @@ export namespace Session {
|
||||||
yield { ...fromRow(row), project }
|
yield { ...fromRow(row), project }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const children = fn(SessionID.zod, (id) => runPromise((svc) => svc.children(id)))
|
|
||||||
export const remove = fn(SessionID.zod, (id) => runPromise((svc) => svc.remove(id)))
|
|
||||||
export async function updateMessage<T extends MessageV2.Info>(msg: T): Promise<T> {
|
|
||||||
MessageV2.Info.parse(msg)
|
|
||||||
return runPromise((svc) => svc.updateMessage(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const removeMessage = fn(z.object({ sessionID: SessionID.zod, messageID: MessageID.zod }), (input) =>
|
|
||||||
runPromise((svc) => svc.removeMessage(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const removePart = fn(
|
|
||||||
z.object({ sessionID: SessionID.zod, messageID: MessageID.zod, partID: PartID.zod }),
|
|
||||||
(input) => runPromise((svc) => svc.removePart(input)),
|
|
||||||
)
|
|
||||||
|
|
||||||
export async function updatePart<T extends MessageV2.Part>(part: T): Promise<T> {
|
|
||||||
MessageV2.Part.parse(part)
|
|
||||||
return runPromise((svc) => svc.updatePart(part))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { ShareNext } from "./share-next"
|
||||||
|
|
||||||
export namespace SessionShare {
|
export namespace SessionShare {
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
readonly create: (input?: Parameters<typeof Session.create>[0]) => Effect.Effect<Session.Info>
|
readonly create: (input?: Session.CreateInput) => Effect.Effect<Session.Info>
|
||||||
readonly share: (sessionID: SessionID) => Effect.Effect<{ url: string }, unknown>
|
readonly share: (sessionID: SessionID) => Effect.Effect<{ url: string }, unknown>
|
||||||
readonly unshare: (sessionID: SessionID) => Effect.Effect<void, unknown>
|
readonly unshare: (sessionID: SessionID) => Effect.Effect<void, unknown>
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ export namespace SessionShare {
|
||||||
yield* Effect.sync(() => SyncEvent.run(Session.Event.Updated, { sessionID, info: { share: { url: null } } }))
|
yield* Effect.sync(() => SyncEvent.run(Session.Event.Updated, { sessionID, info: { share: { url: null } } }))
|
||||||
})
|
})
|
||||||
|
|
||||||
const create = Effect.fn("SessionShare.create")(function* (input?: Parameters<typeof Session.create>[0]) {
|
const create = Effect.fn("SessionShare.create")(function* (input?: Session.CreateInput) {
|
||||||
const result = yield* session.create(input)
|
const result = yield* session.create(input)
|
||||||
if (result.parentID) return result
|
if (result.parentID) return result
|
||||||
const conf = yield* cfg.get()
|
const conf = yield* cfg.get()
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,43 @@
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { Effect } from "effect"
|
||||||
|
import z from "zod"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Project } from "../../src/project/project"
|
import { Project } from "../../src/project/project"
|
||||||
import { Session } from "../../src/session"
|
import { Session as SessionNs } from "../../src/session"
|
||||||
import { Log } from "../../src/util/log"
|
import { Log } from "../../src/util/log"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
describe("Session.listGlobal", () => {
|
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
||||||
|
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const svc = {
|
||||||
|
...SessionNs,
|
||||||
|
create(input?: SessionNs.CreateInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
},
|
||||||
|
setArchived(input: z.output<typeof SessionNs.SetArchivedInput>) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.setArchived(input)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("session.listGlobal", () => {
|
||||||
test("lists sessions across projects with project metadata", async () => {
|
test("lists sessions across projects with project metadata", async () => {
|
||||||
await using first = await tmpdir({ git: true })
|
await using first = await tmpdir({ git: true })
|
||||||
await using second = await tmpdir({ git: true })
|
await using second = await tmpdir({ git: true })
|
||||||
|
|
||||||
const firstSession = await Instance.provide({
|
const firstSession = await Instance.provide({
|
||||||
directory: first.path,
|
directory: first.path,
|
||||||
fn: async () => Session.create({ title: "first-session" }),
|
fn: async () => svc.create({ title: "first-session" }),
|
||||||
})
|
})
|
||||||
const secondSession = await Instance.provide({
|
const secondSession = await Instance.provide({
|
||||||
directory: second.path,
|
directory: second.path,
|
||||||
fn: async () => Session.create({ title: "second-session" }),
|
fn: async () => svc.create({ title: "second-session" }),
|
||||||
})
|
})
|
||||||
|
|
||||||
const sessions = [...Session.listGlobal({ limit: 200 })]
|
const sessions = [...svc.listGlobal({ limit: 200 })]
|
||||||
const ids = sessions.map((session) => session.id)
|
const ids = sessions.map((session) => session.id)
|
||||||
|
|
||||||
expect(ids).toContain(firstSession.id)
|
expect(ids).toContain(firstSession.id)
|
||||||
|
|
@ -44,20 +60,20 @@ describe("Session.listGlobal", () => {
|
||||||
|
|
||||||
const archived = await Instance.provide({
|
const archived = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => Session.create({ title: "archived-session" }),
|
fn: async () => svc.create({ title: "archived-session" }),
|
||||||
})
|
})
|
||||||
|
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => Session.setArchived({ sessionID: archived.id, time: Date.now() }),
|
fn: async () => svc.setArchived({ sessionID: archived.id, time: Date.now() }),
|
||||||
})
|
})
|
||||||
|
|
||||||
const sessions = [...Session.listGlobal({ limit: 200 })]
|
const sessions = [...svc.listGlobal({ limit: 200 })]
|
||||||
const ids = sessions.map((session) => session.id)
|
const ids = sessions.map((session) => session.id)
|
||||||
|
|
||||||
expect(ids).not.toContain(archived.id)
|
expect(ids).not.toContain(archived.id)
|
||||||
|
|
||||||
const allSessions = [...Session.listGlobal({ limit: 200, archived: true })]
|
const allSessions = [...svc.listGlobal({ limit: 200, archived: true })]
|
||||||
const allIds = allSessions.map((session) => session.id)
|
const allIds = allSessions.map((session) => session.id)
|
||||||
|
|
||||||
expect(allIds).toContain(archived.id)
|
expect(allIds).toContain(archived.id)
|
||||||
|
|
@ -68,19 +84,19 @@ describe("Session.listGlobal", () => {
|
||||||
|
|
||||||
const first = await Instance.provide({
|
const first = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => Session.create({ title: "page-one" }),
|
fn: async () => svc.create({ title: "page-one" }),
|
||||||
})
|
})
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5))
|
await new Promise((resolve) => setTimeout(resolve, 5))
|
||||||
const second = await Instance.provide({
|
const second = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => Session.create({ title: "page-two" }),
|
fn: async () => svc.create({ title: "page-two" }),
|
||||||
})
|
})
|
||||||
|
|
||||||
const page = [...Session.listGlobal({ directory: tmp.path, limit: 1 })]
|
const page = [...svc.listGlobal({ directory: tmp.path, limit: 1 })]
|
||||||
expect(page.length).toBe(1)
|
expect(page.length).toBe(1)
|
||||||
expect(page[0].id).toBe(second.id)
|
expect(page[0].id).toBe(second.id)
|
||||||
|
|
||||||
const next = [...Session.listGlobal({ directory: tmp.path, limit: 10, cursor: page[0].time.updated })]
|
const next = [...svc.listGlobal({ directory: tmp.path, limit: 10, cursor: page[0].time.updated })]
|
||||||
const ids = next.map((session) => session.id)
|
const ids = next.map((session) => session.id)
|
||||||
|
|
||||||
expect(ids).toContain(first.id)
|
expect(ids).toContain(first.id)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,28 @@
|
||||||
import { afterEach, describe, expect, mock, test } from "bun:test"
|
import { afterEach, describe, expect, mock, test } from "bun:test"
|
||||||
|
import { Effect } from "effect"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Server } from "../../src/server/server"
|
import { Server } from "../../src/server/server"
|
||||||
import { Session } from "../../src/session"
|
import { Session as SessionNs } from "../../src/session"
|
||||||
|
import type { SessionID } from "../../src/session/schema"
|
||||||
import { Log } from "../../src/util/log"
|
import { Log } from "../../src/util/log"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
||||||
|
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const svc = {
|
||||||
|
...SessionNs,
|
||||||
|
create(input?: SessionNs.CreateInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
},
|
||||||
|
remove(id: SessionID) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.remove(id)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
mock.restore()
|
mock.restore()
|
||||||
await Instance.disposeAll()
|
await Instance.disposeAll()
|
||||||
|
|
@ -18,7 +34,7 @@ describe("session action routes", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const app = Server.Default().app
|
const app = Server.Default().app
|
||||||
|
|
||||||
const res = await app.request(`/session/${session.id}/abort`, { method: "POST" })
|
const res = await app.request(`/session/${session.id}/abort`, { method: "POST" })
|
||||||
|
|
@ -26,7 +42,7 @@ describe("session action routes", () => {
|
||||||
expect(res.status).toBe(200)
|
expect(res.status).toBe(200)
|
||||||
expect(await res.json()).toBe(true)
|
expect(await res.json()).toBe(true)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,42 @@
|
||||||
import { afterEach, describe, expect, test } from "bun:test"
|
import { afterEach, describe, expect, test } from "bun:test"
|
||||||
|
import { Effect } from "effect"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Session } from "../../src/session"
|
import { Session as SessionNs } from "../../src/session"
|
||||||
import { Log } from "../../src/util/log"
|
import { Log } from "../../src/util/log"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
||||||
|
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const svc = {
|
||||||
|
...SessionNs,
|
||||||
|
create(input?: SessionNs.CreateInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await Instance.disposeAll()
|
await Instance.disposeAll()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("Session.list", () => {
|
describe("session.list", () => {
|
||||||
test("filters by directory", async () => {
|
test("filters by directory", async () => {
|
||||||
await using tmp = await tmpdir({ git: true })
|
await using tmp = await tmpdir({ git: true })
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const first = await Session.create({})
|
const first = await svc.create({})
|
||||||
|
|
||||||
await using other = await tmpdir({ git: true })
|
await using other = await tmpdir({ git: true })
|
||||||
const second = await Instance.provide({
|
const second = await Instance.provide({
|
||||||
directory: other.path,
|
directory: other.path,
|
||||||
fn: async () => Session.create({}),
|
fn: async () => svc.create({}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const sessions = [...Session.list({ directory: tmp.path })]
|
const sessions = [...svc.list({ directory: tmp.path })]
|
||||||
const ids = sessions.map((s) => s.id)
|
const ids = sessions.map((s) => s.id)
|
||||||
|
|
||||||
expect(ids).toContain(first.id)
|
expect(ids).toContain(first.id)
|
||||||
|
|
@ -38,10 +50,10 @@ describe("Session.list", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const root = await Session.create({ title: "root-session" })
|
const root = await svc.create({ title: "root-session" })
|
||||||
const child = await Session.create({ title: "child-session", parentID: root.id })
|
const child = await svc.create({ title: "child-session", parentID: root.id })
|
||||||
|
|
||||||
const sessions = [...Session.list({ roots: true })]
|
const sessions = [...svc.list({ roots: true })]
|
||||||
const ids = sessions.map((s) => s.id)
|
const ids = sessions.map((s) => s.id)
|
||||||
|
|
||||||
expect(ids).toContain(root.id)
|
expect(ids).toContain(root.id)
|
||||||
|
|
@ -55,10 +67,10 @@ describe("Session.list", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({ title: "new-session" })
|
const session = await svc.create({ title: "new-session" })
|
||||||
const futureStart = Date.now() + 86400000
|
const futureStart = Date.now() + 86400000
|
||||||
|
|
||||||
const sessions = [...Session.list({ start: futureStart })]
|
const sessions = [...svc.list({ start: futureStart })]
|
||||||
expect(sessions.length).toBe(0)
|
expect(sessions.length).toBe(0)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -69,10 +81,10 @@ describe("Session.list", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
await Session.create({ title: "unique-search-term-abc" })
|
await svc.create({ title: "unique-search-term-abc" })
|
||||||
await Session.create({ title: "other-session-xyz" })
|
await svc.create({ title: "other-session-xyz" })
|
||||||
|
|
||||||
const sessions = [...Session.list({ search: "unique-search" })]
|
const sessions = [...svc.list({ search: "unique-search" })]
|
||||||
const titles = sessions.map((s) => s.title)
|
const titles = sessions.map((s) => s.title)
|
||||||
|
|
||||||
expect(titles).toContain("unique-search-term-abc")
|
expect(titles).toContain("unique-search-term-abc")
|
||||||
|
|
@ -86,11 +98,11 @@ describe("Session.list", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
await Session.create({ title: "session-1" })
|
await svc.create({ title: "session-1" })
|
||||||
await Session.create({ title: "session-2" })
|
await svc.create({ title: "session-2" })
|
||||||
await Session.create({ title: "session-3" })
|
await svc.create({ title: "session-3" })
|
||||||
|
|
||||||
const sessions = [...Session.list({ limit: 2 })]
|
const sessions = [...svc.list({ limit: 2 })]
|
||||||
expect(sessions.length).toBe(2)
|
expect(sessions.length).toBe(2)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { afterEach, describe, expect, test } from "bun:test"
|
import { afterEach, describe, expect, test } from "bun:test"
|
||||||
|
import { Effect } from "effect"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Server } from "../../src/server/server"
|
import { Server } from "../../src/server/server"
|
||||||
import { Session } from "../../src/session"
|
import { Session as SessionNs } from "../../src/session"
|
||||||
import { MessageV2 } from "../../src/session/message-v2"
|
import { MessageV2 } from "../../src/session/message-v2"
|
||||||
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
||||||
import { Log } from "../../src/util/log"
|
import { Log } from "../../src/util/log"
|
||||||
|
|
@ -9,6 +10,26 @@ import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
||||||
|
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const svc = {
|
||||||
|
...SessionNs,
|
||||||
|
create(input?: SessionNs.CreateInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
},
|
||||||
|
remove(id: SessionID) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.remove(id)))
|
||||||
|
},
|
||||||
|
updateMessage<T extends MessageV2.Info>(msg: T) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.updateMessage(msg)))
|
||||||
|
},
|
||||||
|
updatePart<T extends MessageV2.Part>(part: T) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.updatePart(part)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await Instance.disposeAll()
|
await Instance.disposeAll()
|
||||||
})
|
})
|
||||||
|
|
@ -30,7 +51,7 @@ async function fill(sessionID: SessionID, count: number, time = (i: number) => D
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const id = MessageID.ascending()
|
const id = MessageID.ascending()
|
||||||
ids.push(id)
|
ids.push(id)
|
||||||
await Session.updateMessage({
|
await svc.updateMessage({
|
||||||
id,
|
id,
|
||||||
sessionID,
|
sessionID,
|
||||||
role: "user",
|
role: "user",
|
||||||
|
|
@ -40,7 +61,7 @@ async function fill(sessionID: SessionID, count: number, time = (i: number) => D
|
||||||
tools: {},
|
tools: {},
|
||||||
mode: "",
|
mode: "",
|
||||||
} as unknown as MessageV2.Info)
|
} as unknown as MessageV2.Info)
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID,
|
sessionID,
|
||||||
messageID: id,
|
messageID: id,
|
||||||
|
|
@ -58,7 +79,7 @@ describe("session messages endpoint", () => {
|
||||||
Instance.provide({
|
Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 5)
|
const ids = await fill(session.id, 5)
|
||||||
const app = Server.Default().app
|
const app = Server.Default().app
|
||||||
|
|
||||||
|
|
@ -75,7 +96,7 @@ describe("session messages endpoint", () => {
|
||||||
const bBody = (await b.json()) as MessageV2.WithParts[]
|
const bBody = (await b.json()) as MessageV2.WithParts[]
|
||||||
expect(bBody.map((item) => item.info.id)).toEqual(ids.slice(-4, -2))
|
expect(bBody.map((item) => item.info.id)).toEqual(ids.slice(-4, -2))
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -87,7 +108,7 @@ describe("session messages endpoint", () => {
|
||||||
Instance.provide({
|
Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 3)
|
const ids = await fill(session.id, 3)
|
||||||
const app = Server.Default().app
|
const app = Server.Default().app
|
||||||
|
|
||||||
|
|
@ -96,7 +117,7 @@ describe("session messages endpoint", () => {
|
||||||
const body = (await res.json()) as MessageV2.WithParts[]
|
const body = (await res.json()) as MessageV2.WithParts[]
|
||||||
expect(body.map((item) => item.info.id)).toEqual(ids)
|
expect(body.map((item) => item.info.id)).toEqual(ids)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -108,7 +129,7 @@ describe("session messages endpoint", () => {
|
||||||
Instance.provide({
|
Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const app = Server.Default().app
|
const app = Server.Default().app
|
||||||
|
|
||||||
const bad = await app.request(`/session/${session.id}/message?limit=2&before=bad`)
|
const bad = await app.request(`/session/${session.id}/message?limit=2&before=bad`)
|
||||||
|
|
@ -117,7 +138,7 @@ describe("session messages endpoint", () => {
|
||||||
const miss = await app.request(`/session/ses_missing/message?limit=2`)
|
const miss = await app.request(`/session/ses_missing/message?limit=2`)
|
||||||
expect(miss.status).toBe(404)
|
expect(miss.status).toBe(404)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -129,7 +150,7 @@ describe("session messages endpoint", () => {
|
||||||
Instance.provide({
|
Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await fill(session.id, 520)
|
await fill(session.id, 520)
|
||||||
const app = Server.Default().app
|
const app = Server.Default().app
|
||||||
|
|
||||||
|
|
@ -138,7 +159,7 @@ describe("session messages endpoint", () => {
|
||||||
const body = (await res.json()) as MessageV2.WithParts[]
|
const body = (await res.json()) as MessageV2.WithParts[]
|
||||||
expect(body).toHaveLength(510)
|
expect(body).toHaveLength(510)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { afterEach, describe, expect, test } from "bun:test"
|
import { afterEach, describe, expect, test } from "bun:test"
|
||||||
import { Session } from "../../src/session"
|
import { Effect } from "effect"
|
||||||
|
import { Session as SessionNs } from "../../src/session"
|
||||||
|
import type { SessionID } from "../../src/session/schema"
|
||||||
import { Log } from "../../src/util/log"
|
import { Log } from "../../src/util/log"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Server } from "../../src/server/server"
|
import { Server } from "../../src/server/server"
|
||||||
|
|
@ -7,6 +9,20 @@ import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
||||||
|
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const svc = {
|
||||||
|
...SessionNs,
|
||||||
|
create(input?: SessionNs.CreateInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
},
|
||||||
|
remove(id: SessionID) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.remove(id)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await Instance.disposeAll()
|
await Instance.disposeAll()
|
||||||
})
|
})
|
||||||
|
|
@ -18,7 +34,7 @@ describe("tui.selectSession endpoint", () => {
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
// #given
|
// #given
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
// #when
|
// #when
|
||||||
const app = Server.Default().app
|
const app = Server.Default().app
|
||||||
|
|
@ -33,7 +49,7 @@ describe("tui.selectSession endpoint", () => {
|
||||||
const body = await response.json()
|
const body = await response.json()
|
||||||
expect(body).toBe(true)
|
expect(body).toBe(true)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { APICallError } from "ai"
|
||||||
import { Cause, Effect, Exit, Layer, ManagedRuntime } from "effect"
|
import { Cause, Effect, Exit, Layer, ManagedRuntime } from "effect"
|
||||||
import * as Stream from "effect/Stream"
|
import * as Stream from "effect/Stream"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
import z from "zod"
|
||||||
import { Bus } from "../../src/bus"
|
import { Bus } from "../../src/bus"
|
||||||
import { Config } from "../../src/config/config"
|
import { Config } from "../../src/config/config"
|
||||||
import { Agent } from "../../src/agent/agent"
|
import { Agent } from "../../src/agent/agent"
|
||||||
|
|
@ -14,7 +15,7 @@ import { Log } from "../../src/util/log"
|
||||||
import { Permission } from "../../src/permission"
|
import { Permission } from "../../src/permission"
|
||||||
import { Plugin } from "../../src/plugin"
|
import { Plugin } from "../../src/plugin"
|
||||||
import { provideTmpdirInstance, tmpdir } from "../fixture/fixture"
|
import { provideTmpdirInstance, tmpdir } from "../fixture/fixture"
|
||||||
import { Session } from "../../src/session"
|
import { Session as SessionNs } from "../../src/session"
|
||||||
import { MessageV2 } from "../../src/session/message-v2"
|
import { MessageV2 } from "../../src/session/message-v2"
|
||||||
import { MessageID, PartID, SessionID } from "../../src/session/schema"
|
import { MessageID, PartID, SessionID } from "../../src/session/schema"
|
||||||
import { SessionStatus } from "../../src/session/status"
|
import { SessionStatus } from "../../src/session/status"
|
||||||
|
|
@ -29,6 +30,26 @@ import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||||
|
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
||||||
|
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const svc = {
|
||||||
|
...SessionNs,
|
||||||
|
create(input?: SessionNs.CreateInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
},
|
||||||
|
messages(input: z.output<typeof SessionNs.MessagesInput>) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.messages(input)))
|
||||||
|
},
|
||||||
|
updateMessage<T extends MessageV2.Info>(msg: T) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.updateMessage(msg)))
|
||||||
|
},
|
||||||
|
updatePart<T extends MessageV2.Part>(part: T) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.updatePart(part)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const summary = Layer.succeed(
|
const summary = Layer.succeed(
|
||||||
SessionSummary.Service,
|
SessionSummary.Service,
|
||||||
SessionSummary.Service.of({
|
SessionSummary.Service.of({
|
||||||
|
|
@ -80,7 +101,7 @@ function createModel(opts: {
|
||||||
const wide = () => ProviderTest.fake({ model: createModel({ context: 100_000, output: 32_000 }) })
|
const wide = () => ProviderTest.fake({ model: createModel({ context: 100_000, output: 32_000 }) })
|
||||||
|
|
||||||
async function user(sessionID: SessionID, text: string) {
|
async function user(sessionID: SessionID, text: string) {
|
||||||
const msg = await Session.updateMessage({
|
const msg = await svc.updateMessage({
|
||||||
id: MessageID.ascending(),
|
id: MessageID.ascending(),
|
||||||
role: "user",
|
role: "user",
|
||||||
sessionID,
|
sessionID,
|
||||||
|
|
@ -88,7 +109,7 @@ async function user(sessionID: SessionID, text: string) {
|
||||||
model: ref,
|
model: ref,
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
})
|
})
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: msg.id,
|
messageID: msg.id,
|
||||||
sessionID,
|
sessionID,
|
||||||
|
|
@ -119,12 +140,12 @@ async function assistant(sessionID: SessionID, parentID: MessageID, root: string
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
finish: "end_turn",
|
finish: "end_turn",
|
||||||
}
|
}
|
||||||
await Session.updateMessage(msg)
|
await svc.updateMessage(msg)
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tool(sessionID: SessionID, messageID: MessageID, tool: string, output: string) {
|
async function tool(sessionID: SessionID, messageID: MessageID, tool: string, output: string) {
|
||||||
return Session.updatePart({
|
return svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID,
|
messageID,
|
||||||
sessionID,
|
sessionID,
|
||||||
|
|
@ -171,7 +192,7 @@ function runtime(result: "continue" | "compact", plugin = Plugin.defaultLayer, p
|
||||||
return ManagedRuntime.make(
|
return ManagedRuntime.make(
|
||||||
Layer.mergeAll(SessionCompaction.layer, bus).pipe(
|
Layer.mergeAll(SessionCompaction.layer, bus).pipe(
|
||||||
Layer.provide(provider.layer),
|
Layer.provide(provider.layer),
|
||||||
Layer.provide(Session.defaultLayer),
|
Layer.provide(SessionNs.defaultLayer),
|
||||||
Layer.provide(layer(result)),
|
Layer.provide(layer(result)),
|
||||||
Layer.provide(Agent.defaultLayer),
|
Layer.provide(Agent.defaultLayer),
|
||||||
Layer.provide(plugin),
|
Layer.provide(plugin),
|
||||||
|
|
@ -191,9 +212,9 @@ const deps = Layer.mergeAll(
|
||||||
)
|
)
|
||||||
|
|
||||||
const env = Layer.mergeAll(
|
const env = Layer.mergeAll(
|
||||||
Session.defaultLayer,
|
SessionNs.defaultLayer,
|
||||||
CrossSpawnSpawner.defaultLayer,
|
CrossSpawnSpawner.defaultLayer,
|
||||||
SessionCompaction.layer.pipe(Layer.provide(Session.defaultLayer), Layer.provideMerge(deps)),
|
SessionCompaction.layer.pipe(Layer.provide(SessionNs.defaultLayer), Layer.provideMerge(deps)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const it = testEffect(env)
|
const it = testEffect(env)
|
||||||
|
|
@ -227,7 +248,7 @@ function liveRuntime(layer: Layer.Layer<LLM.Service>, provider = ProviderTest.fa
|
||||||
return ManagedRuntime.make(
|
return ManagedRuntime.make(
|
||||||
Layer.mergeAll(SessionCompaction.layer.pipe(Layer.provide(processor)), processor, bus, status).pipe(
|
Layer.mergeAll(SessionCompaction.layer.pipe(Layer.provide(processor)), processor, bus, status).pipe(
|
||||||
Layer.provide(provider.layer),
|
Layer.provide(provider.layer),
|
||||||
Layer.provide(Session.defaultLayer),
|
Layer.provide(SessionNs.defaultLayer),
|
||||||
Layer.provide(Snapshot.defaultLayer),
|
Layer.provide(Snapshot.defaultLayer),
|
||||||
Layer.provide(layer),
|
Layer.provide(layer),
|
||||||
Layer.provide(Permission.defaultLayer),
|
Layer.provide(Permission.defaultLayer),
|
||||||
|
|
@ -467,9 +488,9 @@ describe("session.compaction.create", () => {
|
||||||
provideTmpdirInstance(() =>
|
provideTmpdirInstance(() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const compact = yield* SessionCompaction.Service
|
const compact = yield* SessionCompaction.Service
|
||||||
const session = yield* Session.Service
|
const ssn = yield* SessionNs.Service
|
||||||
|
|
||||||
const info = yield* session.create({})
|
const info = yield* ssn.create({})
|
||||||
|
|
||||||
yield* compact.create({
|
yield* compact.create({
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -479,7 +500,7 @@ describe("session.compaction.create", () => {
|
||||||
overflow: true,
|
overflow: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const msgs = yield* session.messages({ sessionID: info.id })
|
const msgs = yield* ssn.messages({ sessionID: info.id })
|
||||||
expect(msgs).toHaveLength(1)
|
expect(msgs).toHaveLength(1)
|
||||||
expect(msgs[0].info.role).toBe("user")
|
expect(msgs[0].info.role).toBe("user")
|
||||||
expect(msgs[0].parts).toHaveLength(1)
|
expect(msgs[0].parts).toHaveLength(1)
|
||||||
|
|
@ -499,9 +520,9 @@ describe("session.compaction.prune", () => {
|
||||||
provideTmpdirInstance((dir) =>
|
provideTmpdirInstance((dir) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const compact = yield* SessionCompaction.Service
|
const compact = yield* SessionCompaction.Service
|
||||||
const session = yield* Session.Service
|
const ssn = yield* SessionNs.Service
|
||||||
const info = yield* session.create({})
|
const info = yield* ssn.create({})
|
||||||
const a = yield* session.updateMessage({
|
const a = yield* ssn.updateMessage({
|
||||||
id: MessageID.ascending(),
|
id: MessageID.ascending(),
|
||||||
role: "user",
|
role: "user",
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -509,7 +530,7 @@ describe("session.compaction.prune", () => {
|
||||||
model: ref,
|
model: ref,
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
})
|
})
|
||||||
yield* session.updatePart({
|
yield* ssn.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: a.id,
|
messageID: a.id,
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -536,8 +557,8 @@ describe("session.compaction.prune", () => {
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
finish: "end_turn",
|
finish: "end_turn",
|
||||||
}
|
}
|
||||||
yield* session.updateMessage(b)
|
yield* ssn.updateMessage(b)
|
||||||
yield* session.updatePart({
|
yield* ssn.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: b.id,
|
messageID: b.id,
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -554,7 +575,7 @@ describe("session.compaction.prune", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
for (const text of ["second", "third"]) {
|
for (const text of ["second", "third"]) {
|
||||||
const msg = yield* session.updateMessage({
|
const msg = yield* ssn.updateMessage({
|
||||||
id: MessageID.ascending(),
|
id: MessageID.ascending(),
|
||||||
role: "user",
|
role: "user",
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -562,7 +583,7 @@ describe("session.compaction.prune", () => {
|
||||||
model: ref,
|
model: ref,
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
})
|
})
|
||||||
yield* session.updatePart({
|
yield* ssn.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: msg.id,
|
messageID: msg.id,
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -573,7 +594,7 @@ describe("session.compaction.prune", () => {
|
||||||
|
|
||||||
yield* compact.prune({ sessionID: info.id })
|
yield* compact.prune({ sessionID: info.id })
|
||||||
|
|
||||||
const msgs = yield* session.messages({ sessionID: info.id })
|
const msgs = yield* ssn.messages({ sessionID: info.id })
|
||||||
const part = msgs.flatMap((msg) => msg.parts).find((part) => part.type === "tool")
|
const part = msgs.flatMap((msg) => msg.parts).find((part) => part.type === "tool")
|
||||||
expect(part?.type).toBe("tool")
|
expect(part?.type).toBe("tool")
|
||||||
expect(part?.state.status).toBe("completed")
|
expect(part?.state.status).toBe("completed")
|
||||||
|
|
@ -589,9 +610,9 @@ describe("session.compaction.prune", () => {
|
||||||
provideTmpdirInstance((dir) =>
|
provideTmpdirInstance((dir) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const compact = yield* SessionCompaction.Service
|
const compact = yield* SessionCompaction.Service
|
||||||
const session = yield* Session.Service
|
const ssn = yield* SessionNs.Service
|
||||||
const info = yield* session.create({})
|
const info = yield* ssn.create({})
|
||||||
const a = yield* session.updateMessage({
|
const a = yield* ssn.updateMessage({
|
||||||
id: MessageID.ascending(),
|
id: MessageID.ascending(),
|
||||||
role: "user",
|
role: "user",
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -599,7 +620,7 @@ describe("session.compaction.prune", () => {
|
||||||
model: ref,
|
model: ref,
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
})
|
})
|
||||||
yield* session.updatePart({
|
yield* ssn.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: a.id,
|
messageID: a.id,
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -626,8 +647,8 @@ describe("session.compaction.prune", () => {
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
finish: "end_turn",
|
finish: "end_turn",
|
||||||
}
|
}
|
||||||
yield* session.updateMessage(b)
|
yield* ssn.updateMessage(b)
|
||||||
yield* session.updatePart({
|
yield* ssn.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: b.id,
|
messageID: b.id,
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -644,7 +665,7 @@ describe("session.compaction.prune", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
for (const text of ["second", "third"]) {
|
for (const text of ["second", "third"]) {
|
||||||
const msg = yield* session.updateMessage({
|
const msg = yield* ssn.updateMessage({
|
||||||
id: MessageID.ascending(),
|
id: MessageID.ascending(),
|
||||||
role: "user",
|
role: "user",
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -652,7 +673,7 @@ describe("session.compaction.prune", () => {
|
||||||
model: ref,
|
model: ref,
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
})
|
})
|
||||||
yield* session.updatePart({
|
yield* ssn.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: msg.id,
|
messageID: msg.id,
|
||||||
sessionID: info.id,
|
sessionID: info.id,
|
||||||
|
|
@ -663,7 +684,7 @@ describe("session.compaction.prune", () => {
|
||||||
|
|
||||||
yield* compact.prune({ sessionID: info.id })
|
yield* compact.prune({ sessionID: info.id })
|
||||||
|
|
||||||
const msgs = yield* session.messages({ sessionID: info.id })
|
const msgs = yield* ssn.messages({ sessionID: info.id })
|
||||||
const part = msgs.flatMap((msg) => msg.parts).find((part) => part.type === "tool")
|
const part = msgs.flatMap((msg) => msg.parts).find((part) => part.type === "tool")
|
||||||
expect(part?.type).toBe("tool")
|
expect(part?.type).toBe("tool")
|
||||||
if (part?.type === "tool" && part.state.status === "completed") {
|
if (part?.type === "tool" && part.state.status === "completed") {
|
||||||
|
|
@ -680,12 +701,12 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const reply = await assistant(session.id, msg.id, tmp.path)
|
const reply = await assistant(session.id, msg.id, tmp.path)
|
||||||
const rt = runtime("continue")
|
const rt = runtime("continue")
|
||||||
try {
|
try {
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
await expect(
|
await expect(
|
||||||
rt.runPromise(
|
rt.runPromise(
|
||||||
SessionCompaction.Service.use((svc) =>
|
SessionCompaction.Service.use((svc) =>
|
||||||
|
|
@ -710,9 +731,9 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const done = defer()
|
const done = defer()
|
||||||
let seen = false
|
let seen = false
|
||||||
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
||||||
|
|
@ -760,11 +781,11 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const rt = runtime("compact", Plugin.defaultLayer, wide())
|
const rt = runtime("compact", Plugin.defaultLayer, wide())
|
||||||
try {
|
try {
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const result = await rt.runPromise(
|
const result = await rt.runPromise(
|
||||||
SessionCompaction.Service.use((svc) =>
|
SessionCompaction.Service.use((svc) =>
|
||||||
svc.process({
|
svc.process({
|
||||||
|
|
@ -776,7 +797,7 @@ describe("session.compaction.process", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const summary = (await Session.messages({ sessionID: session.id })).find(
|
const summary = (await svc.messages({ sessionID: session.id })).find(
|
||||||
(msg) => msg.info.role === "assistant" && msg.info.summary,
|
(msg) => msg.info.role === "assistant" && msg.info.summary,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -798,11 +819,11 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
||||||
try {
|
try {
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const result = await rt.runPromise(
|
const result = await rt.runPromise(
|
||||||
SessionCompaction.Service.use((svc) =>
|
SessionCompaction.Service.use((svc) =>
|
||||||
svc.process({
|
svc.process({
|
||||||
|
|
@ -814,7 +835,7 @@ describe("session.compaction.process", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const all = await Session.messages({ sessionID: session.id })
|
const all = await svc.messages({ sessionID: session.id })
|
||||||
const last = all.at(-1)
|
const last = all.at(-1)
|
||||||
|
|
||||||
expect(result).toBe("continue")
|
expect(result).toBe("continue")
|
||||||
|
|
@ -838,11 +859,11 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const rt = runtime("continue", autocontinue(false), wide())
|
const rt = runtime("continue", autocontinue(false), wide())
|
||||||
try {
|
try {
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const result = await rt.runPromise(
|
const result = await rt.runPromise(
|
||||||
SessionCompaction.Service.use((svc) =>
|
SessionCompaction.Service.use((svc) =>
|
||||||
svc.process({
|
svc.process({
|
||||||
|
|
@ -854,7 +875,7 @@ describe("session.compaction.process", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const all = await Session.messages({ sessionID: session.id })
|
const all = await svc.messages({ sessionID: session.id })
|
||||||
const last = all.at(-1)
|
const last = all.at(-1)
|
||||||
|
|
||||||
expect(result).toBe("continue")
|
expect(result).toBe("continue")
|
||||||
|
|
@ -881,10 +902,10 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await user(session.id, "root")
|
await user(session.id, "root")
|
||||||
const replay = await user(session.id, "image")
|
const replay = await user(session.id, "image")
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: replay.id,
|
messageID: replay.id,
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
|
|
@ -896,7 +917,7 @@ describe("session.compaction.process", () => {
|
||||||
const msg = await user(session.id, "current")
|
const msg = await user(session.id, "current")
|
||||||
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
||||||
try {
|
try {
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const result = await rt.runPromise(
|
const result = await rt.runPromise(
|
||||||
SessionCompaction.Service.use((svc) =>
|
SessionCompaction.Service.use((svc) =>
|
||||||
svc.process({
|
svc.process({
|
||||||
|
|
@ -909,7 +930,7 @@ describe("session.compaction.process", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const last = (await Session.messages({ sessionID: session.id })).at(-1)
|
const last = (await svc.messages({ sessionID: session.id })).at(-1)
|
||||||
|
|
||||||
expect(result).toBe("continue")
|
expect(result).toBe("continue")
|
||||||
expect(last?.info.role).toBe("user")
|
expect(last?.info.role).toBe("user")
|
||||||
|
|
@ -929,13 +950,13 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await user(session.id, "earlier")
|
await user(session.id, "earlier")
|
||||||
const msg = await user(session.id, "current")
|
const msg = await user(session.id, "current")
|
||||||
|
|
||||||
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
const rt = runtime("continue", Plugin.defaultLayer, wide())
|
||||||
try {
|
try {
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const result = await rt.runPromise(
|
const result = await rt.runPromise(
|
||||||
SessionCompaction.Service.use((svc) =>
|
SessionCompaction.Service.use((svc) =>
|
||||||
svc.process({
|
svc.process({
|
||||||
|
|
@ -948,7 +969,7 @@ describe("session.compaction.process", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const last = (await Session.messages({ sessionID: session.id })).at(-1)
|
const last = (await svc.messages({ sessionID: session.id })).at(-1)
|
||||||
|
|
||||||
expect(result).toBe("continue")
|
expect(result).toBe("continue")
|
||||||
expect(last?.info.role).toBe("user")
|
expect(last?.info.role).toBe("user")
|
||||||
|
|
@ -989,9 +1010,9 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const abort = new AbortController()
|
const abort = new AbortController()
|
||||||
const rt = liveRuntime(stub.layer, wide())
|
const rt = liveRuntime(stub.layer, wide())
|
||||||
let off: (() => void) | undefined
|
let off: (() => void) | undefined
|
||||||
|
|
@ -1063,9 +1084,9 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
const abort = new AbortController()
|
const abort = new AbortController()
|
||||||
const rt = runtime("continue", plugin(ready), wide())
|
const rt = runtime("continue", plugin(ready), wide())
|
||||||
let run: Promise<"continue" | "stop"> | undefined
|
let run: Promise<"continue" | "stop"> | undefined
|
||||||
|
|
@ -1100,7 +1121,7 @@ describe("session.compaction.process", () => {
|
||||||
abort.abort()
|
abort.abort()
|
||||||
expect(await run).toBe("stop")
|
expect(await run).toBe("stop")
|
||||||
|
|
||||||
const all = await Session.messages({ sessionID: session.id })
|
const all = await svc.messages({ sessionID: session.id })
|
||||||
expect(all.some((msg) => msg.info.role === "assistant" && msg.info.summary)).toBe(false)
|
expect(all.some((msg) => msg.info.role === "assistant" && msg.info.summary)).toBe(false)
|
||||||
} finally {
|
} finally {
|
||||||
abort.abort()
|
abort.abort()
|
||||||
|
|
@ -1165,11 +1186,11 @@ describe("session.compaction.process", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const msg = await user(session.id, "hello")
|
const msg = await user(session.id, "hello")
|
||||||
const rt = liveRuntime(stub.layer, wide())
|
const rt = liveRuntime(stub.layer, wide())
|
||||||
try {
|
try {
|
||||||
const msgs = await Session.messages({ sessionID: session.id })
|
const msgs = await svc.messages({ sessionID: session.id })
|
||||||
await rt.runPromise(
|
await rt.runPromise(
|
||||||
SessionCompaction.Service.use((svc) =>
|
SessionCompaction.Service.use((svc) =>
|
||||||
svc.process({
|
svc.process({
|
||||||
|
|
@ -1181,7 +1202,7 @@ describe("session.compaction.process", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const summary = (await Session.messages({ sessionID: session.id })).find(
|
const summary = (await svc.messages({ sessionID: session.id })).find(
|
||||||
(item) => item.info.role === "assistant" && item.info.summary,
|
(item) => item.info.role === "assistant" && item.info.summary,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1211,10 +1232,10 @@ describe("util.token.estimate", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("session.getUsage", () => {
|
describe("SessionNs.getUsage", () => {
|
||||||
test("normalizes standard usage to token format", () => {
|
test("normalizes standard usage to token format", () => {
|
||||||
const model = createModel({ context: 100_000, output: 32_000 })
|
const model = createModel({ context: 100_000, output: 32_000 })
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 1000,
|
inputTokens: 1000,
|
||||||
|
|
@ -1241,7 +1262,7 @@ describe("session.getUsage", () => {
|
||||||
|
|
||||||
test("extracts cached tokens to cache.read", () => {
|
test("extracts cached tokens to cache.read", () => {
|
||||||
const model = createModel({ context: 100_000, output: 32_000 })
|
const model = createModel({ context: 100_000, output: 32_000 })
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 1000,
|
inputTokens: 1000,
|
||||||
|
|
@ -1265,7 +1286,7 @@ describe("session.getUsage", () => {
|
||||||
|
|
||||||
test("handles anthropic cache write metadata", () => {
|
test("handles anthropic cache write metadata", () => {
|
||||||
const model = createModel({ context: 100_000, output: 32_000 })
|
const model = createModel({ context: 100_000, output: 32_000 })
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 1000,
|
inputTokens: 1000,
|
||||||
|
|
@ -1294,7 +1315,7 @@ describe("session.getUsage", () => {
|
||||||
test("subtracts cached tokens for anthropic provider", () => {
|
test("subtracts cached tokens for anthropic provider", () => {
|
||||||
const model = createModel({ context: 100_000, output: 32_000 })
|
const model = createModel({ context: 100_000, output: 32_000 })
|
||||||
// AI SDK v6 normalizes inputTokens to include cached tokens for all providers
|
// AI SDK v6 normalizes inputTokens to include cached tokens for all providers
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 1000,
|
inputTokens: 1000,
|
||||||
|
|
@ -1321,7 +1342,7 @@ describe("session.getUsage", () => {
|
||||||
|
|
||||||
test("separates reasoning tokens from output tokens", () => {
|
test("separates reasoning tokens from output tokens", () => {
|
||||||
const model = createModel({ context: 100_000, output: 32_000 })
|
const model = createModel({ context: 100_000, output: 32_000 })
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 1000,
|
inputTokens: 1000,
|
||||||
|
|
@ -1355,7 +1376,7 @@ describe("session.getUsage", () => {
|
||||||
cache: { read: 0, write: 0 },
|
cache: { read: 0, write: 0 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 0,
|
inputTokens: 0,
|
||||||
|
|
@ -1380,7 +1401,7 @@ describe("session.getUsage", () => {
|
||||||
|
|
||||||
test("handles undefined optional values gracefully", () => {
|
test("handles undefined optional values gracefully", () => {
|
||||||
const model = createModel({ context: 100_000, output: 32_000 })
|
const model = createModel({ context: 100_000, output: 32_000 })
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 0,
|
inputTokens: 0,
|
||||||
|
|
@ -1416,7 +1437,7 @@ describe("session.getUsage", () => {
|
||||||
cache: { read: 0.3, write: 3.75 },
|
cache: { read: 0.3, write: 3.75 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 1_000_000,
|
inputTokens: 1_000_000,
|
||||||
|
|
@ -1457,7 +1478,7 @@ describe("session.getUsage", () => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if (npm === "@ai-sdk/amazon-bedrock") {
|
if (npm === "@ai-sdk/amazon-bedrock") {
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage,
|
usage,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
@ -1478,7 +1499,7 @@ describe("session.getUsage", () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage,
|
usage,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
@ -1499,7 +1520,7 @@ describe("session.getUsage", () => {
|
||||||
|
|
||||||
test("extracts cache write tokens from vertex metadata key", () => {
|
test("extracts cache write tokens from vertex metadata key", () => {
|
||||||
const model = createModel({ context: 100_000, output: 32_000, npm: "@ai-sdk/google-vertex/anthropic" })
|
const model = createModel({ context: 100_000, output: 32_000, npm: "@ai-sdk/google-vertex/anthropic" })
|
||||||
const result = Session.getUsage({
|
const result = SessionNs.getUsage({
|
||||||
model,
|
model,
|
||||||
usage: {
|
usage: {
|
||||||
inputTokens: 1000,
|
inputTokens: 1000,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { Effect } from "effect"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Session } from "../../src/session"
|
import { Session as SessionNs } from "../../src/session"
|
||||||
import { MessageV2 } from "../../src/session/message-v2"
|
import { MessageV2 } from "../../src/session/message-v2"
|
||||||
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
||||||
import { ModelID, ProviderID } from "../../src/provider/schema"
|
import { ModelID, ProviderID } from "../../src/provider/schema"
|
||||||
|
|
@ -10,12 +11,32 @@ import { Log } from "../../src/util/log"
|
||||||
const root = path.join(__dirname, "../..")
|
const root = path.join(__dirname, "../..")
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
||||||
|
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const svc = {
|
||||||
|
...SessionNs,
|
||||||
|
create(input?: SessionNs.CreateInput) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
},
|
||||||
|
remove(id: SessionID) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.remove(id)))
|
||||||
|
},
|
||||||
|
updateMessage<T extends MessageV2.Info>(msg: T) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.updateMessage(msg)))
|
||||||
|
},
|
||||||
|
updatePart<T extends MessageV2.Part>(part: T) {
|
||||||
|
return run(SessionNs.Service.use((svc) => svc.updatePart(part)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
async function fill(sessionID: SessionID, count: number, time = (i: number) => Date.now() + i) {
|
async function fill(sessionID: SessionID, count: number, time = (i: number) => Date.now() + i) {
|
||||||
const ids = [] as MessageID[]
|
const ids = [] as MessageID[]
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const id = MessageID.ascending()
|
const id = MessageID.ascending()
|
||||||
ids.push(id)
|
ids.push(id)
|
||||||
await Session.updateMessage({
|
await svc.updateMessage({
|
||||||
id,
|
id,
|
||||||
sessionID,
|
sessionID,
|
||||||
role: "user",
|
role: "user",
|
||||||
|
|
@ -25,7 +46,7 @@ async function fill(sessionID: SessionID, count: number, time = (i: number) => D
|
||||||
tools: {},
|
tools: {},
|
||||||
mode: "",
|
mode: "",
|
||||||
} as unknown as MessageV2.Info)
|
} as unknown as MessageV2.Info)
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID,
|
sessionID,
|
||||||
messageID: id,
|
messageID: id,
|
||||||
|
|
@ -38,7 +59,7 @@ async function fill(sessionID: SessionID, count: number, time = (i: number) => D
|
||||||
|
|
||||||
async function addUser(sessionID: SessionID, text?: string) {
|
async function addUser(sessionID: SessionID, text?: string) {
|
||||||
const id = MessageID.ascending()
|
const id = MessageID.ascending()
|
||||||
await Session.updateMessage({
|
await svc.updateMessage({
|
||||||
id,
|
id,
|
||||||
sessionID,
|
sessionID,
|
||||||
role: "user",
|
role: "user",
|
||||||
|
|
@ -49,7 +70,7 @@ async function addUser(sessionID: SessionID, text?: string) {
|
||||||
mode: "",
|
mode: "",
|
||||||
} as unknown as MessageV2.Info)
|
} as unknown as MessageV2.Info)
|
||||||
if (text) {
|
if (text) {
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID,
|
sessionID,
|
||||||
messageID: id,
|
messageID: id,
|
||||||
|
|
@ -66,7 +87,7 @@ async function addAssistant(
|
||||||
opts?: { summary?: boolean; finish?: string; error?: MessageV2.Assistant["error"] },
|
opts?: { summary?: boolean; finish?: string; error?: MessageV2.Assistant["error"] },
|
||||||
) {
|
) {
|
||||||
const id = MessageID.ascending()
|
const id = MessageID.ascending()
|
||||||
await Session.updateMessage({
|
await svc.updateMessage({
|
||||||
id,
|
id,
|
||||||
sessionID,
|
sessionID,
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
|
|
@ -87,7 +108,7 @@ async function addAssistant(
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addCompactionPart(sessionID: SessionID, messageID: MessageID) {
|
async function addCompactionPart(sessionID: SessionID, messageID: MessageID) {
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID,
|
sessionID,
|
||||||
messageID,
|
messageID,
|
||||||
|
|
@ -101,14 +122,14 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await fill(session.id, 2)
|
await fill(session.id, 2)
|
||||||
|
|
||||||
const result = MessageV2.page({ sessionID: session.id, limit: 10 })
|
const result = MessageV2.page({ sessionID: session.id, limit: 10 })
|
||||||
expect(result).toBeDefined()
|
expect(result).toBeDefined()
|
||||||
expect(result.items).toBeArray()
|
expect(result.items).toBeArray()
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -117,7 +138,7 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 6)
|
const ids = await fill(session.id, 6)
|
||||||
|
|
||||||
const a = MessageV2.page({ sessionID: session.id, limit: 2 })
|
const a = MessageV2.page({ sessionID: session.id, limit: 2 })
|
||||||
|
|
@ -136,7 +157,7 @@ describe("MessageV2.page", () => {
|
||||||
expect(c.more).toBe(false)
|
expect(c.more).toBe(false)
|
||||||
expect(c.cursor).toBeUndefined()
|
expect(c.cursor).toBeUndefined()
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -145,13 +166,13 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 4)
|
const ids = await fill(session.id, 4)
|
||||||
|
|
||||||
const result = MessageV2.page({ sessionID: session.id, limit: 4 })
|
const result = MessageV2.page({ sessionID: session.id, limit: 4 })
|
||||||
expect(result.items.map((item) => item.info.id)).toEqual(ids)
|
expect(result.items.map((item) => item.info.id)).toEqual(ids)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -160,14 +181,14 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
const result = MessageV2.page({ sessionID: session.id, limit: 10 })
|
const result = MessageV2.page({ sessionID: session.id, limit: 10 })
|
||||||
expect(result.items).toEqual([])
|
expect(result.items).toEqual([])
|
||||||
expect(result.more).toBe(false)
|
expect(result.more).toBe(false)
|
||||||
expect(result.cursor).toBeUndefined()
|
expect(result.cursor).toBeUndefined()
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -186,7 +207,7 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 3)
|
const ids = await fill(session.id, 3)
|
||||||
|
|
||||||
const result = MessageV2.page({ sessionID: session.id, limit: 3 })
|
const result = MessageV2.page({ sessionID: session.id, limit: 3 })
|
||||||
|
|
@ -194,7 +215,7 @@ describe("MessageV2.page", () => {
|
||||||
expect(result.more).toBe(false)
|
expect(result.more).toBe(false)
|
||||||
expect(result.cursor).toBeUndefined()
|
expect(result.cursor).toBeUndefined()
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -203,7 +224,7 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 5)
|
const ids = await fill(session.id, 5)
|
||||||
|
|
||||||
const result = MessageV2.page({ sessionID: session.id, limit: 1 })
|
const result = MessageV2.page({ sessionID: session.id, limit: 1 })
|
||||||
|
|
@ -211,7 +232,7 @@ describe("MessageV2.page", () => {
|
||||||
expect(result.items[0].info.id).toBe(ids[ids.length - 1])
|
expect(result.items[0].info.id).toBe(ids[ids.length - 1])
|
||||||
expect(result.more).toBe(true)
|
expect(result.more).toBe(true)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -220,10 +241,10 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const [id] = await fill(session.id, 1)
|
const [id] = await fill(session.id, 1)
|
||||||
|
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
messageID: id,
|
messageID: id,
|
||||||
|
|
@ -235,7 +256,7 @@ describe("MessageV2.page", () => {
|
||||||
expect(result.items).toHaveLength(1)
|
expect(result.items).toHaveLength(1)
|
||||||
expect(result.items[0].parts).toHaveLength(2)
|
expect(result.items[0].parts).toHaveLength(2)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -244,7 +265,7 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 4, (i) => 1000.5 + i)
|
const ids = await fill(session.id, 4, (i) => 1000.5 + i)
|
||||||
|
|
||||||
const a = MessageV2.page({ sessionID: session.id, limit: 2 })
|
const a = MessageV2.page({ sessionID: session.id, limit: 2 })
|
||||||
|
|
@ -253,7 +274,7 @@ describe("MessageV2.page", () => {
|
||||||
expect(a.items.map((item) => item.info.id)).toEqual(ids.slice(-2))
|
expect(a.items.map((item) => item.info.id)).toEqual(ids.slice(-2))
|
||||||
expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2))
|
expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2))
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -262,7 +283,7 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 4, () => 1000)
|
const ids = await fill(session.id, 4, () => 1000)
|
||||||
|
|
||||||
const a = MessageV2.page({ sessionID: session.id, limit: 2 })
|
const a = MessageV2.page({ sessionID: session.id, limit: 2 })
|
||||||
|
|
@ -273,7 +294,7 @@ describe("MessageV2.page", () => {
|
||||||
expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2))
|
expect(b.items.map((item) => item.info.id)).toEqual(ids.slice(0, 2))
|
||||||
expect(b.more).toBe(false)
|
expect(b.more).toBe(false)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -282,8 +303,8 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const a = await Session.create({})
|
const a = await svc.create({})
|
||||||
const b = await Session.create({})
|
const b = await svc.create({})
|
||||||
await fill(a.id, 3)
|
await fill(a.id, 3)
|
||||||
await fill(b.id, 2)
|
await fill(b.id, 2)
|
||||||
|
|
||||||
|
|
@ -294,8 +315,8 @@ describe("MessageV2.page", () => {
|
||||||
expect(resultA.items.every((item) => item.info.sessionID === a.id)).toBe(true)
|
expect(resultA.items.every((item) => item.info.sessionID === a.id)).toBe(true)
|
||||||
expect(resultB.items.every((item) => item.info.sessionID === b.id)).toBe(true)
|
expect(resultB.items.every((item) => item.info.sessionID === b.id)).toBe(true)
|
||||||
|
|
||||||
await Session.remove(a.id)
|
await svc.remove(a.id)
|
||||||
await Session.remove(b.id)
|
await svc.remove(b.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -304,7 +325,7 @@ describe("MessageV2.page", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 10)
|
const ids = await fill(session.id, 10)
|
||||||
|
|
||||||
const result = MessageV2.page({ sessionID: session.id, limit: 100 })
|
const result = MessageV2.page({ sessionID: session.id, limit: 100 })
|
||||||
|
|
@ -313,7 +334,7 @@ describe("MessageV2.page", () => {
|
||||||
expect(result.more).toBe(false)
|
expect(result.more).toBe(false)
|
||||||
expect(result.cursor).toBeUndefined()
|
expect(result.cursor).toBeUndefined()
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -324,13 +345,13 @@ describe("MessageV2.stream", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 5)
|
const ids = await fill(session.id, 5)
|
||||||
|
|
||||||
const items = Array.from(MessageV2.stream(session.id))
|
const items = Array.from(MessageV2.stream(session.id))
|
||||||
expect(items.map((item) => item.info.id)).toEqual(ids.slice().reverse())
|
expect(items.map((item) => item.info.id)).toEqual(ids.slice().reverse())
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -339,12 +360,12 @@ describe("MessageV2.stream", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
const items = Array.from(MessageV2.stream(session.id))
|
const items = Array.from(MessageV2.stream(session.id))
|
||||||
expect(items).toHaveLength(0)
|
expect(items).toHaveLength(0)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -353,14 +374,14 @@ describe("MessageV2.stream", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 1)
|
const ids = await fill(session.id, 1)
|
||||||
|
|
||||||
const items = Array.from(MessageV2.stream(session.id))
|
const items = Array.from(MessageV2.stream(session.id))
|
||||||
expect(items).toHaveLength(1)
|
expect(items).toHaveLength(1)
|
||||||
expect(items[0].info.id).toBe(ids[0])
|
expect(items[0].info.id).toBe(ids[0])
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -369,7 +390,7 @@ describe("MessageV2.stream", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await fill(session.id, 3)
|
await fill(session.id, 3)
|
||||||
|
|
||||||
const items = Array.from(MessageV2.stream(session.id))
|
const items = Array.from(MessageV2.stream(session.id))
|
||||||
|
|
@ -378,7 +399,7 @@ describe("MessageV2.stream", () => {
|
||||||
expect(item.parts[0].type).toBe("text")
|
expect(item.parts[0].type).toBe("text")
|
||||||
}
|
}
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -387,7 +408,7 @@ describe("MessageV2.stream", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 60)
|
const ids = await fill(session.id, 60)
|
||||||
|
|
||||||
const items = Array.from(MessageV2.stream(session.id))
|
const items = Array.from(MessageV2.stream(session.id))
|
||||||
|
|
@ -395,7 +416,7 @@ describe("MessageV2.stream", () => {
|
||||||
expect(items[0].info.id).toBe(ids[ids.length - 1])
|
expect(items[0].info.id).toBe(ids[ids.length - 1])
|
||||||
expect(items[59].info.id).toBe(ids[0])
|
expect(items[59].info.id).toBe(ids[0])
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -404,7 +425,7 @@ describe("MessageV2.stream", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await fill(session.id, 1)
|
await fill(session.id, 1)
|
||||||
|
|
||||||
const gen = MessageV2.stream(session.id)
|
const gen = MessageV2.stream(session.id)
|
||||||
|
|
@ -414,7 +435,7 @@ describe("MessageV2.stream", () => {
|
||||||
expect(first).toHaveProperty("done")
|
expect(first).toHaveProperty("done")
|
||||||
expect(first.done).toBe(false)
|
expect(first.done).toBe(false)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -425,7 +446,7 @@ describe("MessageV2.parts", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const [id] = await fill(session.id, 1)
|
const [id] = await fill(session.id, 1)
|
||||||
|
|
||||||
const result = MessageV2.parts(id)
|
const result = MessageV2.parts(id)
|
||||||
|
|
@ -433,7 +454,7 @@ describe("MessageV2.parts", () => {
|
||||||
expect(result[0].type).toBe("text")
|
expect(result[0].type).toBe("text")
|
||||||
expect((result[0] as MessageV2.TextPart).text).toBe("m0")
|
expect((result[0] as MessageV2.TextPart).text).toBe("m0")
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -442,13 +463,13 @@ describe("MessageV2.parts", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const id = await addUser(session.id)
|
const id = await addUser(session.id)
|
||||||
|
|
||||||
const result = MessageV2.parts(id)
|
const result = MessageV2.parts(id)
|
||||||
expect(result).toEqual([])
|
expect(result).toEqual([])
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -457,17 +478,17 @@ describe("MessageV2.parts", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const [id] = await fill(session.id, 1)
|
const [id] = await fill(session.id, 1)
|
||||||
|
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
messageID: id,
|
messageID: id,
|
||||||
type: "text",
|
type: "text",
|
||||||
text: "second",
|
text: "second",
|
||||||
})
|
})
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
messageID: id,
|
messageID: id,
|
||||||
|
|
@ -481,7 +502,7 @@ describe("MessageV2.parts", () => {
|
||||||
expect((result[1] as MessageV2.TextPart).text).toBe("second")
|
expect((result[1] as MessageV2.TextPart).text).toBe("second")
|
||||||
expect((result[2] as MessageV2.TextPart).text).toBe("third")
|
expect((result[2] as MessageV2.TextPart).text).toBe("third")
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -490,7 +511,7 @@ describe("MessageV2.parts", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
await Session.create({})
|
await svc.create({})
|
||||||
const result = MessageV2.parts(MessageID.ascending())
|
const result = MessageV2.parts(MessageID.ascending())
|
||||||
expect(result).toEqual([])
|
expect(result).toEqual([])
|
||||||
},
|
},
|
||||||
|
|
@ -501,14 +522,14 @@ describe("MessageV2.parts", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const [id] = await fill(session.id, 1)
|
const [id] = await fill(session.id, 1)
|
||||||
|
|
||||||
const result = MessageV2.parts(id)
|
const result = MessageV2.parts(id)
|
||||||
expect(result[0].sessionID).toBe(session.id)
|
expect(result[0].sessionID).toBe(session.id)
|
||||||
expect(result[0].messageID).toBe(id)
|
expect(result[0].messageID).toBe(id)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -519,7 +540,7 @@ describe("MessageV2.get", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const [id] = await fill(session.id, 1)
|
const [id] = await fill(session.id, 1)
|
||||||
|
|
||||||
const result = MessageV2.get({ sessionID: session.id, messageID: id })
|
const result = MessageV2.get({ sessionID: session.id, messageID: id })
|
||||||
|
|
@ -529,7 +550,7 @@ describe("MessageV2.get", () => {
|
||||||
expect(result.parts).toHaveLength(1)
|
expect(result.parts).toHaveLength(1)
|
||||||
expect((result.parts[0] as MessageV2.TextPart).text).toBe("m0")
|
expect((result.parts[0] as MessageV2.TextPart).text).toBe("m0")
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -538,13 +559,13 @@ describe("MessageV2.get", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
expect(() => MessageV2.get({ sessionID: session.id, messageID: MessageID.ascending() })).toThrow(
|
expect(() => MessageV2.get({ sessionID: session.id, messageID: MessageID.ascending() })).toThrow(
|
||||||
"NotFoundError",
|
"NotFoundError",
|
||||||
)
|
)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -553,16 +574,16 @@ describe("MessageV2.get", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const a = await Session.create({})
|
const a = await svc.create({})
|
||||||
const b = await Session.create({})
|
const b = await svc.create({})
|
||||||
const [id] = await fill(a.id, 1)
|
const [id] = await fill(a.id, 1)
|
||||||
|
|
||||||
expect(() => MessageV2.get({ sessionID: b.id, messageID: id })).toThrow("NotFoundError")
|
expect(() => MessageV2.get({ sessionID: b.id, messageID: id })).toThrow("NotFoundError")
|
||||||
const result = MessageV2.get({ sessionID: a.id, messageID: id })
|
const result = MessageV2.get({ sessionID: a.id, messageID: id })
|
||||||
expect(result.info.id).toBe(id)
|
expect(result.info.id).toBe(id)
|
||||||
|
|
||||||
await Session.remove(a.id)
|
await svc.remove(a.id)
|
||||||
await Session.remove(b.id)
|
await svc.remove(b.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -571,10 +592,10 @@ describe("MessageV2.get", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const [id] = await fill(session.id, 1)
|
const [id] = await fill(session.id, 1)
|
||||||
|
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
messageID: id,
|
messageID: id,
|
||||||
|
|
@ -585,7 +606,7 @@ describe("MessageV2.get", () => {
|
||||||
const result = MessageV2.get({ sessionID: session.id, messageID: id })
|
const result = MessageV2.get({ sessionID: session.id, messageID: id })
|
||||||
expect(result.parts).toHaveLength(2)
|
expect(result.parts).toHaveLength(2)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -594,11 +615,11 @@ describe("MessageV2.get", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const uid = await addUser(session.id, "hello")
|
const uid = await addUser(session.id, "hello")
|
||||||
const aid = await addAssistant(session.id, uid)
|
const aid = await addAssistant(session.id, uid)
|
||||||
|
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
messageID: aid,
|
messageID: aid,
|
||||||
|
|
@ -611,7 +632,7 @@ describe("MessageV2.get", () => {
|
||||||
expect(result.parts).toHaveLength(1)
|
expect(result.parts).toHaveLength(1)
|
||||||
expect((result.parts[0] as MessageV2.TextPart).text).toBe("response")
|
expect((result.parts[0] as MessageV2.TextPart).text).toBe("response")
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -620,14 +641,14 @@ describe("MessageV2.get", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const id = await addUser(session.id)
|
const id = await addUser(session.id)
|
||||||
|
|
||||||
const result = MessageV2.get({ sessionID: session.id, messageID: id })
|
const result = MessageV2.get({ sessionID: session.id, messageID: id })
|
||||||
expect(result.info.id).toBe(id)
|
expect(result.info.id).toBe(id)
|
||||||
expect(result.parts).toEqual([])
|
expect(result.parts).toEqual([])
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -638,7 +659,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 5)
|
const ids = await fill(session.id, 5)
|
||||||
|
|
||||||
const result = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
const result = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
||||||
|
|
@ -646,7 +667,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
// reversed from newest-first to chronological
|
// reversed from newest-first to chronological
|
||||||
expect(result.map((item) => item.info.id)).toEqual(ids)
|
expect(result.map((item) => item.info.id)).toEqual(ids)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -655,13 +676,13 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
// Chronological: u1(+compaction part), a1(summary, parentID=u1), u2, a2
|
// Chronological: u1(+compaction part), a1(summary, parentID=u1), u2, a2
|
||||||
// Stream (newest first): a2, u2, a1(adds u1 to completed), u1(in completed + compaction) -> break
|
// Stream (newest first): a2, u2, a1(adds u1 to completed), u1(in completed + compaction) -> break
|
||||||
const u1 = await addUser(session.id, "first question")
|
const u1 = await addUser(session.id, "first question")
|
||||||
const a1 = await addAssistant(session.id, u1, { summary: true, finish: "end_turn" })
|
const a1 = await addAssistant(session.id, u1, { summary: true, finish: "end_turn" })
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
messageID: a1,
|
messageID: a1,
|
||||||
|
|
@ -672,7 +693,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
|
|
||||||
const u2 = await addUser(session.id, "new question")
|
const u2 = await addUser(session.id, "new question")
|
||||||
const a2 = await addAssistant(session.id, u2)
|
const a2 = await addAssistant(session.id, u2)
|
||||||
await Session.updatePart({
|
await svc.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
sessionID: session.id,
|
sessionID: session.id,
|
||||||
messageID: a2,
|
messageID: a2,
|
||||||
|
|
@ -685,7 +706,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
expect(result[0].info.id).toBe(u1)
|
expect(result[0].info.id).toBe(u1)
|
||||||
expect(result.length).toBe(4)
|
expect(result.length).toBe(4)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -699,7 +720,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
const u1 = await addUser(session.id, "hello")
|
const u1 = await addUser(session.id, "hello")
|
||||||
await addCompactionPart(session.id, u1)
|
await addCompactionPart(session.id, u1)
|
||||||
|
|
@ -708,7 +729,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
const result = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
const result = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
||||||
expect(result).toHaveLength(2)
|
expect(result).toHaveLength(2)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -717,7 +738,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
const u1 = await addUser(session.id, "hello")
|
const u1 = await addUser(session.id, "hello")
|
||||||
await addCompactionPart(session.id, u1)
|
await addCompactionPart(session.id, u1)
|
||||||
|
|
@ -733,7 +754,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
// Error assistant doesn't add to completed, so compaction boundary never triggers
|
// Error assistant doesn't add to completed, so compaction boundary never triggers
|
||||||
expect(result).toHaveLength(3)
|
expect(result).toHaveLength(3)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -742,7 +763,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
|
|
||||||
const u1 = await addUser(session.id, "hello")
|
const u1 = await addUser(session.id, "hello")
|
||||||
await addCompactionPart(session.id, u1)
|
await addCompactionPart(session.id, u1)
|
||||||
|
|
@ -754,7 +775,7 @@ describe("MessageV2.filterCompacted", () => {
|
||||||
const result = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
const result = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
||||||
expect(result).toHaveLength(3)
|
expect(result).toHaveLength(3)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -808,7 +829,7 @@ describe("MessageV2 consistency", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await fill(session.id, 3)
|
await fill(session.id, 3)
|
||||||
|
|
||||||
const paged = MessageV2.page({ sessionID: session.id, limit: 10 })
|
const paged = MessageV2.page({ sessionID: session.id, limit: 10 })
|
||||||
|
|
@ -818,7 +839,7 @@ describe("MessageV2 consistency", () => {
|
||||||
expect(got.parts).toEqual(item.parts)
|
expect(got.parts).toEqual(item.parts)
|
||||||
}
|
}
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -827,14 +848,14 @@ describe("MessageV2 consistency", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const [id] = await fill(session.id, 1)
|
const [id] = await fill(session.id, 1)
|
||||||
|
|
||||||
const got = MessageV2.get({ sessionID: session.id, messageID: id })
|
const got = MessageV2.get({ sessionID: session.id, messageID: id })
|
||||||
const standalone = MessageV2.parts(id)
|
const standalone = MessageV2.parts(id)
|
||||||
expect(got.parts).toEqual(standalone)
|
expect(got.parts).toEqual(standalone)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -843,7 +864,7 @@ describe("MessageV2 consistency", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
await fill(session.id, 7)
|
await fill(session.id, 7)
|
||||||
|
|
||||||
const streamed = Array.from(MessageV2.stream(session.id))
|
const streamed = Array.from(MessageV2.stream(session.id))
|
||||||
|
|
@ -861,7 +882,7 @@ describe("MessageV2 consistency", () => {
|
||||||
|
|
||||||
expect(streamed.map((m) => m.info.id)).toEqual(paged.map((m) => m.info.id))
|
expect(streamed.map((m) => m.info.id)).toEqual(paged.map((m) => m.info.id))
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -870,7 +891,7 @@ describe("MessageV2 consistency", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: root,
|
directory: root,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const session = await svc.create({})
|
||||||
const ids = await fill(session.id, 4)
|
const ids = await fill(session.id, 4)
|
||||||
|
|
||||||
const filtered = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
const filtered = MessageV2.filterCompacted(MessageV2.stream(session.id))
|
||||||
|
|
@ -878,7 +899,7 @@ describe("MessageV2 consistency", () => {
|
||||||
|
|
||||||
expect(filtered.map((m) => m.info.id)).toEqual(all.map((m) => m.info.id))
|
expect(filtered.map((m) => m.info.id)).toEqual(all.map((m) => m.info.id))
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await svc.remove(session.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,62 @@
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Session } from "../../src/session"
|
import { Session as SessionNs } from "../../src/session"
|
||||||
import { Bus } from "../../src/bus"
|
import { Bus } from "../../src/bus"
|
||||||
import { Log } from "../../src/util/log"
|
import { Log } from "../../src/util/log"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { MessageV2 } from "../../src/session/message-v2"
|
import { MessageV2 } from "../../src/session/message-v2"
|
||||||
import { MessageID, PartID } from "../../src/session/schema"
|
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
||||||
|
import { AppRuntime } from "../../src/effect/app-runtime"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
const projectRoot = path.join(__dirname, "../..")
|
const projectRoot = path.join(__dirname, "../..")
|
||||||
Log.init({ print: false })
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
function create(input?: SessionNs.CreateInput) {
|
||||||
|
return AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(id: SessionID) {
|
||||||
|
return AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.get(id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(id: SessionID) {
|
||||||
|
return AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.remove(id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMessage<T extends MessageV2.Info>(msg: T) {
|
||||||
|
return AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.updateMessage(msg)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePart<T extends MessageV2.Part>(part: T) {
|
||||||
|
return AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.updatePart(part)))
|
||||||
|
}
|
||||||
|
|
||||||
describe("session.created event", () => {
|
describe("session.created event", () => {
|
||||||
test("should emit session.created event when session is created", async () => {
|
test("should emit session.created event when session is created", async () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: projectRoot,
|
directory: projectRoot,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
let eventReceived = false
|
let eventReceived = false
|
||||||
let receivedInfo: Session.Info | undefined
|
let receivedInfo: SessionNs.Info | undefined
|
||||||
|
|
||||||
const unsub = Bus.subscribe(Session.Event.Created, (event) => {
|
const unsub = Bus.subscribe(SessionNs.Event.Created, (event) => {
|
||||||
eventReceived = true
|
eventReceived = true
|
||||||
receivedInfo = event.properties.info as Session.Info
|
receivedInfo = event.properties.info as SessionNs.Info
|
||||||
})
|
})
|
||||||
|
|
||||||
const session = await Session.create({})
|
const info = await create({})
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||||
|
|
||||||
unsub()
|
unsub()
|
||||||
|
|
||||||
expect(eventReceived).toBe(true)
|
expect(eventReceived).toBe(true)
|
||||||
expect(receivedInfo).toBeDefined()
|
expect(receivedInfo).toBeDefined()
|
||||||
expect(receivedInfo?.id).toBe(session.id)
|
expect(receivedInfo?.id).toBe(info.id)
|
||||||
expect(receivedInfo?.projectID).toBe(session.projectID)
|
expect(receivedInfo?.projectID).toBe(info.projectID)
|
||||||
expect(receivedInfo?.directory).toBe(session.directory)
|
expect(receivedInfo?.directory).toBe(info.directory)
|
||||||
expect(receivedInfo?.title).toBe(session.title)
|
expect(receivedInfo?.title).toBe(info.title)
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await remove(info.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -48,18 +67,16 @@ describe("session.created event", () => {
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const events: string[] = []
|
const events: string[] = []
|
||||||
|
|
||||||
const unsubCreated = Bus.subscribe(Session.Event.Created, () => {
|
const unsubCreated = Bus.subscribe(SessionNs.Event.Created, () => {
|
||||||
events.push("created")
|
events.push("created")
|
||||||
})
|
})
|
||||||
|
|
||||||
const unsubUpdated = Bus.subscribe(Session.Event.Updated, () => {
|
const unsubUpdated = Bus.subscribe(SessionNs.Event.Updated, () => {
|
||||||
events.push("updated")
|
events.push("updated")
|
||||||
})
|
})
|
||||||
|
|
||||||
const session = await Session.create({})
|
const info = await create({})
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||||
|
|
||||||
unsubCreated()
|
unsubCreated()
|
||||||
unsubUpdated()
|
unsubUpdated()
|
||||||
|
|
||||||
|
|
@ -67,7 +84,7 @@ describe("session.created event", () => {
|
||||||
expect(events).toContain("updated")
|
expect(events).toContain("updated")
|
||||||
expect(events.indexOf("created")).toBeLessThan(events.indexOf("updated"))
|
expect(events.indexOf("created")).toBeLessThan(events.indexOf("updated"))
|
||||||
|
|
||||||
await Session.remove(session.id)
|
await remove(info.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -80,12 +97,12 @@ describe("step-finish token propagation via Bus event", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: projectRoot,
|
directory: projectRoot,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const session = await Session.create({})
|
const info = await create({})
|
||||||
|
|
||||||
const messageID = MessageID.ascending()
|
const messageID = MessageID.ascending()
|
||||||
await Session.updateMessage({
|
await updateMessage({
|
||||||
id: messageID,
|
id: messageID,
|
||||||
sessionID: session.id,
|
sessionID: info.id,
|
||||||
role: "user",
|
role: "user",
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
agent: "user",
|
agent: "user",
|
||||||
|
|
@ -110,15 +127,14 @@ describe("step-finish token propagation via Bus event", () => {
|
||||||
const partInput = {
|
const partInput = {
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID,
|
messageID,
|
||||||
sessionID: session.id,
|
sessionID: info.id,
|
||||||
type: "step-finish" as const,
|
type: "step-finish" as const,
|
||||||
reason: "stop",
|
reason: "stop",
|
||||||
cost: 0.005,
|
cost: 0.005,
|
||||||
tokens,
|
tokens,
|
||||||
}
|
}
|
||||||
|
|
||||||
await Session.updatePart(partInput)
|
await updatePart(partInput)
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||||
|
|
||||||
expect(received).toBeDefined()
|
expect(received).toBeDefined()
|
||||||
|
|
@ -134,7 +150,7 @@ describe("step-finish token propagation via Bus event", () => {
|
||||||
expect(received).not.toBe(partInput)
|
expect(received).not.toBe(partInput)
|
||||||
|
|
||||||
unsub()
|
unsub()
|
||||||
await Session.remove(session.id)
|
await remove(info.id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -146,17 +162,17 @@ describe("Session", () => {
|
||||||
test("remove works without an instance", async () => {
|
test("remove works without an instance", async () => {
|
||||||
await using tmp = await tmpdir({ git: true })
|
await using tmp = await tmpdir({ git: true })
|
||||||
|
|
||||||
const session = await Instance.provide({
|
const info = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => Session.create({ title: "remove-without-instance" }),
|
fn: () => create({ title: "remove-without-instance" }),
|
||||||
})
|
})
|
||||||
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await Session.remove(session.id)
|
await remove(info.id)
|
||||||
}).not.toThrow()
|
}).not.toThrow()
|
||||||
|
|
||||||
let missing = false
|
let missing = false
|
||||||
await Session.get(session.id).catch(() => {
|
await get(info.id).catch(() => {
|
||||||
missing = true
|
missing = true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue