import assert from "node:assert/strict"; import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { describe, it } from "node:test"; import type { AgentConfig } from "../../src/agents/agents.ts"; import { readSubagentRoutingSettings, routeSubagentLaunch, type SubagentRoutingSettings, } from "../../src/runs/shared/jev-subagent-routing.ts"; function agent(name: string, extra: Partial = {}): AgentConfig { return { name, description: `${name} agent`, systemPromptMode: "append", inheritProjectContext: true, inheritGlobalContext: true, inheritSkills: true, systemPrompt: "", source: "builtin", filePath: `${name}.md`, ...extra } as AgentConfig; } const agents = [ agent("delegate"), agent("reviewer", { tools: ["read", "grep"] }), agent("worker", { tools: ["read", "bash", "edit", "write"], model: "anthropic/opus", modelProvider: "anthropic" }), agent("secret", { disabled: true }), agent("external", { runner: { type: "external-cli" } as AgentConfig["runner"] }), ]; const settings: SubagentRoutingSettings = { connection: { provider: "tokenin", model: "jev", timeoutMs: 1000, minConfidence: 0.6 }, maxBytes: 16_384, contextChars: 4000, genericAgents: ["delegate"], tiers: { simple: "p/small", reasoning: "p/big" }, }; type Asked = { allowed: Record }; /** A fake Jev: answers each asked question from `answers`, records what was offered. */ function fakeAsk(answers: Record, asked: Asked[] = []) { return async (_ctx: unknown, _connection: unknown, request: Asked) => { asked.push({ allowed: request.allowed }); const choices = Object.fromEntries(Object.keys(request.allowed).filter((q) => answers[q]).map((q) => [q, answers[q]!])); return { choices, rejected: {}, elapsedMs: 5 }; }; } const ctx = {} as never; describe("Jev subagent routing", () => { it("routes an agentless task, offering only enabled native agents inside the ceiling", async () => { const asked: Asked[] = []; const result = await routeSubagentLaunch( { task: "fix the bug", agents, ceiling: { allowedAgents: ["delegate", "worker", "secret"], sources: ["test"] } as never }, ctx, settings, fakeAsk({ agent: { choice: "worker", confidence: 0.9 } }, asked) as never, ); assert.equal(result.agent, "worker"); assert.deepEqual(asked[0]!.allowed.agent, ["delegate", "worker"]); }); it("offers keep for a generic agent and never re-routes an explicit specialist", async () => { const asked: Asked[] = []; const kept = await routeSubagentLaunch({ task: "t", agent: "delegate", agents }, ctx, settings, fakeAsk({ agent: { choice: "keep", confidence: 0.9 } }, asked) as never); assert.equal(kept.agent, "delegate"); assert.ok(asked[0]!.allowed.agent!.includes("keep")); assert.ok(!asked[0]!.allowed.agent!.includes("delegate")); const explicitAsked: Asked[] = []; const explicit = await routeSubagentLaunch({ task: "t", agent: "reviewer", model: "x/y", agents }, ctx, settings, fakeAsk({}, explicitAsked) as never); assert.equal(explicit.agent, "reviewer"); assert.ok(explicitAsked.every((ask) => !("agent" in ask.allowed))); }); it("abstention leaves the launch as requested", async () => { const result = await routeSubagentLaunch({ task: "t", agents }, ctx, settings, (async () => ({ choices: {}, rejected: { agent: "no-credential" }, failure: "no-credential", elapsedMs: 0 })) as never); assert.equal(result.agent, undefined); assert.equal(result.agents, agents); assert.equal(result.abstained, "no-credential"); }); it("only drops declared, unprotected tools on a confident unneeded, and applies the tier model", async () => { const asked: Asked[] = []; const result = await routeSubagentLaunch( { task: "summarize the README", agent: "worker", agents }, ctx, settings, fakeAsk({ tier: { choice: "simple", confidence: 0.8 }, tool_0: { choice: "unneeded", confidence: 0.9 }, tool_1: { choice: "unneeded" }, tool_2: { choice: "needed", confidence: 0.9 } }, asked) as never, ); // read is never offered: tool_0..2 are bash, edit, write. assert.deepEqual(Object.keys(asked[0]!.allowed).sort(), ["tier", "tool_0", "tool_1", "tool_2"]); assert.deepEqual(asked[0]!.allowed.tier, ["simple", "reasoning"]); assert.deepEqual(result.droppedTools, ["bash"]); const worker = result.agents.find((a) => a.name === "worker")!; assert.deepEqual(worker.excludeTools, ["bash"]); assert.equal(worker.model, "p/small"); assert.equal(worker.modelProvider, undefined); assert.deepEqual(worker.tools, ["read", "bash", "edit", "write"]); // The discovered list itself is untouched. assert.equal(agents.find((a) => a.name === "worker")!.model, "anthropic/opus"); }); it("skips the tier question when the parent chose a model", async () => { const asked: Asked[] = []; const result = await routeSubagentLaunch({ task: "t", agent: "worker", model: "x/y", agents }, ctx, settings, fakeAsk({ tier: { choice: "simple", confidence: 0.9 } }, asked) as never); assert.ok(!("tier" in asked[0]!.allowed)); assert.equal(result.tier, undefined); }); it("reads settings: off by default, tiers and generic agents when enabled", () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "jev-subagent-")); try { const file = path.join(dir, "settings.json"); fs.writeFileSync(file, JSON.stringify({ jevAdvisory: { routes: {} } })); assert.equal(readSubagentRoutingSettings(file), undefined); fs.writeFileSync(file, JSON.stringify({ jevAdvisory: { routes: { subagent: { enabled: true, tiers: { simple: " p/s ", bogus: "x" }, genericAgents: ["delegate", "general"] } } } })); const read = readSubagentRoutingSettings(file)!; assert.deepEqual(read.tiers, { simple: "p/s" }); assert.deepEqual(read.genericAgents, ["delegate", "general"]); assert.equal(read.connection.timeoutMs, 5000); } finally { fs.rmSync(dir, { recursive: true, force: true }); } }); });