A running status shows 'running since', a stale one shows no age, since its time is when the run began. The reference now says the status reads and the row lookups are two passes, so the list waits up to four seconds in all.
144 lines
4.7 KiB
JavaScript
144 lines
4.7 KiB
JavaScript
// 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,
|
|
ageUnix,
|
|
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 says what the age counts from", () => {
|
|
assert.equal(stateLabel(withState("a", "finished", 5)), "finished");
|
|
assert.equal(stateLabel(withState("a", "stale", 5)), "stopped reporting");
|
|
assert.equal(stateLabel(withState("a", "running", 5)), "running since");
|
|
assert.equal(stateLabel(sub("a", 5)), "");
|
|
});
|
|
|
|
test("a stale status shows no age, the others show theirs", () => {
|
|
assert.equal(ageUnix(withState("a", "stale", 500)), null);
|
|
assert.equal(ageUnix(withState("a", "running", 500)), NOW - 500);
|
|
assert.equal(ageUnix(withState("a", "finished", 500)), NOW - 500);
|
|
assert.equal(ageUnix(sub("a", 30)), NOW - 30);
|
|
});
|
|
|
|
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: "" }), "");
|
|
});
|