diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index 16e32a059f..137fb152f7 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -52,7 +52,7 @@ "name": "agent-radar", "source": "plugins/agent-radar", "description": "See a live map of the Copilot agents on your computer, their sub-agents, their commands, and where their data goes.", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "agentcouncil", diff --git a/extensions/agent-radar/graph.test.mjs b/extensions/agent-radar/graph.test.mjs index 987f65d83f..2358da7d76 100644 --- a/extensions/agent-radar/graph.test.mjs +++ b/extensions/agent-radar/graph.test.mjs @@ -38,9 +38,9 @@ function graph(width = 1000, height = 500, panels = false) { vm.createContext(sandbox); vm.runInContext((panels ? panelSource : source) + ` reduceMotion = true; - globalThis.graph = { build, draw, pick, toggleSelect, nodeInfo, label, nodes, focusedNodes, messagePoint, drawMessages, + globalThis.graph = { build, draw, pick, toggleSelect, nodeInfo, label, nodes, focusedNodes, messagePoint, drawMessages, spawnParticles, setReducedMotion(value) { reduceMotion = value; }, - get messagePulses() { return messagePulses; }, + get messagePulses() { return messagePulses; }, get particles() { return particles; }, get links() { return links; }, get selected() { return selected; }, get size() { return [W, H]; } }; `, sandbox); @@ -105,6 +105,27 @@ test("idle filtering keeps node identities and data flow attribution", () => { assert.equal(api.links.find((l) => l.kind === "flow").a, "s:one"); }); +test("in-flight particles follow rebuilt links and drop with removed links", () => { + const { api, elements } = graph(); + const idle = session("two"); + idle.busy = false; + const s = session("one", 1); + api.setReducedMotion(false); + api.build(snapshot(s, idle)); + api.spawnParticles(1000); + assert.ok(api.particles.some((p) => p.l.b === "a:one:child-0")); + elements.get("showIdle").checked = true; + api.build(snapshot(s, idle)); + assert.ok(api.particles.length > 0); + for (const p of api.particles) assert.ok(api.links.includes(p.l)); + elements.get("showIdle").checked = false; + s.subagents[0].running = false; + api.build(snapshot(s, idle)); + assert.ok(api.particles.length > 0); + assert.ok(!api.particles.some((p) => p.l.b === "a:one:child-0")); + for (const p of api.particles) assert.ok(api.links.includes(p.l)); +}); + test("drawing shows diamond, name, status and task with pixel-bounded labels", () => { const { api, labels, paths } = graph(900); const s = session("one", 1); diff --git a/extensions/agent-radar/ui.html b/extensions/agent-radar/ui.html index 4cf6b60a46..b541751350 100644 --- a/extensions/agent-radar/ui.html +++ b/extensions/agent-radar/ui.html @@ -363,6 +363,9 @@

Agent Radar

} for (const [id, n] of nodes) if (!n.seen) nodes.delete(id); links = links.filter((l) => nodes.has(l.a) && nodes.has(l.b)); + // Move in-flight particles onto the rebuilt links so they follow re-laid-out paths. + const linkByEnds = new Map(links.map((l) => [l.a + "→" + l.b, l])); + particles = particles.filter((p) => (p.l = linkByEnds.get(p.l.a + "→" + p.l.b))); observeMessages(snap); if (selected && !nodes.has(selected.id)) selected = null; if (hover && !nodes.has(hover.id)) { hover = null; tip.style.display = "none"; cv.style.cursor = "default"; } diff --git a/plugins/agent-radar/plugin.json b/plugins/agent-radar/plugin.json index c263c3ab17..4747c9a315 100644 --- a/plugins/agent-radar/plugin.json +++ b/plugins/agent-radar/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "agent-radar", "description": "See a live map of the Copilot agents on your computer, their sub-agents, their commands, and where their data goes.", - "version": "1.0.0", + "version": "1.0.1", "author": { "name": "Annika Wickert", "url": "https://github.com/awlx"