repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
pi/agent/skills/pi-sessions/scripts/inspect.test.mjs
Rawimport assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import test from "node:test";
import { inspect } from "./inspect.mjs";
const script = fileURLToPath(new URL("./inspect.mjs", import.meta.url));
const stamp = "2026-09-01T12:00:00.000Z";
const entry = (id, parentId, type, rest = {}) => ({ id, parentId, type, timestamp: stamp, ...rest });
const message = (id, parentId, role, rest = {}) =>
entry(id, parentId, "message", {
message: { role, content: [{ type: "text", text: `${role} text` }], ...rest },
});
function fixture(t) {
const root = mkdtempSync(join(tmpdir(), "pi-sessions-test-"));
t.after(() => rmSync(root, { recursive: true, force: true }));
const cwd = join(root, "project");
const home = join(root, "home");
const agent = join(home, ".pi/agent");
const store = join(home, ".local/state/pi/sessions");
for (const dir of [cwd, agent, store]) mkdirSync(dir, { recursive: true });
const ctx = { cwd, home, env: {} };
function json(file, value) {
mkdirSync(dirname(file), { recursive: true });
writeFileSync(file, JSON.stringify(value));
}
function add(id, name = "task", entries = [message("u", null, "user")], options = {}) {
const file = join(options.dir ?? store, `${id}.jsonl`);
const header = {
type: "session",
version: 3,
id,
cwd: options.cwd ?? cwd,
timestamp: options.timestamp ?? stamp,
};
const records = [header, ...entries];
if (name !== null) records.push(entry("name", entries.at(-1)?.id ?? null, "session_info", { name }));
mkdirSync(dirname(file), { recursive: true });
writeFileSync(file, `${records.map((e) => JSON.stringify(e)).join("\n")}\n`);
return file;
}
return { root, cwd, home, agent, store, ctx, json, add };
}
test("storage precedence: CLI, environment, project, global, then ordered fallbacks", async (t) => {
const f = fixture(t);
for (const id of ["cli", "env", "project", "global"]) f.add(id, id, [], { dir: join(f.cwd, id) });
f.json(join(f.agent, "settings.json"), { sessionDir: "global" });
f.json(join(f.cwd, ".pi/settings.json"), { sessionDir: "project" });
assert.equal((await inspect(["list"], f.ctx)).sessions[0].id, "project");
f.ctx.env.PI_CODING_AGENT_SESSION_DIR = "env";
assert.equal((await inspect(["list"], f.ctx)).sessions[0].id, "env");
assert.equal((await inspect(["list", "--session-dir", "cli"], f.ctx)).sessions[0].id, "cli");
delete f.ctx.env.PI_CODING_AGENT_SESSION_DIR;
rmSync(join(f.cwd, ".pi/settings.json"));
assert.equal((await inspect(["list"], f.ctx)).sessions[0].id, "global");
f.json(join(f.agent, "settings.json"), { sessionDir: "missing" });
f.add("fallback");
assert.equal((await inspect(["list"], f.ctx)).total, 0, "configured empty roots never fall through");
rmSync(join(f.agent, "settings.json"));
assert.equal((await inspect(["list"], f.ctx)).sessions[0].id, "fallback");
rmSync(f.store, { recursive: true });
f.add("agent", "agent", [], { dir: join(f.agent, "sessions/nested") });
f.add("local", "local", [], { dir: join(f.cwd, ".pi/sessions") });
assert.equal((await inspect(["list"], f.ctx)).sessions[0].id, "agent");
rmSync(join(f.agent, "sessions"), { recursive: true });
assert.equal((await inspect(["list"], f.ctx)).sessions[0].id, "local");
});
test("agent directory and tilde settings resolve without loading other settings fields", async (t) => {
const f = fixture(t);
const agent = join(f.root, "agent override");
f.json(join(agent, "settings.json"), { sessionDir: "~/sessions", irrelevant: "NEVER_EMIT_SETTINGS_CONTENT" });
f.ctx.env.PI_CODING_AGENT_DIR = agent;
f.add("custom", "custom", [], { dir: join(f.home, "sessions") });
const result = await inspect(["list"], f.ctx);
assert.equal(result.sessions[0].id, "custom");
assert.doesNotMatch(JSON.stringify(result), /NEVER_EMIT_SETTINGS_CONTENT/);
});
test("list filters literal names, exact cwd, UTC dates and pages with stable ordering", async (t) => {
const f = fixture(t);
f.add("bbb", "same [literal]", [], { timestamp: "2026-09-02T12:00:00Z" });
f.add("aaa", "same [literal]", [], { timestamp: "2026-09-02T12:00:00Z" });
f.add("old", "same [literal]", [], { timestamp: "2026-08-31T12:00:00Z" });
f.add("other", "another", [], { cwd: join(f.cwd, "other") });
const r = await inspect(
[
"list",
"[literal]",
"--cwd",
f.cwd,
"--since",
"2026-09-01",
"--until",
"2026-09-02",
"--offset",
"1",
"--limit",
"1",
],
f.ctx,
);
assert.equal(r.total, 2);
assert.equal(r.sessions.length, 1);
assert.equal(r.sessions[0].id, "bbb");
f.add("utc", "offset date", [], { timestamp: "2026-09-01T01:00:00+02:00" });
const utc = await inspect(["list", "offset date", "--until", "2026-08-31"], f.ctx);
assert.equal(utc.total, 1);
assert.equal(utc.sessions[0].started, "2026-08-31T23:00:00.000Z");
await assert.rejects(inspect(["list", "--since", "2026-02-30"], f.ctx), /date/i);
await assert.rejects(inspect(["list", "--limit", "101"], f.ctx), /limit/i);
await assert.rejects(inspect(["list", "--offset", "-1"], f.ctx), /offset/i);
});
test("scoped lists skip only valid headers proven outside date and cwd bounds", async (t) => {
const f = fixture(t);
f.add("inside", "inside", [], { timestamp: "2026-09-01T00:00:00Z" });
const before = f.add("before", "before", [], { timestamp: "2026-08-31T23:59:59Z" });
const after = f.add("after", "after", [], { timestamp: "2026-09-03T00:00:00Z" });
const other = f.add("other", "other", [], { cwd: join(f.cwd, "other") });
for (const file of [before, after, other])
writeFileSync(file, `${readFileSync(file, "utf8")}{"MALFORMED_BODY":`);
const scoped = await inspect(
["list", "--cwd", f.cwd, "--since", "2026-09-01", "--until", "2026-09-02"],
f.ctx,
);
assert.equal(scoped.complete, true);
assert.equal(scoped.scanned, 4);
assert.equal(scoped.parsed, 1);
assert.equal(scoped.skipped, 3);
assert.equal(scoped.errorCount, 0);
assert.equal(scoped.sessions[0].id, "inside");
assert.equal(scoped.scanned, scoped.parsed + scoped.skipped + scoped.errorCount);
const unscoped = await inspect(["list"], f.ctx);
assert.equal(unscoped.complete, false);
assert.equal(unscoped.scanned, 4);
assert.equal(unscoped.parsed, 1);
assert.equal(unscoped.skipped, 0);
assert.equal(unscoped.errorCount, 3);
assert.equal(unscoped.scanned, unscoped.parsed + unscoped.skipped + unscoped.errorCount);
await assert.rejects(inspect(["show", before, "--since", "2026-09-01"], f.ctx), /line 3/);
});
test("scoped lists retain unknown and in-scope errors before query filtering", async (t) => {
const f = fixture(t);
f.add("name-hit", "wanted by name", [], { timestamp: "2026-09-01T12:00:00Z" });
const broken = f.add("broken", "unrelated", [], { timestamp: "2026-09-01T12:00:00Z" });
writeFileSync(broken, `${readFileSync(broken, "utf8")}{"MALFORMED_BODY":`);
const unknown = f.add("unknown", "unknown", [], { timestamp: "2026-08-01T12:00:00Z" });
f.json(unknown, {
type: "session",
version: 99,
id: "unknown",
cwd: f.cwd,
timestamp: "2026-08-01T12:00:00Z",
});
const result = await inspect(["list", "wanted by name", "--since", "2026-09-01"], f.ctx);
assert.equal(result.complete, false);
assert.equal(result.scanned, 3);
assert.equal(result.parsed, 1);
assert.equal(result.skipped, 0);
assert.equal(result.errorCount, 2);
assert.equal(result.sessions[0].id, "name-hit");
assert.match(result.errors.find((error) => error.file === broken).error, /line 3/);
assert.match(result.errors.find((error) => error.file === unknown).error, /version/i);
assert.equal(result.scanned, result.parsed + result.skipped + result.errorCount);
});
test("duplicate names and ID prefixes require disambiguation; discovered paths work", async (t) => {
const f = fixture(t);
const file = f.add("abc-1", "duplicate");
f.add("abc-2", "duplicate");
await assert.rejects(inspect(["show", "duplicate"], f.ctx), /ambiguous/i);
await assert.rejects(inspect(["show", "abc"], f.ctx), /ambiguous/i);
assert.equal((await inspect(["show", "abc-1"], f.ctx)).id, "abc-1");
assert.equal((await inspect(["show", file], f.ctx)).file, file);
await assert.rejects(inspect(["show", "missing"], f.ctx), /not found/i);
});
test("persisted branch excludes abandoned errors; explicit leaf recovers them", async (t) => {
const f = fixture(t);
f.add("branches", null, [
message("u", null, "user"),
message("old", "u", "assistant", { stopReason: "error" }),
message("new", "u", "assistant", {
stopReason: "stop",
content: [{ type: "toolCall", id: "call1", name: "read", arguments: { path: "ARGUMENT_MUST_NOT_APPEAR" } }],
}),
message("result", "new", "toolResult", { toolName: "read", isError: true }),
message("abort", "result", "assistant", { stopReason: "aborted" }),
entry("exec", "abort", "message", {
message: { role: "bashExecution", exitCode: 1, output: "OUTPUT_MUST_NOT_APPEAR" },
}),
]);
const r = await inspect(["show", "branches"], f.ctx);
assert.equal(r.leafId, "exec");
assert.equal(r.heads.total, 2);
assert.equal(r.branch.counts.assistantErrors, 0);
assert.equal(r.branch.counts.toolErrors, 1);
assert.equal(r.branch.counts.toolCalls, 1);
assert.equal(r.branch.counts.aborts, 1);
assert.equal(r.branch.counts.bashErrors, 1);
assert.deepEqual(
r.timeline.map((e) => e.id),
["u", "new", "result", "abort", "exec"],
);
assert.equal(r.timeline[1].line, 4);
const old = await inspect(["show", "branches", "--leaf", "old"], f.ctx);
assert.equal(old.branch.counts.assistantErrors, 1);
assert.equal(old.counts.assistantErrors, 1);
assert.doesNotMatch(JSON.stringify(r), /ARGUMENT_MUST_NOT_APPEAR|OUTPUT_MUST_NOT_APPEAR|user text/);
await assert.rejects(inspect(["show", "branches", "--leaf", "unknown"], f.ctx), /leaf/i);
});
test("retained-tail compaction supplies context once, without inflating branch turns", async (t) => {
const f = fixture(t);
f.add("tail", null, [
message("u1", null, "user"),
message("u2", "u1", "user"),
entry("c", "u2", "compaction", {
summary: "SUMMARY_TEXT",
tokensBefore: 9000,
retainedTail: [
{ role: "user", content: "retained user" },
{ role: "assistant", content: [{ type: "text", text: "retained answer" }] },
],
}),
message("a", "c", "assistant"),
]);
const r = await inspect(["show", "tail", "--view", "context", "--text"], f.ctx);
assert.equal(r.branch.counts.users, 2);
assert.equal(r.context.counts.users, 1);
assert.equal(r.context.counts.assistants, 2);
assert.deepEqual(
r.timeline.map((e) => e.id),
["c", "c:tail:0", "c:tail:1", "a"],
);
assert.equal(r.timeline[0].text, "SUMMARY_TEXT");
assert.equal(r.timeline[1].line, 4);
assert.equal(r.timeline[1].text, "retained user");
});
test("firstKeptEntryId and branch summaries reconstruct the documented context order", async (t) => {
const f = fixture(t);
f.add("kept", null, [
message("u1", null, "user"),
message("u2", "u1", "user"),
entry("c", "u2", "compaction", { summary: "summary", tokensBefore: 5000, firstKeptEntryId: "u2" }),
entry("b", "c", "branch_summary", { summary: "branch text", fromId: "u1" }),
message("a", "b", "assistant"),
]);
const r = await inspect(["show", "kept", "--view", "context"], f.ctx);
assert.deepEqual(
r.timeline.map((e) => e.id),
["c", "u2", "b", "a"],
);
assert.equal(r.context.counts.users, 1);
assert.equal(r.context.counts.compactions, 1);
});
test("text is bounded while content exposes exact persisted payloads on request", async (t) => {
const f = fixture(t);
f.add("bounded", null, [
message("u", null, "user", { content: "x".repeat(10000) }),
message("a", "u", "assistant", {
errorMessage: "EXACT_ERROR",
content: [
{ type: "thinking", thinking: "EXACT_THINKING" },
{ type: "image", data: "EXACT_IMAGE", mimeType: "image/png" },
{ type: "text", text: "visible answer" },
{ type: "toolCall", name: "bash", id: "t", arguments: { command: "EXACT_ARGUMENTS" } },
],
}),
message("r", "a", "toolResult", {
content: [{ type: "text", text: "EXACT_RESULT" }],
details: { reason: "EXACT_DETAILS" },
}),
entry("custom", "r", "custom", { customType: "state", data: "EXACT_STATE" }),
]);
const r = await inspect(["show", "bounded", "--text", "--limit", "1"], f.ctx);
assert.equal(r.total, 4);
assert.equal(r.timeline.length, 1);
assert.ok(r.timeline[0].text.length <= 500);
assert.equal(r.timeline[0].textTruncated, true);
const text = await inspect(["show", "bounded", "--text"], f.ctx);
assert.doesNotMatch(JSON.stringify(text), /EXACT_/);
assert.equal(text.timeline[1].text, "visible answer");
const content = await inspect(["show", "bounded", "--content"], f.ctx);
assert.equal(content.timeline[1].payload.message.errorMessage, "EXACT_ERROR");
assert.equal(content.timeline[1].payload.message.content[0].thinking, "EXACT_THINKING");
assert.equal(content.timeline[1].payload.message.content[1].data, "EXACT_IMAGE");
assert.equal(content.timeline[1].payload.message.content[3].arguments.command, "EXACT_ARGUMENTS");
assert.equal(content.timeline[2].payload.message.content[0].text, "EXACT_RESULT");
assert.equal(content.timeline[2].payload.message.details.reason, "EXACT_DETAILS");
assert.equal(content.timeline[3].payload.data, "EXACT_STATE");
const page = await inspect(["show", "bounded", "--offset", "1", "--limit", "1"], f.ctx);
assert.equal(page.timeline[0].id, "a");
assert.equal(page.timeline[0].text, undefined);
assert.equal(page.timeline[0].payload, undefined);
});
test("malformed lines and partial tails report locations, never parser content; listing is explicitly partial", async (t) => {
const f = fixture(t);
f.add("valid");
const broken = f.add("broken", null, []);
writeFileSync(broken, `${readFileSync(broken, "utf8")}{"NEVER_ECHO_BAD_JSON":`);
const r = await inspect(["list"], f.ctx);
assert.equal(r.complete, false);
assert.equal(r.errorCount, 1);
assert.equal(r.sessions[0].id, "valid");
assert.match(r.errors[0].error, /line 2/);
assert.doesNotMatch(JSON.stringify(r), /NEVER_ECHO_BAD_JSON/);
await assert.rejects(inspect(["show", broken], f.ctx), /line 2/);
const settings = join(f.agent, "settings.json");
writeFileSync(settings, '{"NEVER_ECHO_SETTINGS":');
await assert.rejects(
inspect(["list"], f.ctx),
(error) => /settings/.test(error.message) && !error.message.includes("NEVER_ECHO_SETTINGS"),
);
});
test("unsupported versions, duplicate IDs and missing parents fail, rather than guessing", async (t) => {
const f = fixture(t);
const file = f.add("invalid", null, [message("u", "absent", "user")]);
await assert.rejects(inspect(["show", file], f.ctx), /parent/i);
f.add("invalid", null, [message("u", null, "user"), message("u", null, "user")]);
await assert.rejects(inspect(["show", file], f.ctx), /duplicate/i);
f.json(file, { type: "session", version: 99, id: "invalid", cwd: f.cwd, timestamp: stamp });
await assert.rejects(inspect(["show", file], f.ctx), /version/i);
});
test("latest compaction wins, including an empty retained tail", async (t) => {
const f = fixture(t);
f.add("latest", null, [
message("u", null, "user"),
entry("c1", "u", "compaction", { summary: "old", retainedTail: [{ role: "user", content: "old user" }] }),
entry("c2", "c1", "compaction", { summary: "new", retainedTail: [], firstKeptEntryId: "u" }),
]);
const r = await inspect(["show", "latest", "--view", "context"], f.ctx);
assert.deepEqual(
r.timeline.map((e) => e.id),
["c2"],
);
assert.equal(r.context.counts.users, 0);
assert.equal(r.branch.counts.compactions, 2);
});
test("invalid compaction metadata is rejected without serializing its payload", async (t) => {
const f = fixture(t);
const file = f.add("bad-compaction", null, [
entry("c", null, "compaction", { retainedTail: [], firstKeptEntryId: { value: "HIDDEN_METADATA" } }),
]);
await assert.rejects(
inspect(["show", file], f.ctx),
(e) => /line 2/.test(e.message) && !e.message.includes("HIDDEN_METADATA"),
);
f.add("bad-compaction", null, [entry("c", null, "compaction", { firstKeptEntryId: "absent" })]);
await assert.rejects(inspect(["show", file], f.ctx), (e) => e.message.includes(file) && /line 2/.test(e.message));
});
test("empty stores, header-only sessions and valid unterminated final lines are read-only", async (t) => {
const f = fixture(t);
assert.equal((await inspect(["list"], f.ctx)).total, 0);
const file = f.add("header", null, []);
const text = readFileSync(file, "utf8").trimEnd();
writeFileSync(file, text);
const result = await inspect(["show", file], f.ctx);
assert.equal(result.total, 0);
assert.equal(result.leafId, null);
assert.equal(result.scanned, 1);
assert.equal(readFileSync(file, "utf8"), text);
writeFileSync(file, "");
await assert.rejects(inspect(["show", file], f.ctx), /empty session/);
});
test("file symlinks are not followed and the input file is unchanged", async (t) => {
const f = fixture(t);
const file = f.add("real");
const before = readFileSync(file);
const time = statSync(file).mtimeMs;
if (process.platform !== "win32") symlinkSync(file, join(f.store, "alias.jsonl"));
const r = await inspect(["list"], f.ctx);
assert.equal(r.total, 1);
await inspect(["show", file, "--view", "context", "--text"], f.ctx);
assert.deepEqual(readFileSync(file), before);
assert.equal(statSync(file).mtimeMs, time);
});
test("CLI prints JSON, reports partial failures through exit status, and rejects unsupported options", (t) => {
const f = fixture(t);
f.add("cli");
const args = [script, "list", "--session-dir", f.store];
const env = { ...process.env, PI_CODING_AGENT_DIR: f.agent, PI_CODING_AGENT_SESSION_DIR: "" };
const good = spawnSync(process.execPath, args, { cwd: f.cwd, env, encoding: "utf8" });
assert.equal(good.status, 0, good.stderr);
assert.equal(JSON.parse(good.stdout).sessions[0].id, "cli");
writeFileSync(join(f.store, "broken.jsonl"), "bad");
const partial = spawnSync(process.execPath, args, { cwd: f.cwd, env, encoding: "utf8" });
assert.equal(partial.status, 1);
assert.equal(JSON.parse(partial.stdout).complete, false);
const invalid = spawnSync(process.execPath, [script, "--unsupported"], { encoding: "utf8" });
assert.equal(invalid.status, 1);
assert.ok(JSON.parse(invalid.stderr).error);
const help = spawnSync(process.execPath, [script, "--help"], { encoding: "utf8" });
assert.equal(help.status, 0);
assert.match(help.stdout, /show/);
if (process.platform !== "win32") {
const linkedScript = join(f.cwd, "inspect-link.mjs");
symlinkSync(script, linkedScript);
const linked = spawnSync(process.execPath, [linkedScript, "--help"], { encoding: "utf8" });
assert.equal(linked.status, 0);
assert.match(linked.stdout, /show/);
}
});