58e85960a1
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
181 lines
4.5 KiB
TypeScript
181 lines
4.5 KiB
TypeScript
import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test"
|
|
import { existsSync, unlinkSync, readFileSync } from "fs"
|
|
import {
|
|
buildTranscriptFromSession,
|
|
deleteTempTranscript,
|
|
clearTranscriptCache,
|
|
} from "./transcript"
|
|
|
|
function createMockClient(messages: unknown[] = []) {
|
|
return {
|
|
session: {
|
|
messages: mock(() =>
|
|
Promise.resolve({
|
|
data: messages,
|
|
})
|
|
),
|
|
},
|
|
}
|
|
}
|
|
|
|
describe("transcript caching", () => {
|
|
afterEach(() => {
|
|
clearTranscriptCache()
|
|
})
|
|
|
|
// #given same session called twice
|
|
// #when buildTranscriptFromSession is invoked
|
|
// #then session.messages() should be called only once (cached)
|
|
it("should cache transcript and not re-fetch for same session", async () => {
|
|
const client = createMockClient([
|
|
{
|
|
info: { role: "assistant" },
|
|
parts: [
|
|
{
|
|
type: "tool",
|
|
tool: "bash",
|
|
state: { status: "completed", input: { command: "ls" } },
|
|
},
|
|
],
|
|
},
|
|
])
|
|
|
|
const path1 = await buildTranscriptFromSession(
|
|
client,
|
|
"ses_cache1",
|
|
"/tmp",
|
|
"bash",
|
|
{ command: "echo hi" }
|
|
)
|
|
|
|
const path2 = await buildTranscriptFromSession(
|
|
client,
|
|
"ses_cache1",
|
|
"/tmp",
|
|
"read",
|
|
{ path: "/tmp/file" }
|
|
)
|
|
|
|
// session.messages() called only once
|
|
expect(client.session.messages).toHaveBeenCalledTimes(1)
|
|
|
|
// Both return valid paths
|
|
expect(path1).not.toBeNull()
|
|
expect(path2).not.toBeNull()
|
|
|
|
// Second call should append the new tool entry
|
|
if (path2) {
|
|
const content = readFileSync(path2, "utf-8")
|
|
expect(content).toContain("Read")
|
|
}
|
|
|
|
deleteTempTranscript(path1)
|
|
deleteTempTranscript(path2)
|
|
})
|
|
|
|
// #given different sessions
|
|
// #when buildTranscriptFromSession called for each
|
|
// #then session.messages() should be called for each
|
|
it("should not share cache between different sessions", async () => {
|
|
const client = createMockClient([])
|
|
|
|
await buildTranscriptFromSession(client, "ses_a", "/tmp", "bash", {})
|
|
await buildTranscriptFromSession(client, "ses_b", "/tmp", "bash", {})
|
|
|
|
expect(client.session.messages).toHaveBeenCalledTimes(2)
|
|
|
|
clearTranscriptCache()
|
|
})
|
|
|
|
// #given clearTranscriptCache is called
|
|
// #when buildTranscriptFromSession called again
|
|
// #then should re-fetch
|
|
it("should re-fetch after cache is cleared", async () => {
|
|
const client = createMockClient([])
|
|
|
|
await buildTranscriptFromSession(client, "ses_clear", "/tmp", "bash", {})
|
|
clearTranscriptCache()
|
|
await buildTranscriptFromSession(client, "ses_clear", "/tmp", "bash", {})
|
|
|
|
expect(client.session.messages).toHaveBeenCalledTimes(2)
|
|
})
|
|
|
|
it("keeps intermediate tool calls across sequential transcript rebuilds", async () => {
|
|
// given
|
|
const client = createMockClient([])
|
|
|
|
// when
|
|
const firstPath = await buildTranscriptFromSession(
|
|
client,
|
|
"ses_sequential",
|
|
"/tmp",
|
|
"bash",
|
|
{ command: "echo first" }
|
|
)
|
|
const secondPath = await buildTranscriptFromSession(
|
|
client,
|
|
"ses_sequential",
|
|
"/tmp",
|
|
"read",
|
|
{ filePath: "/tmp/second.txt" }
|
|
)
|
|
const thirdPath = await buildTranscriptFromSession(
|
|
client,
|
|
"ses_sequential",
|
|
"/tmp",
|
|
"write",
|
|
{ filePath: "/tmp/third.txt", content: "third" }
|
|
)
|
|
|
|
// then
|
|
expect(firstPath).not.toBeNull()
|
|
expect(secondPath).not.toBeNull()
|
|
expect(thirdPath).not.toBeNull()
|
|
|
|
if (thirdPath) {
|
|
const content = readFileSync(thirdPath, "utf-8")
|
|
|
|
expect(content).toContain("Bash")
|
|
expect(content).toContain("Read")
|
|
expect(content).toContain("Write")
|
|
}
|
|
|
|
deleteTempTranscript(firstPath)
|
|
deleteTempTranscript(secondPath)
|
|
deleteTempTranscript(thirdPath)
|
|
})
|
|
|
|
it("cleans up previous temp transcript files when rebuilding cached transcripts", async () => {
|
|
// given
|
|
const client = createMockClient([])
|
|
|
|
// when
|
|
const firstPath = await buildTranscriptFromSession(
|
|
client,
|
|
"ses_cleanup",
|
|
"/tmp",
|
|
"bash",
|
|
{ command: "echo first" }
|
|
)
|
|
const secondPath = await buildTranscriptFromSession(
|
|
client,
|
|
"ses_cleanup",
|
|
"/tmp",
|
|
"read",
|
|
{ filePath: "/tmp/second.txt" }
|
|
)
|
|
|
|
// then
|
|
expect(firstPath).not.toBeNull()
|
|
expect(secondPath).not.toBeNull()
|
|
|
|
if (firstPath && secondPath) {
|
|
expect(existsSync(firstPath)).toBe(false)
|
|
expect(existsSync(secondPath)).toBe(true)
|
|
}
|
|
|
|
deleteTempTranscript(firstPath)
|
|
deleteTempTranscript(secondPath)
|
|
})
|
|
})
|