mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-06 12:19:34 +00:00
178 lines
6.6 KiB
TypeScript
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 },
|
|
],
|
|
},
|
|
])
|
|
})
|
|
})
|