opencode/packages/schema/test/session-legacy.test.ts
2026-10-20 00:42:13 -05:00

178 lines
6.6 KiB
TypeScript

import { describe, expect, test } from "bun:test"
import { Schema } from "effect"
import { Durable } from "../src/durable-event-manifest.js"
import { SessionLegacy } from "../src/session-legacy.js"
import { SessionMessage } from "../src/session-message.js"
const ref = { sessionID: "ses_legacy", assistantMessageID: "msg_legacy" }
const tokens = { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }
const native = { signature: "kept" }
const textPart = { type: "text", text: "hi", state: native }
const result = { result: "kept" }
const toolPart = {
type: "tool",
id: "call",
name: "search",
providerState: native,
providerResultState: result,
state: { status: "streaming", input: "" },
time: { created: 0 },
}
const decode = (type: string, data: unknown) => {
const definition = Durable.get(type)
if (!definition) throw new Error(`Unknown durable event ${type}`)
return Schema.decodeUnknownSync(definition.data)(SessionLegacy.event(definition.type, data))
}
describe("SessionLegacy", () => {
test.each([
["session.text.ended.1", { ...ref, ordinal: 0, text: "hi", state: native }],
["session.reasoning.started.1", { ...ref, ordinal: 0, state: native }],
["session.reasoning.ended.1", { ...ref, ordinal: 0, text: "thought", state: native }],
["session.tool.called.1", { ...ref, id: "call", input: {}, executed: true, state: native }],
["session.step.ended.1", { ...ref, finish: "stop", providerState: native, cost: 0, tokens }],
[
"session.step.failed.1",
{ ...ref, error: { type: "provider.unknown", message: "failed" }, providerState: native },
],
[
"session.compaction.ended.1",
{ sessionID: ref.sessionID, reason: "auto", text: "s", recent: "", providerState: native },
],
])("upgrades %s", (type, data) => {
expect(decode(type, data)).toMatchObject({ native })
})
test.each([
["session.tool.success.2", { ...ref, id: "call", content: [{ type: "text", text: "done" }], executed: true }],
[
"session.tool.failed.2",
{ ...ref, id: "call", error: { type: "tool.execution", message: "failed" }, executed: true },
],
])("upgrades %s result native data", (type, data) => {
expect(decode(type, { ...data, resultState: native })).toMatchObject({ native })
})
test("upgrades replaced assistant content without touching tool status", () => {
expect(
decode("session.message.content.updated.1", {
sessionID: ref.sessionID,
messageID: ref.assistantMessageID,
content: [textPart, toolPart],
}),
).toMatchObject({
content: [
{ type: "text", native },
{ type: "tool", native: { ...native, ...result }, state: { status: "streaming", input: "" } },
],
})
})
test("leaves current and unrelated payloads unchanged", () => {
const current = { ...ref, ordinal: 0, text: "hi", native }
expect(SessionLegacy.event("session.text.ended", current)).toBe(current)
const success = { ...ref, id: "call", content: [], executed: true, native }
expect(SessionLegacy.event("session.tool.success", success)).toBe(success)
const user = { type: "user", text: "hi", state: native }
expect(SessionLegacy.message("user", user)).toBe(user)
})
test("keeps the current name when a payload carries both", () => {
expect(SessionLegacy.event("session.text.ended", { text: "hi", state: { old: true }, native })).toEqual({
text: "hi",
native,
})
})
test("merges legacy tool result data over call data without touching lifecycle state", () => {
expect(
SessionLegacy.message("assistant", {
content: [
{
...toolPart,
native: { itemId: "shared", call: true },
providerResultState: { itemId: "shared", result: true },
},
],
}),
).toMatchObject({
content: [{ native: { itemId: "shared", call: true, result: true }, state: toolPart.state }],
})
})
test("upgrades assistant and compaction messages", () => {
expect(SessionLegacy.message("assistant", { providerState: native, content: [textPart, toolPart] })).toEqual({
native,
content: [
{ type: "text", text: "hi", native },
{
type: "tool",
id: "call",
name: "search",
native: { ...native, ...result },
state: toolPart.state,
time: toolPart.time,
},
],
})
expect(SessionLegacy.message("compaction", { status: "completed", providerState: native })).toEqual({
status: "completed",
native,
})
})
test("moves the kept text a compaction stored in recent into metadata", () => {
expect(
SessionLegacy.message("compaction", { status: "completed", recent: "[User]: kept", metadata: { source: "x" } }),
).toEqual({ status: "completed", metadata: { recent: "[User]: kept", source: "x" } })
expect(SessionLegacy.message("compaction", { status: "completed", recent: "" })).toEqual({ status: "completed" })
expect(
SessionLegacy.event("session.compaction.ended", { reason: "auto", text: "s", recent: "[User]: kept" }),
).toEqual({ reason: "auto", text: "s" })
expect(SessionLegacy.event("session.compaction.started", { reason: "auto", recent: "" })).toEqual({
reason: "auto",
})
})
test("moves a provider compaction's window from providerContext into native", () => {
const messages = [{ role: "assistant", content: [{ type: "compaction", provider: "openai", encrypted: "opaque" }] }]
const providerContext = { version: 1, provenance: { providerID: "openai" }, messages }
expect(
SessionLegacy.message("compaction", {
status: "completed",
summary: "",
native: { step: true },
providerContext,
}),
).toEqual({ status: "completed", summary: "", native: { messages } })
expect(SessionLegacy.event("session.compaction.ended", { reason: "auto", text: "", providerContext })).toEqual({
reason: "auto",
text: "",
native: { messages },
})
})
test("upgrades exported messages before decoding", () => {
const message = {
id: "msg_legacy",
type: "assistant",
agent: "build",
model: { providerID: "anthropic", id: "claude" },
providerState: native,
content: [textPart, { type: "reasoning", text: "thought", state: native }],
time: { created: 0 },
}
expect(
Schema.decodeUnknownSync(Schema.Array(SessionMessage.Info))(SessionLegacy.messages([message])),
).toMatchObject([
{
native,
content: [
{ type: "text", native },
{ type: "reasoning", native },
],
},
])
})
})