Improve v2 session message rendering (#25634)

This commit is contained in:
Dax
2026-05-04 22:35:21 -04:00
committed by GitHub
parent 0df2bb0f3b
commit 39c88f9afb
17 changed files with 677 additions and 275 deletions
+56 -20
View File
@@ -3,17 +3,17 @@ import { SessionID } from "@/session/schema"
import { WorkspaceID } from "@/control-plane/schema"
import { and, asc, desc, eq, gt, gte, isNull, like, lt, or, type SQL } from "@/storage/db"
import * as Database from "@/storage/db"
import { Context, DateTime, Effect, Layer, Schema } from "effect"
import { Context, DateTime, Effect, Layer, Option, Schema } from "effect"
import { SessionMessage } from "./session-message"
import type { Prompt } from "./session-prompt"
import { EventV2 } from "./event"
import { ProjectID } from "@/project/schema"
import { ModelID, ProviderID } from "@/provider/schema"
import { SessionEvent } from "./session-event"
import { V2Schema } from "./schema"
import { optionalOmitUndefined } from "@/util/schema"
import { Modelv2 } from "./model"
export const Delivery = Schema.Union([Schema.Literal("immediate"), Schema.Literal("deferred")]).annotate({
export const Delivery = Schema.Literals(["immediate", "deferred"]).annotate({
identifier: "Session.Delivery",
})
export type Delivery = Schema.Schema.Type<typeof Delivery>
@@ -27,11 +27,7 @@ export class Info extends Schema.Class<Info>("Session.Info")({
workspaceID: optionalOmitUndefined(WorkspaceID),
path: optionalOmitUndefined(Schema.String),
agent: optionalOmitUndefined(Schema.String),
model: Schema.Struct({
id: ModelID,
providerID: ProviderID,
variant: optionalOmitUndefined(Schema.String),
}).pipe(optionalOmitUndefined),
model: Modelv2.Ref.pipe(optionalOmitUndefined),
time: Schema.Struct({
created: V2Schema.DateTimeUtcFromMillis,
updated: V2Schema.DateTimeUtcFromMillis,
@@ -53,7 +49,18 @@ export class Info extends Schema.Class<Info>("Session.Info")({
*/
}) {}
export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Session.NotFoundError", {
sessionID: SessionID,
}) {}
export interface Interface {
readonly create: (input?: {
agent?: string
model?: Modelv2.Ref
parentID?: SessionID
workspaceID?: WorkspaceID
}) => Effect.Effect<Info>
readonly get: (sessionID: SessionID) => Effect.Effect<Info, NotFoundError>
readonly list: (input: {
limit?: number
order?: "asc" | "desc"
@@ -88,13 +95,15 @@ export interface Interface {
}) => Effect.Effect<SessionMessage.User, never>
readonly shell: (input: { id?: EventV2.ID; sessionID: SessionID; command: string }) => Effect.Effect<void, never>
readonly skill: (input: { id?: EventV2.ID; sessionID: SessionID; skill: string }) => Effect.Effect<void, never>
readonly subagent: (input: {
id?: EventV2.ID
parentID: SessionID
prompt: Prompt
agent: string
model?: Modelv2.Ref
}) => Effect.Effect<void, NotFoundError>
readonly switchAgent: (input: { sessionID: SessionID; agent: string }) => Effect.Effect<void, never>
readonly switchModel: (input: {
sessionID: SessionID
id: ModelID
providerID: ProviderID
variant?: string
}) => Effect.Effect<void, never>
readonly switchModel: (input: { sessionID: SessionID; model: Modelv2.Ref }) => Effect.Effect<void, never>
readonly compact: (sessionID: SessionID) => Effect.Effect<void, never>
readonly wait: (sessionID: SessionID) => Effect.Effect<void, never>
}
@@ -120,9 +129,9 @@ export const layer = Layer.effect(
agent: row.agent ?? undefined,
model: row.model
? {
id: ModelID.make(row.model.id),
providerID: ProviderID.make(row.model.providerID),
variant: row.model.variant,
id: Modelv2.ID.make(row.model.id),
providerID: Modelv2.ProviderID.make(row.model.providerID),
variant: Modelv2.VariantID.make(row.model.variant ?? "default"),
}
: undefined,
time: {
@@ -134,6 +143,14 @@ export const layer = Layer.effect(
}
const result: Interface = {
create: Effect.fn("V2Session.create")(function* (_input) {
return {} as any
}),
get: Effect.fn("V2Session.get")(function* (sessionID) {
const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, sessionID)).get())
if (!row) return yield* new NotFoundError({ sessionID })
return fromRow(row)
}),
list: Effect.fn("V2Session.list")(function* (input) {
const direction = input.cursor?.direction ?? "next"
let order = input.order ?? "desc"
@@ -262,11 +279,30 @@ export const layer = Layer.effect(
EventV2.run(SessionEvent.ModelSwitched.Sync, {
sessionID: input.sessionID,
timestamp: DateTime.makeUnsafe(Date.now()),
id: input.id,
providerID: input.providerID,
variant: input.variant,
model: input.model,
})
}),
subagent: Effect.fn("V2Session.subagent")(function* (input) {
const parent = yield* result.get(input.parentID)
const session = yield* result.create({
agent: input.agent,
model: input.model,
parentID: input.parentID,
workspaceID: parent.workspaceID,
})
yield* result.prompt({
prompt: input.prompt,
sessionID: session.id,
})
yield* Effect.gen(function* () {
yield* result.wait(session.id)
const messages = yield* result.messages({ sessionID: session.id, order: "desc" })
const assistant = messages.find((msg) => msg.type === "assistant")
if (!assistant) return
const text = assistant.content.findLast((part) => part.type === "text")
if (!text) return
}).pipe(Effect.forkChild())
}),
compact: Effect.fn("V2Session.compact")(function* (_sessionID) {}),
wait: Effect.fn("V2Session.wait")(function* (_sessionID) {}),
}