// What counts as a running subagent, and the menu badge's wording. Run by // `npm test`, which Node runs with its built-in test runner and type // stripping, so the `.ts` module is imported as it is. import assert from "node:assert/strict"; import { test } from "node:test"; import { STALE_S, byRecency, isDimmed, isRunning, runLabel, runningLabel, splitRunning, stateLabel, stillListed, } from "../src/agent/terminal/subagent-list.ts"; const NOW = 1_000_000; const sub = (name, ago) => ({ name, last_row_unix: ago === null ? null : NOW - ago, model: null, effort: null, }); test("a subagent with a recent row is running, a quiet one is not", () => { assert.equal(isRunning(sub("a", 5), NOW), true); assert.equal(isRunning(sub("a", STALE_S), NOW), true); assert.equal(isRunning(sub("a", STALE_S + 1), NOW), false); }); test("a subagent whose time could not be read is not counted as running", () => { assert.equal(isRunning(sub("a", null), NOW), false); }); test("newest first, unreadable last, ties by name", () => { const list = [sub("c", 50), sub("z", null), sub("b", 10), sub("a", 10)]; assert.deepEqual( [...list].sort(byRecency).map((s) => s.name), ["a", "b", "c", "z"], ); }); test("the list splits into running and quiet, each newest first", () => { const { running, quiet } = splitRunning( [ sub("old", STALE_S + 100), sub("new", 1), sub("mid", 60), sub("none", null), ], NOW, ); assert.deepEqual( running.map((s) => s.name), ["new", "mid"], ); assert.deepEqual( quiet.map((s) => s.name), ["old", "none"], ); }); test("the badge counts in words", () => { assert.equal(runningLabel(1), "1 running subagent"); assert.equal(runningLabel(4), "4 running subagents"); }); test("a pick holds only while the refreshed list still has it", () => { const subs = [sub("a", 10), sub("b", 20)]; assert.equal(stillListed("a", subs), "a"); assert.equal(stillListed("gone", subs), null); assert.equal(stillListed("a", []), null); assert.equal(stillListed("a", null), null); assert.equal(stillListed(null, subs), null); }); const withState = (name, state, sinceAgo, rowAgo = null) => ({ ...sub(name, rowAgo), state, since_unix: NOW - sinceAgo, exit: state === "finished" ? 0 : null, }); test("a published state decides, whatever the newest row's age says", () => { // Quiet for an hour but the daemon says running: thinking, not stopped. assert.equal(isRunning(withState("a", "running", 3600, 3600), NOW), true); // A row a second ago but the daemon says finished. assert.equal(isRunning(withState("a", "finished", 5, 1), NOW), false); assert.equal(isRunning(withState("a", "stale", 900), NOW), false); }); test("only a subagent without a status falls back to the row's age", () => { assert.equal(isDimmed(sub("a", STALE_S + 1), NOW), true); assert.equal(isDimmed(sub("a", 5), NOW), false); assert.equal(isDimmed(sub("a", null), NOW), false); assert.equal(isDimmed(withState("a", "running", 3600, 3600), NOW), false); assert.equal(isDimmed(withState("a", "finished", 5, 1), NOW), true); assert.equal(isDimmed(withState("a", "stale", 5), NOW), true); }); test("the state word names finished and stale subagents only", () => { assert.equal(stateLabel(withState("a", "finished", 5)), "finished"); assert.equal(stateLabel(withState("a", "stale", 5)), "stopped reporting"); assert.equal(stateLabel(withState("a", "running", 5)), ""); assert.equal(stateLabel(sub("a", 5)), ""); }); test("a subagent with only a status sorts by when its state began", () => { const list = [ withState("old", "finished", 500), sub("row", 100), withState("new", "running", 5), ]; assert.deepEqual( [...list].sort(byRecency).map((s) => s.name), ["new", "row", "old"], ); const { running, quiet } = splitRunning(list, NOW); assert.deepEqual( running.map((s) => s.name), ["new", "row"], ); assert.deepEqual( quiet.map((s) => s.name), ["old"], ); }); test("the run label names the model and effort, whichever are known", () => { assert.equal(runLabel({ model: "opus", effort: "high" }), "opus ยท high"); assert.equal(runLabel({ model: "opus", effort: null }), "opus"); assert.equal(runLabel({ model: null, effort: "medium" }), "medium"); assert.equal(runLabel({ model: null, effort: null }), ""); assert.equal(runLabel({ model: "", effort: "" }), ""); });