Files
oh-my-pi/packages/coding-agent/test/session/session-history-format.test.ts
T
can1357 a64ff00cb8 feat(coding-agent): added history:// internal URL for agent transcripts
Registers a HistoryProtocolHandler with the internal URL router: history:// lists every registered agent (id, status, kind, last activity) and history://<agentId> renders a concise markdown transcript (tool calls collapsed to one line each, thinking elided). Live refs render from the in-memory message array; parked refs load read-only from the JSONL session file via loadSessionMessagesReadOnly (no writer, no lock). System prompt + read tool prompt + docs/tools/read.md learn the new scheme.
2026-06-10 17:52:13 +02:00

109 lines
3.7 KiB
TypeScript

/**
* Contracts: history:// transcript serializer (rework-contracts.md §5).
*
* - `## user` / `## assistant` headers carry full text.
* - Thinking blocks are elided entirely.
* - Each toolCall collapses with its toolResult into ONE `→ name(…) ⇒ …`
* line (ok and error variants); result bodies are never dumped.
* - Custom messages render as one-liners (`[irc] from → me: …`).
* - No system prompt / tool catalog sections.
*/
import { describe, expect, it } from "bun:test";
import { formatSessionHistoryMarkdown } from "@oh-my-pi/pi-coding-agent/session/session-history-format";
function buildMessages(): unknown[] {
return [
{ role: "user", content: "Please read the config.", timestamp: 1 },
{
role: "assistant",
content: [
{ type: "thinking", thinking: "SECRET-THOUGHT about the approach" },
{ type: "text", text: "Reading it now." },
{ type: "toolCall", id: "tc-1", name: "read", arguments: { path: "src/config.ts" } },
{ type: "toolCall", id: "tc-2", name: "bash", arguments: { command: "bun test" } },
],
api: "anthropic-messages",
provider: "anthropic",
model: "test-model",
usage: {},
stopReason: "toolUse",
timestamp: 2,
},
{
role: "toolResult",
toolCallId: "tc-1",
toolName: "read",
content: [{ type: "text", text: "const a = 1;\nconst b = 2;\nconst c = 3;" }],
isError: false,
timestamp: 3,
},
{
role: "toolResult",
toolCallId: "tc-2",
toolName: "bash",
content: [{ type: "text", text: "FAIL: 1 test failed" }],
isError: true,
timestamp: 4,
},
{
role: "custom",
customType: "irc:incoming",
content: "full rendered irc prompt that must not appear",
details: { from: "Main", message: "status update please" },
timestamp: 5,
},
];
}
describe("formatSessionHistoryMarkdown", () => {
it("renders role headers, collapses tool pairs to one line, and elides thinking", () => {
const output = formatSessionHistoryMarkdown(buildMessages());
expect(output).toContain("## user");
expect(output).toContain("Please read the config.");
expect(output).toContain("## assistant");
expect(output).toContain("Reading it now.");
// Thinking is elided entirely.
expect(output).not.toContain("SECRET-THOUGHT");
// Tool call + result collapse to one line each; bodies are not dumped.
expect(output).toContain("→ read(src/config.ts) ⇒ ok · 3 lines");
expect(output).not.toContain("const a = 1;");
// Error variant carries the first line of the error output.
expect(output).toContain("→ bash(bun test) ⇒ error · 1 line — FAIL: 1 test failed");
// Consumed toolResults do not render a second orphan line.
const toolLines = output.split("\n").filter(line => line.startsWith("→ "));
expect(toolLines).toHaveLength(2);
// Custom messages are one-liners; the rendered prompt body is dropped.
expect(output).toContain("[irc] Main → me: status update please");
expect(output).not.toContain("full rendered irc prompt");
// Concise transcript: no prompt/tool-catalog sections.
expect(output).not.toContain("System Prompt");
expect(output).not.toContain("Available Tools");
});
it("prefixes an H1 title when requested", () => {
const output = formatSessionHistoryMarkdown(buildMessages(), { title: "Spawnling (idle)" });
expect(output.startsWith("# Spawnling (idle)\n")).toBe(true);
});
it("renders an orphan toolResult (truncated history) as its own line", () => {
const output = formatSessionHistoryMarkdown([
{
role: "toolResult",
toolCallId: "tc-orphan",
toolName: "search",
content: [{ type: "text", text: "one match" }],
isError: false,
timestamp: 1,
},
]);
expect(output).toContain("→ search() ⇒ ok · 1 line");
});
});