mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
220 lines
6.1 KiB
TypeScript
220 lines
6.1 KiB
TypeScript
import type { SessionSnapshot } from "@earendil-works/pi-protocol";
|
|
import { describe, expect, test } from "vitest";
|
|
import {
|
|
applyTranscriptProgress,
|
|
applyTranscriptSnapshot,
|
|
createTranscriptState,
|
|
selectTranscript,
|
|
} from "../../src/client/transcript.ts";
|
|
|
|
function snapshot(revision: number, text = "saved"): SessionSnapshot {
|
|
return {
|
|
id: "session-1",
|
|
cwd: "/workspace",
|
|
createdAt: 1,
|
|
updatedAt: revision + 1,
|
|
phase: "turn",
|
|
model: { provider: "faux", id: "faux-1" },
|
|
thinkingLevel: "off",
|
|
attached: true,
|
|
locked: true,
|
|
revision,
|
|
transcript: [
|
|
{
|
|
id: "assistant-1",
|
|
role: "assistant",
|
|
content: [{ type: "text", text }],
|
|
status: "streaming",
|
|
model: { provider: "faux", id: "faux-1" },
|
|
timestamp: 1,
|
|
},
|
|
],
|
|
queuedSteer: [],
|
|
queuedSteerCount: 0,
|
|
};
|
|
}
|
|
|
|
describe("remote transcript projection", () => {
|
|
test("projects progress without mutating the authoritative snapshot", () => {
|
|
let state = createTranscriptState(snapshot(1));
|
|
state = applyTranscriptProgress(state, {
|
|
type: "assistant_delta",
|
|
messageId: "assistant-1",
|
|
contentIndex: 0,
|
|
kind: "text",
|
|
delta: " response",
|
|
});
|
|
|
|
expect(state.snapshot.transcript[0]).toMatchObject({ content: [{ type: "text", text: "saved" }] });
|
|
expect(selectTranscript(state)[0]).toMatchObject({ content: [{ type: "text", text: "saved response" }] });
|
|
});
|
|
|
|
test("applies streamed tool-call argument deltas", () => {
|
|
let state = createTranscriptState({
|
|
...snapshot(1),
|
|
transcript: [
|
|
{
|
|
id: "assistant-1",
|
|
role: "assistant",
|
|
content: [{ type: "toolCall", toolCallId: "call-1", toolName: "bash", input: null }],
|
|
status: "streaming",
|
|
model: { provider: "faux", id: "faux-1" },
|
|
timestamp: 1,
|
|
},
|
|
],
|
|
});
|
|
state = applyTranscriptProgress(state, {
|
|
type: "assistant_delta",
|
|
messageId: "assistant-1",
|
|
contentIndex: 0,
|
|
kind: "toolCall",
|
|
delta: '{"command":',
|
|
});
|
|
expect(selectTranscript(state)[0]).toMatchObject({ content: [{ input: '{"command":' }] });
|
|
|
|
state = applyTranscriptProgress(state, {
|
|
type: "item_updated",
|
|
item: {
|
|
id: "assistant-1",
|
|
role: "assistant",
|
|
content: [{ type: "toolCall", toolCallId: "call-1", toolName: "bash", input: null }],
|
|
status: "streaming",
|
|
model: { provider: "faux", id: "faux-1" },
|
|
timestamp: 1,
|
|
},
|
|
});
|
|
state = applyTranscriptProgress(state, {
|
|
type: "assistant_delta",
|
|
messageId: "assistant-1",
|
|
contentIndex: 0,
|
|
kind: "toolCall",
|
|
delta: '"pwd"}',
|
|
});
|
|
expect(selectTranscript(state)[0]).toMatchObject({ content: [{ input: { command: "pwd" } }] });
|
|
});
|
|
|
|
test("appends tool-call deltas to a partial input restored from a snapshot", () => {
|
|
let state = createTranscriptState({
|
|
...snapshot(1),
|
|
transcript: [
|
|
{
|
|
id: "assistant-1",
|
|
role: "assistant",
|
|
content: [{ type: "toolCall", toolCallId: "call-1", toolName: "bash", input: '{"command":' }],
|
|
status: "streaming",
|
|
model: { provider: "faux", id: "faux-1" },
|
|
timestamp: 1,
|
|
},
|
|
],
|
|
});
|
|
|
|
state = applyTranscriptProgress(state, {
|
|
type: "assistant_delta",
|
|
messageId: "assistant-1",
|
|
contentIndex: 0,
|
|
kind: "toolCall",
|
|
delta: '"pwd"}',
|
|
});
|
|
|
|
expect(selectTranscript(state)[0]).toMatchObject({ content: [{ input: { command: "pwd" } }] });
|
|
});
|
|
|
|
test("appends transient tool progress and replaces it by id", () => {
|
|
let state = createTranscriptState(snapshot(1));
|
|
state = applyTranscriptProgress(state, {
|
|
type: "item_started",
|
|
item: {
|
|
id: "tool-call-1",
|
|
role: "tool",
|
|
toolCallId: "call-1",
|
|
toolName: "bash",
|
|
input: { command: "printf hi" },
|
|
content: [],
|
|
status: "running",
|
|
isError: false,
|
|
timestamp: 2,
|
|
},
|
|
});
|
|
expect(selectTranscript(state).at(-1)).toMatchObject({
|
|
id: "tool-call-1",
|
|
role: "tool",
|
|
status: "running",
|
|
content: [],
|
|
});
|
|
state = applyTranscriptProgress(state, {
|
|
type: "item_updated",
|
|
item: {
|
|
id: "tool-call-1",
|
|
role: "tool",
|
|
toolCallId: "call-1",
|
|
toolName: "bash",
|
|
input: { command: "printf hi" },
|
|
content: [{ type: "text", text: "hi" }],
|
|
status: "running",
|
|
isError: false,
|
|
timestamp: 2,
|
|
},
|
|
});
|
|
|
|
expect(selectTranscript(state)).toHaveLength(2);
|
|
expect(selectTranscript(state)[1]).toMatchObject({
|
|
role: "tool",
|
|
status: "running",
|
|
content: [{ type: "text", text: "hi" }],
|
|
});
|
|
});
|
|
|
|
test("resets revision history when the same session runtime is reacquired", () => {
|
|
let state = createTranscriptState(snapshot(50, "old runtime"));
|
|
state = createTranscriptState(snapshot(0, "new runtime"));
|
|
|
|
expect(state.snapshot.revision).toBe(0);
|
|
expect(selectTranscript(state)[0]).toMatchObject({ content: [{ text: "new runtime" }] });
|
|
});
|
|
|
|
test("accepts a lower revision when switching to a different session", () => {
|
|
let state = createTranscriptState(snapshot(50, "old session"));
|
|
state = applyTranscriptSnapshot(state, { ...snapshot(0, "new session"), id: "session-2" });
|
|
|
|
expect(state.snapshot.id).toBe("session-2");
|
|
expect(selectTranscript(state)[0]).toMatchObject({ content: [{ text: "new session" }] });
|
|
});
|
|
|
|
test("renders accepted steering messages from authoritative queued state", () => {
|
|
const state = createTranscriptState({
|
|
...snapshot(2),
|
|
queuedSteerCount: 1,
|
|
queuedSteer: [
|
|
{
|
|
id: "user-steer",
|
|
role: "user",
|
|
content: [{ type: "text", text: "adjust the approach" }],
|
|
timestamp: 2,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(selectTranscript(state).at(-1)).toMatchObject({
|
|
role: "user",
|
|
content: [{ text: "adjust the approach" }],
|
|
});
|
|
});
|
|
|
|
test("a newer snapshot is authoritative and stale snapshots are ignored", () => {
|
|
let state = createTranscriptState(snapshot(3, "new"));
|
|
state = applyTranscriptProgress(state, {
|
|
type: "assistant_delta",
|
|
messageId: "assistant-1",
|
|
contentIndex: 0,
|
|
kind: "text",
|
|
delta: " transient",
|
|
});
|
|
state = applyTranscriptSnapshot(state, snapshot(4, "authoritative"));
|
|
state = applyTranscriptSnapshot(state, snapshot(2, "stale"));
|
|
|
|
expect(state.snapshot.revision).toBe(4);
|
|
expect(selectTranscript(state)[0]).toMatchObject({
|
|
content: [{ type: "text", text: "authoritative" }],
|
|
});
|
|
});
|
|
});
|