feat: Claude Code Monitor — lanes, pipelines and a merged workspace

Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
This commit is contained in:
2026-07-29 17:07:45 +07:00
commit f78c7f9a2e
783 changed files with 221663 additions and 0 deletions
@@ -0,0 +1,754 @@
/**
* @file Tests for subagent tool-event attribution.
*
* Subagent tool calls (Read, Bash, Edit, etc.) never fire hooks on the
* parent session — they only show up in the subagent's own JSONL file.
* Without dedicated extraction, every subagent ends up with at most a
* single spawn event, leaving 561/561 historical subagents with 05
* events instead of the dozens-to-hundreds they actually performed.
*
* This suite verifies that:
* 1. parseSubagentFile pairs tool_use blocks with their tool_result
* counterparts and surfaces them as `toolEvents`.
* 2. importSubagentFromJsonl emits PreToolUse + PostToolUse events
* under the subagent's own `agent_id`, so the UI attributes them
* to the subagent rather than the main agent.
* 3. Re-running the import is idempotent — no duplicate event rows.
* 4. When a live subagent (created via PreToolUse "Agent" hook) matches
* the JSONL by type + start time, events attach to the live row
* instead of creating a duplicate JSONL-keyed row.
*
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { describe, it, before, after } = require("node:test");
const assert = require("node:assert/strict");
const path = require("path");
const fs = require("fs");
const os = require("os");
const TEST_DB = path.join(os.tmpdir(), `dashboard-subagent-test-${Date.now()}-${process.pid}.db`);
process.env.DASHBOARD_DB_PATH = TEST_DB;
const dbModule = require("../db");
const { db, stmts } = dbModule;
const importHistory = require("../../scripts/import-history");
const { calculateCost } = require("../routes/pricing");
after(() => {
if (db) db.close();
for (const suffix of ["", "-wal", "-shm"]) {
try {
fs.unlinkSync(TEST_DB + suffix);
} catch {
/* ignore */
}
}
});
// ── Fixture helpers ──────────────────────────────────────────────────
function writeSubagentJsonl(filePath, lines) {
fs.writeFileSync(filePath, lines.map((o) => JSON.stringify(o)).join("\n"));
}
/**
* Builds a minimal subagent JSONL with two tool calls — one Read with a
* paired tool_result, and one Bash with a paired error tool_result.
*/
function buildSubagentLines(agentType = "coder") {
return [
{
type: "user",
timestamp: "2026-04-28T10:00:00.000Z",
message: { content: [{ type: "text", text: "Investigate the bug" }] },
},
{
type: "assistant",
timestamp: "2026-04-28T10:00:01.000Z",
message: {
model: "claude-opus-4-7",
content: [
{
type: "tool_use",
id: "toolu_read_001",
name: "Read",
input: { file_path: "/tmp/foo.py" },
},
],
usage: {
input_tokens: 50,
output_tokens: 20,
cache_read_input_tokens: 0,
cache_creation_input_tokens: 0,
},
},
},
{
type: "user",
timestamp: "2026-04-28T10:00:02.000Z",
message: {
content: [
{
type: "tool_result",
tool_use_id: "toolu_read_001",
content: "def main():\n pass\n",
},
],
},
},
{
type: "assistant",
timestamp: "2026-04-28T10:00:03.000Z",
message: {
model: "claude-opus-4-7",
content: [
{
type: "tool_use",
id: "toolu_bash_002",
name: "Bash",
input: { command: "ls /tmp" },
},
],
},
},
{
type: "user",
timestamp: "2026-04-28T10:00:04.000Z",
message: {
content: [
{
type: "tool_result",
tool_use_id: "toolu_bash_002",
content: "ls: cannot access /tmp: not allowed",
is_error: true,
},
],
},
},
{
// Meta: keeps file timestamps coherent
type: "user",
timestamp: "2026-04-28T10:00:05.000Z",
message: { content: [{ type: "text", text: "done" }] },
},
].map((line, i) => {
// Inject agentType into one entry as a hint, mirroring real CC output
if (i === 0) line.agentType = agentType;
return line;
});
}
function writeMetaJson(filePath, agentType) {
fs.writeFileSync(filePath, JSON.stringify({ agentType }));
}
/**
* Minimal subagent JSONL on a specific model carrying one usage record, so
* parseSubagentFile yields a single token bucket keyed by that model.
*/
function buildModelSubLines(agentType, model, usage, startedAt = "2026-05-01T10:00:00.000Z") {
const t0 = startedAt;
return [
{
type: "user",
timestamp: t0,
agentType,
message: { content: [{ type: "text", text: "go" }] },
},
{
type: "assistant",
timestamp: "2026-05-01T10:00:01.000Z",
message: {
model,
content: [{ type: "tool_use", id: `toolu_${agentType}_1`, name: "Read", input: {} }],
usage: {
input_tokens: usage.input || 0,
output_tokens: usage.output || 0,
cache_read_input_tokens: usage.cacheRead || 0,
cache_creation_input_tokens: usage.cacheWrite || 0,
},
},
},
{
type: "user",
timestamp: "2026-05-01T10:00:02.000Z",
message: {
content: [{ type: "tool_result", tool_use_id: `toolu_${agentType}_1`, content: "ok" }],
},
},
];
}
// Lay out <base>/<sessionId>/subagents/agent-*.jsonl and return the matching
// transcriptPath (<base>/<sessionId>.jsonl) that scanAndImportSubagents expects.
function buildSubagentDir(sessionId, files) {
const base = fs.mkdtempSync(path.join(os.tmpdir(), `sa-scan-${process.pid}-`));
const subDir = path.join(base, sessionId, "subagents");
fs.mkdirSync(subDir, { recursive: true });
for (const f of files) {
writeSubagentJsonl(path.join(subDir, `agent-${f.id}.jsonl`), f.lines);
// Companion meta.json so parseSubagentFile resolves agentType (the live-match key).
if (f.agentType) {
fs.writeFileSync(
path.join(subDir, `agent-${f.id}.meta.json`),
JSON.stringify({ agentType: f.agentType })
);
}
}
return { transcriptPath: path.join(base, `${sessionId}.jsonl`), base };
}
// ── Tests ────────────────────────────────────────────────────────────
describe("parseSubagentFile — tool event extraction", () => {
it("pairs tool_use with tool_result and returns ordered toolEvents", async () => {
const tmpFile = path.join(os.tmpdir(), `agent-${Date.now()}-${process.pid}.jsonl`);
writeSubagentJsonl(tmpFile, buildSubagentLines("coder"));
writeMetaJson(tmpFile.replace(/\.jsonl$/, ".meta.json"), "coder");
try {
const data = await importHistory.parseSubagentFile(tmpFile);
assert.ok(data, "subagent data should parse");
assert.equal(data.agentType, "coder");
assert.ok(Array.isArray(data.toolEvents));
assert.equal(data.toolEvents.length, 2);
const [readEv, bashEv] = data.toolEvents;
assert.equal(readEv.tool_use_id, "toolu_read_001");
assert.equal(readEv.tool_name, "Read");
assert.deepEqual(readEv.tool_input, { file_path: "/tmp/foo.py" });
assert.equal(readEv.is_error, false);
assert.equal(typeof readEv.pre_timestamp, "string");
assert.equal(typeof readEv.post_timestamp, "string");
assert.ok(readEv.tool_response);
assert.equal(bashEv.tool_use_id, "toolu_bash_002");
assert.equal(bashEv.is_error, true);
} finally {
fs.unlinkSync(tmpFile);
try {
fs.unlinkSync(tmpFile.replace(/\.jsonl$/, ".meta.json"));
} catch {
/* ignore */
}
}
});
it("emits a tool_use even when no matching tool_result exists yet (live tail)", async () => {
const tmpFile = path.join(os.tmpdir(), `agent-tail-${Date.now()}-${process.pid}.jsonl`);
writeSubagentJsonl(tmpFile, [
{
type: "assistant",
timestamp: "2026-04-28T10:00:01.000Z",
message: {
model: "claude-opus-4-7",
content: [{ type: "tool_use", id: "toolu_pending", name: "Read", input: {} }],
},
},
]);
try {
const data = await importHistory.parseSubagentFile(tmpFile);
assert.equal(data.toolEvents.length, 1);
const ev = data.toolEvents[0];
assert.equal(ev.tool_use_id, "toolu_pending");
assert.equal(ev.post_timestamp, null);
assert.equal(ev.tool_response, null);
} finally {
fs.unlinkSync(tmpFile);
}
});
});
describe("importSubagentFromJsonl — event attribution", () => {
const sessionId = "test-sess-attribution";
const mainAgentId = `${sessionId}-main`;
before(() => {
// Seed session + main agent so importSubagentFromJsonl has parents to point at.
stmts.insertSession.run(sessionId, "Test Session", "active", "/tmp", null, null);
stmts.insertAgent.run(
mainAgentId,
sessionId,
"Main Agent",
"main",
null,
"waiting",
null,
null,
null
);
});
it("creates one subagent row and per-call PreToolUse + PostToolUse events", async () => {
const tmpFile = path.join(os.tmpdir(), `agent-attr-${Date.now()}-${process.pid}.jsonl`);
writeSubagentJsonl(tmpFile, buildSubagentLines("coder"));
writeMetaJson(tmpFile.replace(/\.jsonl$/, ".meta.json"), "coder");
try {
const data = await importHistory.parseSubagentFile(tmpFile);
const created = importHistory.importSubagentFromJsonl(dbModule, sessionId, mainAgentId, data);
assert.ok(created > 0, "should create at least the agent + spawn + 4 events");
const subId = `${sessionId}-jsonl-${data.agentId}`;
const subAgent = stmts.getAgent.get(subId);
assert.ok(subAgent, "JSONL-keyed subagent row should exist");
assert.equal(subAgent.parent_agent_id, mainAgentId);
const toolEvents = db
.prepare(
"SELECT event_type, tool_name FROM events WHERE agent_id = ? AND event_type IN ('PreToolUse', 'PostToolUse') ORDER BY id ASC"
)
.all(subId);
assert.equal(toolEvents.length, 4, "expected 2 Pre + 2 Post events under subagent's id");
assert.deepEqual(
toolEvents.map((e) => `${e.event_type}:${e.tool_name}`),
["PreToolUse:Read", "PostToolUse:Read", "PreToolUse:Bash", "PostToolUse:Bash"]
);
// Spawn marker lives under the main agent so the parent chain shows
// "Subagent spawned: coder" alongside main's other actions.
const spawnEvents = db
.prepare(
"SELECT 1 FROM events WHERE agent_id = ? AND event_type = 'PreToolUse' AND tool_name = 'Agent'"
)
.all(mainAgentId);
assert.equal(spawnEvents.length, 1);
} finally {
fs.unlinkSync(tmpFile);
try {
fs.unlinkSync(tmpFile.replace(/\.jsonl$/, ".meta.json"));
} catch {
/* ignore */
}
}
});
it("is idempotent — re-running does not duplicate events", async () => {
const tmpFile = path.join(os.tmpdir(), `agent-idem-${Date.now()}-${process.pid}.jsonl`);
writeSubagentJsonl(tmpFile, buildSubagentLines("reviewer"));
writeMetaJson(tmpFile.replace(/\.jsonl$/, ".meta.json"), "reviewer");
try {
const data = await importHistory.parseSubagentFile(tmpFile);
importHistory.importSubagentFromJsonl(dbModule, sessionId, mainAgentId, data);
const subId = `${sessionId}-jsonl-${data.agentId}`;
const before = db.prepare("SELECT COUNT(*) AS c FROM events WHERE agent_id = ?").get(subId).c;
// Second run — should be a no-op.
importHistory.importSubagentFromJsonl(dbModule, sessionId, mainAgentId, data);
const after = db.prepare("SELECT COUNT(*) AS c FROM events WHERE agent_id = ?").get(subId).c;
assert.equal(after, before, "idempotent re-import — no new rows");
} finally {
fs.unlinkSync(tmpFile);
try {
fs.unlinkSync(tmpFile.replace(/\.jsonl$/, ".meta.json"));
} catch {
/* ignore */
}
}
});
it("merges into a live subagent when one matches — no JSONL-keyed duplicate row", async () => {
// Simulate a live PreToolUse Agent hook having pre-created a subagent row.
const liveSubId = "live-uuid-xyz";
const startedAt = "2026-04-28T10:00:00.000Z";
stmts.insertAgent.run(
liveSubId,
sessionId,
"Live Coder",
"subagent",
"live-coder",
"completed",
"task",
mainAgentId,
null
);
db.prepare("UPDATE agents SET started_at = ?, ended_at = ?, updated_at = ? WHERE id = ?").run(
startedAt,
startedAt,
startedAt,
liveSubId
);
const tmpFile = path.join(os.tmpdir(), `agent-live-${Date.now()}-${process.pid}.jsonl`);
const lines = buildSubagentLines("live-coder");
writeSubagentJsonl(tmpFile, lines);
writeMetaJson(tmpFile.replace(/\.jsonl$/, ".meta.json"), "live-coder");
try {
const data = await importHistory.parseSubagentFile(tmpFile);
importHistory.importSubagentFromJsonl(dbModule, sessionId, mainAgentId, data);
const jsonlSubId = `${sessionId}-jsonl-${data.agentId}`;
assert.equal(
stmts.getAgent.get(jsonlSubId),
undefined,
"no JSONL-keyed row when a live subagent absorbed the events"
);
const eventsUnderLive = db
.prepare(
"SELECT 1 FROM events WHERE agent_id = ? AND event_type IN ('PreToolUse', 'PostToolUse')"
)
.all(liveSubId);
assert.ok(eventsUnderLive.length >= 4, "events should attach to the live subagent's id");
} finally {
fs.unlinkSync(tmpFile);
try {
fs.unlinkSync(tmpFile.replace(/\.jsonl$/, ".meta.json"));
} catch {
/* ignore */
}
}
});
});
// ── Per-subagent model token attribution (issue #185) ──────────────────
describe("scanAndImportSubagents — per-subagent model token attribution", () => {
it("buckets each subagent's tokens under its OWN model, skipping the parent model", async () => {
const sessionId = "sess-185-tiered";
// Orchestrator on Opus; subagents tiered to Sonnet + Haiku, plus one on the
// SAME model as the parent (Opus) to verify that bucket is intentionally skipped.
stmts.insertSession.run(sessionId, "Tiered", "active", "/tmp", "claude-opus-4-8", null);
stmts.insertAgent.run(
`${sessionId}-main`,
sessionId,
"Main",
"main",
null,
"working",
null,
null,
null
);
const { transcriptPath, base } = buildSubagentDir(sessionId, [
{
id: "hq",
lines: buildModelSubLines("qa", "claude-haiku-4-5-20251001", { input: 1000, output: 500 }),
},
{
id: "se",
lines: buildModelSubLines("engineer", "claude-sonnet-4-6", { input: 2000, output: 800 }),
},
{
id: "op",
lines: buildModelSubLines("planner", "claude-opus-4-8", { input: 9999, output: 9999 }),
},
]);
try {
await importHistory.scanAndImportSubagents(dbModule, sessionId, transcriptPath);
const rows = stmts.getTokensBySession.all(sessionId);
const byModel = Object.fromEntries(rows.map((r) => [r.model, r]));
const models = Object.keys(byModel).sort();
// Haiku + Sonnet buckets are written under their own models.
assert.deepEqual(models, ["claude-haiku-4-5-20251001", "claude-sonnet-4-6"]);
assert.equal(byModel["claude-haiku-4-5-20251001"].input_tokens, 1000);
assert.equal(byModel["claude-haiku-4-5-20251001"].output_tokens, 500);
assert.equal(byModel["claude-sonnet-4-6"].input_tokens, 2000);
assert.equal(byModel["claude-sonnet-4-6"].output_tokens, 800);
// The Opus subagent's tokens are NOT written here — that bucket belongs to
// the main-transcript writer; double-writing it would inflate via baseline.
assert.equal(byModel["claude-opus-4-8"], undefined, "parent-model bucket must be skipped");
// Cost is priced at the real (cheaper) per-subagent models, never Opus.
const cost = calculateCost(rows, stmts.listPricing.all());
const costModels = cost.breakdown.map((b) => b.model).sort();
assert.deepEqual(costModels, ["claude-haiku-4-5-20251001", "claude-sonnet-4-6"]);
assert.ok(cost.total_cost > 0);
// Each subagent row records its real model in metadata.
const hq = stmts.getAgent.get(`${sessionId}-jsonl-hq`);
assert.equal(JSON.parse(hq.metadata).model, "claude-haiku-4-5-20251001");
const se = stmts.getAgent.get(`${sessionId}-jsonl-se`);
assert.equal(JSON.parse(se.metadata).model, "claude-sonnet-4-6");
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
it("re-running does not inflate token buckets (idempotent per-model write)", async () => {
const sessionId = "sess-185-idem";
stmts.insertSession.run(sessionId, "Idem", "active", "/tmp", "claude-opus-4-8", null);
stmts.insertAgent.run(
`${sessionId}-main`,
sessionId,
"Main",
"main",
null,
"working",
null,
null,
null
);
const { transcriptPath, base } = buildSubagentDir(sessionId, [
{
id: "hq",
lines: buildModelSubLines("qa", "claude-haiku-4-5-20251001", { input: 1000, output: 500 }),
},
]);
try {
await importHistory.scanAndImportSubagents(dbModule, sessionId, transcriptPath);
await importHistory.scanAndImportSubagents(dbModule, sessionId, transcriptPath);
const rows = stmts.getTokensBySession.all(sessionId);
const haiku = rows.find((r) => r.model === "claude-haiku-4-5-20251001");
// getTokensBySession already returns effective totals (current + baseline).
// Re-running must not double them — append-only subagent JSONLs never drop.
assert.equal(haiku.input_tokens, 1000);
assert.equal(haiku.output_tokens, 500);
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
it("skips every model the MAIN transcript used, not just the latest (mid-session /model switch)", async () => {
const sessionId = "sess-185-switch";
// Orchestrator switched Opus → Sonnet mid-session; session.model holds the
// latest (Sonnet), but the main transcript wrote BOTH. A subagent on the
// earlier Opus must still be skipped to avoid colliding with the main writer.
stmts.insertSession.run(sessionId, "Switch", "active", "/tmp", "claude-sonnet-4-6", null);
stmts.insertAgent.run(
`${sessionId}-main`,
sessionId,
"Main",
"main",
null,
"working",
null,
null,
null
);
const { transcriptPath, base } = buildSubagentDir(sessionId, [
{
id: "op",
lines: buildModelSubLines("planner", "claude-opus-4-8", { input: 5000, output: 5000 }),
},
{
id: "hq",
lines: buildModelSubLines("qa", "claude-haiku-4-5-20251001", { input: 100, output: 50 }),
},
]);
try {
// parentModels carries BOTH orchestrator models (as hooks.js would pass).
await importHistory.scanAndImportSubagents(dbModule, sessionId, transcriptPath, {
parentModels: ["claude-sonnet-4-6", "claude-opus-4-8"],
});
const models = stmts.getTokensBySession
.all(sessionId)
.map((r) => r.model)
.sort();
// Only Haiku is written; both orchestrator models (Sonnet + the earlier
// Opus) are skipped even though Opus != session.model.
assert.deepEqual(models, ["claude-haiku-4-5-20251001"]);
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
it("backfills a live subagent row's model from its own transcript", async () => {
const sessionId = "sess-185-live";
const startedAt = "2026-05-01T10:00:00.000Z";
stmts.insertSession.run(sessionId, "Live", "active", "/tmp", "claude-opus-4-8", null);
stmts.insertAgent.run(
`${sessionId}-main`,
sessionId,
"Main",
"main",
null,
"working",
null,
null,
null
);
// Live subagent created by the PreToolUse "Agent" hook — no model recorded.
const liveId = "live-185-qa";
stmts.insertAgent.run(
liveId,
sessionId,
"QA",
"subagent",
"qa",
"working",
"task",
`${sessionId}-main`,
null
);
db.prepare("UPDATE agents SET started_at = ?, ended_at = ?, updated_at = ? WHERE id = ?").run(
startedAt,
startedAt,
startedAt,
liveId
);
const { transcriptPath, base } = buildSubagentDir(sessionId, [
{
id: "qa1",
agentType: "qa",
lines: buildModelSubLines(
"qa",
"claude-haiku-4-5-20251001",
{ input: 10, output: 5 },
startedAt
),
},
]);
try {
assert.equal(stmts.getAgent.get(liveId).metadata, null, "live row starts with no model");
await importHistory.scanAndImportSubagents(dbModule, sessionId, transcriptPath);
const meta = JSON.parse(stmts.getAgent.get(liveId).metadata || "{}");
assert.equal(meta.model, "claude-haiku-4-5-20251001", "live row backfilled with real model");
// No JSONL-keyed duplicate row was created (events merged into the live row).
assert.equal(stmts.getAgent.get(`${sessionId}-jsonl-qa1`), undefined);
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
});
describe("scanAndImportSubagents — nested subagent hierarchy", () => {
// A subagent's own transcript records each child it spawned via the Task tool
// as `toolUseResult.agentId`. Build a spawner file that claims `childIds`.
function buildSpawnerLines(childIds, startedAt = "2026-06-01T10:00:00.000Z") {
const lines = [
{ type: "user", timestamp: startedAt, message: { content: [{ type: "text", text: "go" }] } },
];
childIds.forEach((cid, i) => {
const ts = `2026-06-01T10:00:0${i + 1}.000Z`;
lines.push({
type: "assistant",
timestamp: ts,
message: {
model: "claude-opus-4-8",
content: [{ type: "tool_use", id: `toolu_task_${cid}`, name: "Task", input: {} }],
},
});
lines.push({
type: "user",
timestamp: ts,
toolUseResult: { agentId: cid, status: "completed" },
message: {
content: [{ type: "tool_result", tool_use_id: `toolu_task_${cid}`, content: "spawned" }],
},
});
});
return lines;
}
function buildLeafLines(startedAt = "2026-06-01T10:01:00.000Z") {
return [
{
type: "user",
timestamp: startedAt,
message: { content: [{ type: "text", text: "work" }] },
},
{
type: "assistant",
timestamp: startedAt,
message: {
model: "claude-haiku-4-5-20251001",
content: [{ type: "tool_use", id: "toolu_leaf", name: "Read", input: {} }],
},
},
];
}
it("nests subagents under their true spawner instead of flattening to main", async () => {
const sessionId = "sess-nested-tree";
const mainAgentId = `${sessionId}-main`;
stmts.insertSession.run(sessionId, "Nested", "active", "/tmp", "claude-opus-4-8", null);
stmts.insertAgent.run(
mainAgentId,
sessionId,
"Main",
"main",
null,
"working",
null,
null,
null
);
// main → orch; orch → leafA, leafB; main → solo
const { transcriptPath, base } = buildSubagentDir(sessionId, [
{ id: "orch", lines: buildSpawnerLines(["leafA", "leafB"]) },
{ id: "leafA", lines: buildLeafLines() },
{ id: "leafB", lines: buildLeafLines() },
{ id: "solo", lines: buildLeafLines() },
]);
try {
const res = await importHistory.scanAndImportSubagents(dbModule, sessionId, transcriptPath);
assert.equal(res.reparented, 2, "leafA + leafB repointed under orch");
const parentOf = (id) => stmts.getAgent.get(`${sessionId}-jsonl-${id}`).parent_agent_id;
assert.equal(parentOf("orch"), mainAgentId, "orch is a direct child of main");
assert.equal(parentOf("solo"), mainAgentId, "solo stays under main");
assert.equal(parentOf("leafA"), `${sessionId}-jsonl-orch`, "leafA nests under orch");
assert.equal(parentOf("leafB"), `${sessionId}-jsonl-orch`, "leafB nests under orch");
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
it("is idempotent — a second scan repoints nothing", async () => {
const sessionId = "sess-nested-idem";
const mainAgentId = `${sessionId}-main`;
stmts.insertSession.run(sessionId, "NestedIdem", "active", "/tmp", "claude-opus-4-8", null);
stmts.insertAgent.run(
mainAgentId,
sessionId,
"Main",
"main",
null,
"working",
null,
null,
null
);
const { transcriptPath, base } = buildSubagentDir(sessionId, [
{ id: "orch2", lines: buildSpawnerLines(["leafC"]) },
{ id: "leafC", lines: buildLeafLines() },
]);
try {
const first = await importHistory.scanAndImportSubagents(dbModule, sessionId, transcriptPath);
assert.equal(first.reparented, 1);
const second = await importHistory.scanAndImportSubagents(
dbModule,
sessionId,
transcriptPath
);
assert.equal(second.reparented, 0, "no re-parenting on the second pass");
assert.equal(
stmts.getAgent.get(`${sessionId}-jsonl-leafC`).parent_agent_id,
`${sessionId}-jsonl-orch2`
);
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});
});
describe("events dedup index", () => {
it("has a (agent_id, event_type) index so per-tool-event dedup is not a full scan", () => {
// importSubagentFromJsonl dedups every tool event with
// "WHERE agent_id = ? AND event_type = ? AND data LIKE '%tool_use_id%'".
// Without this index each dedup full-scans the events table; on a large DB a
// single re-import (e.g. the startup sweep touching a subagent-heavy session)
// takes tens of seconds and blocks the event loop. Guard it from regressing.
const row = db
.prepare(
"SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'idx_events_agent_type'"
)
.get();
assert.ok(row, "idx_events_agent_type must exist to keep subagent dedup indexed");
});
});