Official
opencode
OpenCode agent provider: runs opencode serve and talks to it over HTTP and server-sent events.
The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/opencode@0.2.0
Permissions in 0.2.0
Files
map.test.ts30.8 KB
import { test } from "node:test";
import assert from "node:assert/strict";
import {
Mapper,
addUsage,
approval,
firstLine,
parseTaskOutput,
questionId,
taskTitle,
transcriptTree,
} from "./map.ts";
import type { Mapped, Part, TaskLink } from "./map.ts";
import { obj, arr } from "./map.ts";
const part = (sessionID: string, value: Part) => ({
type: "message.part.updated",
properties: { sessionID, part: value },
});
/// The single mapped record of a scenario.
function first(mapped: Mapped[]): Record<string, unknown> {
return obj(mapped[0]);
}
/// The agent event of a single-event mapping.
function eventOf(mapped: Mapped[]): Record<string, unknown> {
return obj(first(mapped)["event"]);
}
test("text deltas accumulate byte for byte, keyed by the part", () => {
const mapper = new Mapper();
const delta = (text: string) =>
mapper.event({
type: "message.part.delta",
properties: { sessionID: "ses_1", messageID: "msg_a", partID: "prt_1", field: "text", delta: text },
});
assert.deepEqual(delta("Hello"), [
{ type: "event", session: "ses_1", event: { event: "text_delta", itemId: "prt_1", text: "Hello", mode: "append" } },
]);
assert.equal(eventOf(delta(" "))["text"], " ", "whitespace is kept");
});
test("user message parts are not assistant text", () => {
const mapper = new Mapper();
mapper.event({ type: "message.updated", properties: { info: { id: "msg_user", role: "user" } } });
assert.deepEqual(
mapper.event(part("ses_1", { type: "text", id: "prt_u", messageID: "msg_user", sessionID: "ses_1", text: "hi" })),
[],
);
assert.deepEqual(
mapper.event({
type: "message.part.delta",
properties: { sessionID: "ses_1", messageID: "msg_user", partID: "prt_u", field: "text", delta: "hi" },
}),
[],
);
});
test("assistant updates retain native parent identity even without model metadata", () => {
const mapper = new Mapper();
const info = { id: "msg_a", sessionID: "ses_1", role: "assistant", parentID: "msg_u" };
assert.deepEqual(mapper.event({ type: "message.updated", data: { info } }), [
{ type: "assistant", session: "ses_1", parent: "msg_u", provider: null, model: null, variant: null },
]);
const aborted = mapper.event({
type: "message.updated",
properties: { info: { ...info, error: { name: "MessageAbortedError" } } },
});
assert.equal(first(aborted)["parent"], null, "an aborted snapshot is not pickup evidence");
assert.deepEqual(mapper.event({ type: "message.updated", properties: { info: { ...info, role: "system" } } }), []);
assert.equal(
first(mapper.event({ type: "message.updated", properties: { info: { ...info, parentID: 1 } } }))["parent"],
null,
);
});
test("a reasoning snapshot replaces", () => {
const mapped = new Mapper().event(
part("ses_1", { type: "reasoning", id: "prt_r", messageID: "msg_a", sessionID: "ses_1", text: "thinking" }),
);
assert.deepEqual(first(mapped)["event"], {
event: "reasoning_delta",
itemId: "prt_r",
text: "thinking",
mode: "replace",
});
});
test("a command tool reports its terminal output and exit code", () => {
const mapper = new Mapper();
const running = mapper.event(
part("ses_1", {
type: "tool",
id: "prt_b",
messageID: "msg_a",
sessionID: "ses_1",
callID: "call_1",
tool: "bash",
state: { status: "running", input: { command: "echo hi-there" }, time: { start: 1 } },
}),
);
assert.equal(eventOf(running)["event"], "tool_call_started");
assert.equal(eventOf(running)["kind"], "execute");
assert.equal(eventOf(running)["status"], "running");
assert.equal(eventOf(running)["title"], "echo hi-there");
const completed = mapper.event(
part("ses_1", {
type: "tool",
id: "prt_b",
messageID: "msg_a",
sessionID: "ses_1",
callID: "call_1",
tool: "bash",
state: {
status: "completed",
input: { command: "echo hi-there" },
output: "hi-there\n",
metadata: { output: "hi-there\n", exit: 0 },
title: "echo hi-there",
},
}),
);
assert.equal(eventOf(completed)["event"], "tool_call_updated");
assert.equal(eventOf(completed)["status"], "completed");
assert.deepEqual(eventOf(completed)["content"], [
{ type: "terminal", command: "echo hi-there", output: "hi-there\n", exitCode: 0 },
]);
});
test("a file edit carries the unified diff and its location", () => {
const diff = "@@ -1,2 +1,3 @@\n hello\n world\n+third\n";
const mapped = new Mapper().event(
part("ses_1", {
type: "tool",
id: "prt_e",
messageID: "msg_a",
sessionID: "ses_1",
callID: "call_2",
tool: "edit",
state: {
status: "completed",
input: { filePath: "/w/a.txt", oldString: "world", newString: "world\nthird" },
output: "Edit applied successfully.",
metadata: { diff },
title: "a.txt",
},
}),
);
const call = eventOf(mapped);
assert.equal(call["kind"], "edit");
assert.deepEqual(call["locations"], [{ path: "/w/a.txt" }]);
assert.deepEqual(call["content"], [{ type: "diff", path: "/w/a.txt", diff }]);
});
test("a plugin tool served by the host keeps its full name and is titled by its own", () => {
const mapped = new Mapper().event(
part("ses_1", {
type: "tool",
id: "prt_t",
messageID: "msg_a",
sessionID: "ses_1",
callID: "c",
tool: "convergence_lucky_number",
state: { status: "completed", input: {}, output: "The lucky number is 7319.", title: "" },
}),
);
const call = eventOf(mapped);
assert.equal(call["name"], "convergence_lucky_number");
assert.equal(call["title"], "lucky_number");
assert.equal(call["kind"], "other");
assert.deepEqual(call["content"], [{ type: "text", text: "The lucky number is 7319." }]);
});
test("replies made in another client resolve the pending request", () => {
const mapper = new Mapper();
assert.deepEqual(
mapper.event({ type: "permission.replied", properties: { sessionID: "ses_1", requestID: "per_1", reply: "once" } }),
[{ type: "resolved", session: "ses_1", approval: "per_1" }],
);
assert.deepEqual(
mapper.event({ type: "question.replied", properties: { sessionID: "ses_1", requestID: "que_1", answers: [] } }),
[{ type: "resolved", session: "ses_1", question: "que_1" }],
);
assert.deepEqual(
mapper.event({ type: "question.rejected", properties: { sessionID: "ses_1", requestID: "que_2" } }),
[{ type: "resolved", session: "ses_1", question: "que_2" }],
);
});
test("placeholder titles are not forwarded; real ones are", () => {
const mapper = new Mapper();
const update = (title: unknown) =>
mapper.event({ type: "session.updated", properties: { sessionID: "ses_1", info: { id: "ses_1", title } } });
assert.deepEqual(update("New session - 2026-09-16T09:01:02.003Z"), []);
assert.deepEqual(update("Child session - 2026-09-16T09:01:02.003Z"), []);
assert.deepEqual(update("New Chat"), []);
assert.deepEqual(update(" "), []);
assert.deepEqual(update(null), []);
assert.deepEqual(update("Fix the parser"), [
{ type: "event", session: "ses_1", event: { event: "session_info", title: "Fix the parser" } },
]);
// A real title that merely starts like the placeholder is kept.
assert.equal(eventOf(update("New session - retry the import"))["title"], "New session - retry the import");
});
test("a permission becomes an approval with the native options", () => {
const mapped = new Mapper().event({
type: "permission.asked",
properties: {
id: "per_1",
sessionID: "ses_1",
permission: "bash",
patterns: ["echo permtest"],
metadata: { command: "echo permtest" },
always: ["echo *"],
tool: { messageID: "msg_a", callID: "call_3" },
},
});
const firstApproval = first(mapped);
assert.equal(firstApproval["session"], "ses_1");
const request = obj(firstApproval["request"]);
assert.equal(request["id"], "per_1");
assert.equal(request["title"], "bash echo permtest");
assert.deepEqual(
arr(request["options"]).map((option: unknown) => [obj(option)["id"], obj(option)["kind"]]),
[
["once", "allow_once"],
["always", "allow_always"],
["reject", "reject_once"],
],
);
});
test("a permission without always patterns offers no always option", () => {
const mapped = new Mapper().event({
type: "permission.asked",
properties: {
id: "per_2",
sessionID: "ses_1",
permission: "external_directory",
patterns: ["/etc"],
metadata: {},
always: [],
},
});
const onlyRequest = obj(first(mapped)["request"]);
assert.deepEqual(
arr(onlyRequest["options"]).map((option: unknown) => obj(option)["id"]),
["once", "reject"],
);
assert.equal(onlyRequest["title"], "external_directory /etc");
});
test("a question becomes a structured field", () => {
const mapped = new Mapper().event({
type: "question.asked",
properties: {
id: "que_1",
sessionID: "ses_1",
questions: [
{
question: "Which database?",
header: "Database",
multiple: false,
custom: true,
options: [
{ label: "Postgres", description: "Relational" },
{ label: "SQLite", description: "Embedded" },
],
},
],
},
});
const question = obj(first(mapped)["request"]);
assert.equal(question["id"], "que_1");
assert.equal(question["message"], "Which database?");
const field = obj(arr(question["fields"])[0]);
assert.equal(field["kind"], "select");
assert.equal(field["allowOther"], true);
assert.equal(field["description"], "Database");
assert.deepEqual(
arr(field["options"]).map((option: unknown) => obj(option)["value"]),
["Postgres", "SQLite"],
);
});
test("idle and error drive the run outcome", () => {
const mapper = new Mapper();
assert.deepEqual(
mapper.event({
type: "session.error",
properties: { sessionID: "ses_1", error: { name: "APIError", data: { message: "Forbidden: not licensed" } } },
}),
[{ type: "failed", session: "ses_1", message: "Forbidden: not licensed" }],
);
assert.deepEqual(mapper.event({ type: "session.idle", properties: { sessionID: "ses_1" } }), [
{ type: "idle", session: "ses_1" },
]);
});
test("usage comes from the step totals", () => {
const mapped = new Mapper().event(
part("ses_1", {
type: "step-finish",
id: "prt_s",
messageID: "msg_a",
sessionID: "ses_1",
tokens: { total: 22145, input: 20306, output: 47, reasoning: 0, cache: { read: 1792, write: 0 } },
cost: 0.5,
}),
);
// The breakdown is what the request cost; it need not add up to the
// context.
assert.deepEqual(mapped, [
{
type: "event",
session: "ses_1",
event: {
event: "usage",
usedTokens: 22145,
inputTokens: 20306,
cachedInputTokens: 1792,
outputTokens: 47,
reasoningTokens: 0,
costUsd: 0.5,
},
},
]);
});
test("a session with a parent is announced as a subagent", () => {
const mapper = new Mapper();
const mapped = mapper.event({
type: "session.created",
properties: {
sessionID: "ses_child",
info: { id: "ses_child", parentID: "ses_1", title: "Search the tests", agent: "explore" },
},
});
assert.deepEqual(mapped, [
{ type: "child", child: { id: "ses_child", parent: "ses_1", title: "Search the tests", agent: "explore" } },
{ type: "event", session: "ses_child", event: { event: "session_info", title: "Search the tests" } },
]);
const topLevel = mapper.event({
type: "session.created",
properties: { sessionID: "ses_2", info: { id: "ses_2", title: "Fix the parser" } },
});
assert.ok(!topLevel.some((entry) => entry.type === "child"));
// The placeholder a fresh child is minted with is still a placeholder.
const placeholder = mapper.event({
type: "session.created",
properties: {
sessionID: "ses_c2",
info: { id: "ses_c2", parentID: "ses_1", title: "Child session - 2026-09-16T09:01:02.003Z", agent: "general" },
},
});
assert.deepEqual(placeholder, [
{ type: "child", child: { id: "ses_c2", parent: "ses_1", title: null, agent: "general" } },
]);
});
test("compaction is reported so the transcript can show the boundary", () => {
assert.deepEqual(new Mapper().event({ type: "session.compacted", properties: { sessionID: "ses_1" } }), [
{ type: "event", session: "ses_1", event: { event: "compacted" } },
]);
});
test("an approval for a call without arguments yet shows the requested command", () => {
const mapper = new Mapper();
mapper.event(
part("ses_1", {
type: "tool",
id: "prt_b",
messageID: "msg_a",
sessionID: "ses_1",
callID: "call_1",
tool: "bash",
state: { status: "pending", input: {}, raw: "" },
}),
);
const mapped = mapper.event({
type: "permission.asked",
properties: {
id: "per_1",
sessionID: "ses_1",
permission: "bash",
patterns: ["wc -l a.txt b.txt"],
metadata: {},
always: ["wc *"],
tool: { messageID: "msg_a", callID: "call_1" },
},
});
const announced = obj(first(mapped)["request"]);
assert.equal(announced["title"], "bash wc -l a.txt b.txt");
const announcedCall = obj(announced["toolCall"]);
assert.equal(announcedCall["id"], "prt_b", "the announced call is still the one shown");
assert.equal(announcedCall["title"], "wc -l a.txt b.txt");
assert.deepEqual(announcedCall["content"], [{ type: "terminal", command: "wc -l a.txt b.txt", output: "" }]);
});
test("an approval carries the tool call it blocks, and a finished call is no longer held", () => {
const mapper = new Mapper();
mapper.event(
part("ses_1", {
type: "tool",
id: "prt_b",
messageID: "msg_a",
sessionID: "ses_1",
callID: "call_1",
tool: "bash",
state: { status: "pending", input: { command: "rm -rf build" } },
}),
);
const held = obj(
first(
mapper.event({
type: "permission.asked",
properties: {
id: "per_1",
sessionID: "ses_1",
permission: "bash",
patterns: ["rm *"],
metadata: { command: "rm -rf build" },
always: ["rm *"],
tool: { messageID: "msg_a", callID: "call_1" },
},
}),
)["request"],
);
const heldCall = obj(held["toolCall"]);
assert.equal(heldCall["id"], "prt_b");
assert.equal(heldCall["name"], "bash");
assert.equal(obj(heldCall["input"])["command"], "rm -rf build");
// An `always` grant is remembered against the folder.
assert.match(String(held["warning"]), /workspace/);
mapper.event(
part("ses_1", {
type: "tool",
id: "prt_b",
messageID: "msg_a",
sessionID: "ses_1",
callID: "call_1",
tool: "bash",
state: { status: "completed", input: { command: "rm -rf build" }, output: "", metadata: { exit: 0 } },
}),
);
assert.equal(mapper.pendingTools.size, 0, "finished tool calls are still being held");
});
test("an unannounced request describes itself from its own fields", () => {
const mapped = approval(
{
id: "per_2",
sessionID: "ses_1",
permission: "external_directory",
patterns: ["/etc", "*"],
metadata: {},
always: [],
},
null,
);
assert.equal(mapped.toolCall.id, "per_2");
// `*` is the catch-all rule matching, which tells a reader nothing.
assert.equal(mapped.toolCall.title, "external directory /etc");
assert.equal(mapped.warning, undefined, "nothing would be remembered, so nothing to warn about");
// A plugin tool's request is named by the tool, not the rule name.
const tool = approval(
{
id: "per_3",
sessionID: "ses_1",
permission: "convergence_lucky_number",
patterns: ["*"],
metadata: {},
always: ["*"],
},
null,
);
assert.equal(tool.title, "convergence_lucky_number");
assert.equal(tool.toolCall.title, "lucky_number");
});
test("question ids are stable names, not positions", () => {
assert.equal(questionId(0, "Database"), "question-0-database");
assert.equal(questionId(1, " Migrate now? "), "question-1-migrate-now");
assert.equal(questionId(2, "build_target"), "question-2-build_target");
assert.equal(questionId(3, "Café"), "question-3-caf");
assert.equal(questionId(4, ""), "question-4");
assert.equal(questionId(5, "???"), "question-5");
assert.notEqual(questionId(0, "Which"), questionId(1, "Which"));
const named = obj(
first(
new Mapper().event({
type: "question.asked",
properties: {
id: "que_1",
sessionID: "ses_1",
questions: [
{ question: "Which database?", header: "Database", options: [] },
{ question: "Run it now?", header: "", options: [] },
],
},
}),
)["request"],
);
assert.deepEqual(
arr(named["fields"]).map((field: unknown) => obj(field)["id"]),
["question-0-database", "question-1"],
);
});
test("todos become a plan", () => {
const mapped = new Mapper().event({
type: "todo.updated",
properties: {
sessionID: "ses_1",
todos: [
{ content: "read the file", status: "completed", priority: "high" },
{ content: "write the fix", status: "in_progress", priority: "high" },
],
},
});
assert.deepEqual(first(mapped)["event"], {
event: "plan",
entries: [
{ content: "read the file", status: "completed" },
{ content: "write the fix", status: "in_progress" },
],
});
});
test("unknown events and parts are ignored", () => {
const mapper = new Mapper();
assert.deepEqual(mapper.event({ type: "plugin.added", properties: { id: "anthropic" } }), []);
assert.deepEqual(
mapper.event(
part("ses_1", { type: "patch", id: "prt_p", messageID: "msg_a", sessionID: "ses_1", hash: "abc", files: [] }),
),
[],
);
assert.deepEqual(mapper.event({ type: "server.heartbeat", properties: {} }), []);
});
test("history maps to transcript items", () => {
const messages = [
{
info: { id: "msg_u", role: "user", sessionID: "ses_1", time: { created: 1700000000000 } },
parts: [
{ type: "text", id: "prt_1", messageID: "msg_u", sessionID: "ses_1", text: "hello" },
{ type: "text", id: "prt_2", messageID: "msg_u", sessionID: "ses_1", text: "injected", synthetic: true },
],
},
{
info: { id: "msg_a", role: "assistant", sessionID: "ses_1", time: { created: 1700000001000 } },
parts: [
{ type: "reasoning", id: "prt_3", messageID: "msg_a", sessionID: "ses_1", text: "why" },
{
type: "tool",
id: "prt_4",
messageID: "msg_a",
sessionID: "ses_1",
callID: "c",
tool: "read",
state: {
status: "completed",
input: { filePath: "/w/a.txt" },
output: "body",
metadata: { preview: "body" },
title: "a.txt",
},
},
{ type: "text", id: "prt_5", messageID: "msg_a", sessionID: "ses_1", text: "done" },
],
},
];
const items = transcriptTree("ses_1", new Map([["ses_1", { messages, children: [] }]]));
assert.equal(items.length, 4);
assert.deepEqual(items[0], {
id: "msg_u",
createdAt: "2023-11-14T22:13:20.000Z",
role: "user",
blocks: [{ type: "text", text: "hello" }],
});
assert.equal(obj(items[1])["role"], "reasoning");
assert.equal(obj(items[1])["text"], "why");
assert.equal(obj(items[2])["role"], "tool");
assert.equal(obj(obj(items[2])["call"])["kind"], "read");
assert.equal(obj(items[3])["role"], "assistant");
assert.equal(obj(items[3])["text"], "done");
});
function taskPart(status: string, extra: Record<string, unknown> = {}): Part {
const state = {
status,
input: {
description: "Find the parser",
prompt: "Look for the parser and report its path.",
subagent_type: "explore",
},
title: "Find the parser",
metadata: {
parentSessionId: "ses_1",
sessionId: "ses_child",
model: { providerID: "opencode", modelID: "big-pickle" },
},
time: { start: 1700000000000 },
...extra,
};
return {
type: "tool",
id: "prt_task",
messageID: "msg_a",
sessionID: "ses_1",
callID: "call_t",
tool: "task",
state,
};
}
const links = (mapped: Mapped[]): TaskLink[] =>
mapped.flatMap((entry) => (entry.type === "task_link" ? [entry.link] : []));
test("a running task call links its subagent session", () => {
const mapper = new Mapper();
const mapped = mapper.event(part("ses_1", taskPart("running")));
assert.equal(eventOf(mapped)["event"], "tool_call_started");
assert.equal(eventOf(mapped)["kind"], "task");
const link = links(mapped)[0];
assert.equal(link.child, "ses_child");
assert.equal(link.parent, "ses_1");
assert.equal(link.toolCallId, "prt_task", "the transcript knows the call by its part id");
assert.equal(link.title, "Find the parser");
assert.equal(link.prompt, "Look for the parser and report its path.");
assert.equal(link.agent, "explore");
assert.equal(link.model, "big-pickle");
assert.equal(link.background, false);
assert.equal(link.startedAt, new Date(1700000000000).toISOString());
assert.equal(link.outcome, null);
// Before the server names the child there is nothing to link.
assert.deepEqual(links(mapper.event(part("ses_1", taskPart("pending", { metadata: {} })))), []);
});
test("a finished task call carries the answer without its wrapper", () => {
const mapper = new Mapper();
const output =
'<task id="ses_child" state="completed">\n<task_result>\nThe parser is in src/parse.rs.\n</task_result>\n</task>';
const link = links(
mapper.event(part("ses_1", taskPart("completed", { output, time: { start: 1700000000000, end: 1700000005000 } }))),
)[0];
assert.deepEqual(link.outcome, {
child: "ses_child",
status: "completed",
summary: "The parser is in src/parse.rs.",
});
assert.equal(link.endedAt, new Date(1700000005000).toISOString());
assert.equal(
links(mapper.event(part("ses_1", taskPart("error", { error: "Task cancelled" }))))[0]?.outcome?.status,
"cancelled",
);
assert.equal(
links(
mapper.event(part("ses_1", taskPart("error", { error: "Subagent failed (task_id: ses_child): rate limited" }))),
)[0]?.outcome?.status,
"failed",
);
});
test("a background task ends with the injected report", () => {
const mapper = new Mapper();
const started =
'<task id="ses_child" state="running">\n<summary>Background task started</summary>\n<task_result>\nThe task is working in the background.\n</task_result>\n</task>';
const link = links(
mapper.event(
part(
"ses_1",
taskPart("completed", {
output: started,
metadata: {
parentSessionId: "ses_1",
sessionId: "ses_child",
background: true,
jobId: "ses_child",
model: { providerID: "opencode", modelID: "big-pickle" },
},
}),
),
),
)[0];
assert.equal(link.background, true);
assert.equal(link.outcome, null, "the subagent is still running");
mapper.event({ type: "message.updated", properties: { info: { id: "msg_u2", role: "user" } } });
const report =
'<task id="ses_child" state="completed">\n<summary>Background task completed: Find the parser</summary>\n<task_result>\nFound it.\n</task_result>\n</task>';
assert.deepEqual(
mapper.event(
part("ses_1", {
type: "text",
id: "prt_r",
messageID: "msg_u2",
sessionID: "ses_1",
text: report,
synthetic: true,
}),
),
[{ type: "task_result", result: { child: "ses_child", status: "completed", summary: "Found it." } }],
);
assert.deepEqual(parseTaskOutput(report)?.summary, "Background task completed: Find the parser");
assert.equal(parseTaskOutput("plain text"), null);
});
test("task titles drop the subagent suffix", () => {
assert.equal(taskTitle("Find the parser (@explore subagent)"), "Find the parser");
assert.equal(taskTitle("Find (the) parser"), "Find (the) parser");
assert.equal(firstLine("\n Look here\nthen there"), "Look here");
});
test("a step is reported once, and a subagent's usage keeps the latest context and sums the cost", () => {
const mapper = new Mapper();
const step = part("ses_1", {
type: "step-finish",
id: "prt_s",
messageID: "msg_a",
sessionID: "ses_1",
tokens: { total: 100, input: 90, output: 10 },
cost: 0.25,
});
assert.equal(mapper.event(step).length, 1);
assert.deepEqual(mapper.event(step), []);
let total = addUsage(null, { usedTokens: 100, costUsd: 0.25 });
total = addUsage(total, { usedTokens: 180, costUsd: 0.5 });
assert.equal(total.usedTokens, 180, "the latest context, not a sum of contexts");
assert.equal(total.costUsd, 0.75);
assert.equal("costUsd" in addUsage(null, { usedTokens: 1 }), false);
});
test("history nests each subagent under its task call, at any depth", () => {
const answer =
'<task id="ses_child" state="completed">\n<task_result>\nThe parser is in src/parse.rs.\n</task_result>\n</task>';
const root = [
{
info: { id: "msg_u", role: "user", time: { created: 1700000000000 } },
parts: [{ type: "text", id: "prt_1", messageID: "msg_u", sessionID: "ses_1", text: "find the parser" }],
},
{
info: { id: "msg_a", role: "assistant", time: { created: 1700000001000 } },
parts: [
taskPart("completed", { output: answer, time: { start: 1700000001000, end: 1700000009000 } }),
{ type: "text", id: "prt_5", messageID: "msg_a", sessionID: "ses_1", text: "It is in src/parse.rs." },
],
},
];
const child = [
{
info: { id: "msg_cu", role: "user", time: { created: 1700000002000 } },
parts: [
{
type: "text",
id: "prt_c1",
messageID: "msg_cu",
sessionID: "ses_child",
text: "Look for the parser and report its path.",
},
],
},
{
info: {
id: "msg_ca",
role: "assistant",
modelID: "big-pickle",
variant: "high",
time: { created: 1700000003000, completed: 1700000008000 },
},
parts: [
{
type: "tool",
id: "prt_c2",
messageID: "msg_ca",
sessionID: "ses_child",
callID: "c2",
tool: "grep",
state: { status: "completed", input: { pattern: "parse" }, output: "src/parse.rs", title: "parse" },
},
{
type: "tool",
id: "prt_c3",
messageID: "msg_ca",
sessionID: "ses_child",
callID: "c3",
tool: "task",
state: {
status: "completed",
input: { description: "Read it", prompt: "Read src/parse.rs", subagent_type: "general" },
output: '<task id="ses_grand" state="completed">\n<task_result>\nIt parses.\n</task_result>\n</task>',
metadata: { sessionId: "ses_grand" },
},
},
{
type: "step-finish",
id: "prt_c4",
messageID: "msg_ca",
sessionID: "ses_child",
tokens: { total: 5000, input: 4900, output: 100 },
cost: 0.1,
},
{
type: "text",
id: "prt_c5",
messageID: "msg_ca",
sessionID: "ses_child",
text: "The parser is in src/parse.rs.",
},
],
},
];
const grand = [
{
info: { id: "msg_ga", role: "assistant", time: { created: 1700000004000, completed: 1700000005000 } },
parts: [{ type: "text", id: "prt_g1", messageID: "msg_ga", sessionID: "ses_grand", text: "It parses." }],
},
];
const orphan = {
id: "ses_orphan",
parentID: "ses_1",
title: "Check the lexer (@explore subagent)",
agent: "explore",
};
const sessions = new Map([
["ses_1", { messages: root, children: [orphan] }],
["ses_child", { messages: child, children: [] }],
["ses_grand", { messages: grand, children: [] }],
]);
const items = transcriptTree("ses_1", sessions);
assert.deepEqual(
items.map((item) => item.id),
["msg_u", "prt_task", "task-ses_child", "prt_5", "task-ses_orphan"],
);
const third = items[2];
assert.equal(third.role, "task");
if (third.role !== "task") throw new Error("expected a task item");
const { task, items: nested } = third;
assert.equal(task.id, "ses_child");
assert.equal(task.title, "Find the parser");
assert.equal(task.toolCallId, "prt_task");
assert.equal(task.parentTaskId, undefined);
assert.equal(task.status, "completed");
assert.equal(task.name, "explore");
assert.equal(task.prompt, "Look for the parser and report its path.");
assert.equal(task.model, "big-pickle");
assert.equal(task.effort, "high");
assert.equal(task.summary, "The parser is in src/parse.rs.");
assert.equal(task.toolUses, 2);
assert.equal(task.usage?.usedTokens, 5000);
assert.equal(task.startedAt, new Date(1700000001000).toISOString());
assert.equal(task.endedAt, new Date(1700000009000).toISOString());
assert.deepEqual(
nested?.map((item) => item.id),
["msg_cu", "prt_c2", "prt_c3", "task-ses_grand", "prt_c5"],
);
const grandItem = nested?.[3];
assert.ok(grandItem);
if (grandItem.role !== "task") throw new Error("expected a task item");
assert.equal(grandItem.task.parentTaskId, "ses_child");
assert.equal(grandItem.task.toolCallId, "prt_c3");
assert.equal(grandItem.task.title, "Read it");
assert.equal(grandItem.task.summary, "It parses.");
assert.equal(grandItem.items?.length, 1);
const orphanItem = items[4];
if (orphanItem.role !== "task") throw new Error("expected a task item");
const orphanTask = orphanItem.task;
assert.equal(orphanTask.title, "Check the lexer");
assert.equal(orphanTask.toolCallId, undefined);
assert.equal(orphanTask.name, "explore");
});
test("history takes a background answer from the report", () => {
const started =
'<task id="ses_child" state="running">\n<summary>Background task started</summary>\n<task_result>\nworking\n</task_result>\n</task>';
const report =
'<task id="ses_child" state="error">\n<summary>Background task failed: Find the parser</summary>\n<task_error>\nrate limited\n</task_error>\n</task>';
const root = [
{
info: { id: "msg_a", role: "assistant", time: { created: 1700000001000 } },
parts: [taskPart("completed", { output: started, metadata: { sessionId: "ses_child", background: true } })],
},
{
info: { id: "msg_u2", role: "user", time: { created: 1700000009000 } },
parts: [{ type: "text", id: "prt_r", messageID: "msg_u2", sessionID: "ses_1", text: report, synthetic: true }],
},
];
const items = transcriptTree("ses_1", new Map([["ses_1", { messages: root, children: [] }]]));
assert.equal(items.length, 2, "the report is not a user message");
const backgroundItem = items[1];
if (backgroundItem.role !== "task") throw new Error("expected a task item");
const { task, items: nested } = backgroundItem;
assert.equal(task.background, true);
assert.equal(task.status, "failed");
assert.equal(task.summary, "rate limited");
assert.deepEqual(nested, []);
});Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 94.8 KB | 5 permissions | Listed |
No comments yet.