|
|
@@ -1,5 +1,5 @@
|
|
|
-import { describe, expect, test } from "bun:test";
|
|
|
-import type { PluginConfig } from "../config";
|
|
|
+import { describe, expect, test } from 'bun:test';
|
|
|
+import type { PluginConfig } from '../config';
|
|
|
import {
|
|
|
AgentOverrideConfigSchema,
|
|
|
CouncilConfigSchema,
|
|
|
@@ -7,390 +7,390 @@ import {
|
|
|
DEFAULT_MODELS,
|
|
|
PluginConfigSchema,
|
|
|
SUBAGENT_NAMES,
|
|
|
-} from "../config";
|
|
|
+} from '../config';
|
|
|
import {
|
|
|
createAgents,
|
|
|
getAgentConfigs,
|
|
|
getDisabledAgents,
|
|
|
getEnabledAgentNames,
|
|
|
isSubagent,
|
|
|
-} from "./index";
|
|
|
+} from './index';
|
|
|
|
|
|
function councilConfig() {
|
|
|
const parsed = CouncilConfigSchema.parse({
|
|
|
- presets: { default: { alpha: { model: "test/councillor" } } },
|
|
|
+ presets: { default: { alpha: { model: 'test/councillor' } } },
|
|
|
});
|
|
|
return parsed;
|
|
|
}
|
|
|
|
|
|
-describe("agent alias backward compatibility", () => {
|
|
|
+describe('agent alias backward compatibility', () => {
|
|
|
test("applies 'explore' config to 'explorer' agent", () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- explore: { model: "test/old-explore-model" },
|
|
|
+ explore: { model: 'test/old-explore-model' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const explorer = agents.find((a) => a.name === "explorer");
|
|
|
+ const explorer = agents.find((a) => a.name === 'explorer');
|
|
|
expect(explorer).toBeDefined();
|
|
|
- expect(explorer?.config.model).toBe("test/old-explore-model");
|
|
|
+ expect(explorer?.config.model).toBe('test/old-explore-model');
|
|
|
});
|
|
|
|
|
|
test("applies 'frontend-ui-ux-engineer' config to 'designer' agent", () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- "frontend-ui-ux-engineer": { model: "test/old-frontend-model" },
|
|
|
+ 'frontend-ui-ux-engineer': { model: 'test/old-frontend-model' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const designer = agents.find((a) => a.name === "designer");
|
|
|
+ const designer = agents.find((a) => a.name === 'designer');
|
|
|
expect(designer).toBeDefined();
|
|
|
- expect(designer?.config.model).toBe("test/old-frontend-model");
|
|
|
+ expect(designer?.config.model).toBe('test/old-frontend-model');
|
|
|
});
|
|
|
|
|
|
- test("new name takes priority over old alias", () => {
|
|
|
+ test('new name takes priority over old alias', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- explore: { model: "old-model" },
|
|
|
- explorer: { model: "new-model" },
|
|
|
+ explore: { model: 'old-model' },
|
|
|
+ explorer: { model: 'new-model' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const explorer = agents.find((a) => a.name === "explorer");
|
|
|
- expect(explorer?.config.model).toBe("new-model");
|
|
|
+ const explorer = agents.find((a) => a.name === 'explorer');
|
|
|
+ expect(explorer?.config.model).toBe('new-model');
|
|
|
});
|
|
|
|
|
|
- test("new agent names work directly", () => {
|
|
|
+ test('new agent names work directly', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- explorer: { model: "direct-explorer" },
|
|
|
- designer: { model: "direct-designer" },
|
|
|
+ explorer: { model: 'direct-explorer' },
|
|
|
+ designer: { model: 'direct-designer' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- expect(agents.find((a) => a.name === "explorer")?.config.model).toBe(
|
|
|
- "direct-explorer"
|
|
|
+ expect(agents.find((a) => a.name === 'explorer')?.config.model).toBe(
|
|
|
+ 'direct-explorer',
|
|
|
);
|
|
|
- expect(agents.find((a) => a.name === "designer")?.config.model).toBe(
|
|
|
- "direct-designer"
|
|
|
+ expect(agents.find((a) => a.name === 'designer')?.config.model).toBe(
|
|
|
+ 'direct-designer',
|
|
|
);
|
|
|
});
|
|
|
|
|
|
- test("temperature override via old alias", () => {
|
|
|
+ test('temperature override via old alias', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
explore: { temperature: 0.5 },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const explorer = agents.find((a) => a.name === "explorer");
|
|
|
+ const explorer = agents.find((a) => a.name === 'explorer');
|
|
|
expect(explorer?.config.temperature).toBe(0.5);
|
|
|
});
|
|
|
|
|
|
- test("variant override via old alias", () => {
|
|
|
+ test('variant override via old alias', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- explore: { variant: "low" },
|
|
|
+ explore: { variant: 'low' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const explorer = agents.find((a) => a.name === "explorer");
|
|
|
- expect(explorer?.config.variant).toBe("low");
|
|
|
+ const explorer = agents.find((a) => a.name === 'explorer');
|
|
|
+ expect(explorer?.config.variant).toBe('low');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("fixer agent fallback", () => {
|
|
|
- test("fixer inherits librarian model when no fixer config provided", () => {
|
|
|
+describe('fixer agent fallback', () => {
|
|
|
+ test('fixer inherits librarian model when no fixer config provided', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- librarian: { model: "librarian-custom-model" },
|
|
|
+ librarian: { model: 'librarian-custom-model' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const fixer = agents.find((a) => a.name === "fixer");
|
|
|
- const librarian = agents.find((a) => a.name === "librarian");
|
|
|
+ const fixer = agents.find((a) => a.name === 'fixer');
|
|
|
+ const librarian = agents.find((a) => a.name === 'librarian');
|
|
|
expect(fixer?.config.model).toBe(librarian?.config.model);
|
|
|
});
|
|
|
|
|
|
- test("fixer uses its own model when explicitly configured", () => {
|
|
|
+ test('fixer uses its own model when explicitly configured', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- librarian: { model: "librarian-model" },
|
|
|
- fixer: { model: "fixer-specific-model" },
|
|
|
+ librarian: { model: 'librarian-model' },
|
|
|
+ fixer: { model: 'fixer-specific-model' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const fixer = agents.find((a) => a.name === "fixer");
|
|
|
- expect(fixer?.config.model).toBe("fixer-specific-model");
|
|
|
+ const fixer = agents.find((a) => a.name === 'fixer');
|
|
|
+ expect(fixer?.config.model).toBe('fixer-specific-model');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("orchestrator agent", () => {
|
|
|
- test("orchestrator is first in agents array", () => {
|
|
|
+describe('orchestrator agent', () => {
|
|
|
+ test('orchestrator is first in agents array', () => {
|
|
|
const agents = createAgents();
|
|
|
- expect(agents[0].name).toBe("orchestrator");
|
|
|
+ expect(agents[0].name).toBe('orchestrator');
|
|
|
});
|
|
|
|
|
|
- test("orchestrator has question permission set to allow", () => {
|
|
|
+ test('orchestrator has question permission set to allow', () => {
|
|
|
const agents = createAgents();
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
expect(orchestrator?.config.permission).toBeDefined();
|
|
|
- expect((orchestrator?.config.permission as any).question).toBe("allow");
|
|
|
+ expect((orchestrator?.config.permission as any).question).toBe('allow');
|
|
|
});
|
|
|
|
|
|
- test("orchestrator is denied access to council_session", () => {
|
|
|
+ test('orchestrator is denied access to council_session', () => {
|
|
|
const agents = createAgents();
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
expect((orchestrator?.config.permission as any).council_session).toBe(
|
|
|
- "deny"
|
|
|
+ 'deny',
|
|
|
);
|
|
|
});
|
|
|
|
|
|
- test("orchestrator accepts overrides", () => {
|
|
|
+ test('orchestrator accepts overrides', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- orchestrator: { model: "custom-orchestrator-model", temperature: 0.3 },
|
|
|
+ orchestrator: { model: 'custom-orchestrator-model', temperature: 0.3 },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
- expect(orchestrator?.config.model).toBe("custom-orchestrator-model");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
+ expect(orchestrator?.config.model).toBe('custom-orchestrator-model');
|
|
|
expect(orchestrator?.config.temperature).toBe(0.3);
|
|
|
});
|
|
|
|
|
|
- test("orchestrator accepts variant override", () => {
|
|
|
+ test('orchestrator accepts variant override', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- orchestrator: { variant: "high" },
|
|
|
+ orchestrator: { variant: 'high' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
- expect(orchestrator?.config.variant).toBe("high");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
+ expect(orchestrator?.config.variant).toBe('high');
|
|
|
});
|
|
|
|
|
|
- test("orchestrator stores model array with per-model variants in _modelArray", () => {
|
|
|
+ test('orchestrator stores model array with per-model variants in _modelArray', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
orchestrator: {
|
|
|
model: [
|
|
|
- { id: "google/gemini-3-pro", variant: "high" },
|
|
|
- { id: "github-copilot/claude-3.5-haiku" },
|
|
|
- "openai/gpt-4",
|
|
|
+ { id: 'google/gemini-3-pro', variant: 'high' },
|
|
|
+ { id: 'github-copilot/claude-3.5-haiku' },
|
|
|
+ 'openai/gpt-4',
|
|
|
],
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
expect(orchestrator?._modelArray).toEqual([
|
|
|
- { id: "google/gemini-3-pro", variant: "high" },
|
|
|
- { id: "github-copilot/claude-3.5-haiku" },
|
|
|
- { id: "openai/gpt-4" },
|
|
|
+ { id: 'google/gemini-3-pro', variant: 'high' },
|
|
|
+ { id: 'github-copilot/claude-3.5-haiku' },
|
|
|
+ { id: 'openai/gpt-4' },
|
|
|
]);
|
|
|
expect(orchestrator?.config.model).toBeUndefined();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("per-model variant in array config", () => {
|
|
|
- test("subagent stores model array with per-model variants", () => {
|
|
|
+describe('per-model variant in array config', () => {
|
|
|
+ test('subagent stores model array with per-model variants', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
explorer: {
|
|
|
model: [
|
|
|
- { id: "google/gemini-3-flash", variant: "low" },
|
|
|
- "openai/gpt-4o-mini",
|
|
|
+ { id: 'google/gemini-3-flash', variant: 'low' },
|
|
|
+ 'openai/gpt-4o-mini',
|
|
|
],
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const explorer = agents.find((a) => a.name === "explorer");
|
|
|
+ const explorer = agents.find((a) => a.name === 'explorer');
|
|
|
expect(explorer?._modelArray).toEqual([
|
|
|
- { id: "google/gemini-3-flash", variant: "low" },
|
|
|
- { id: "openai/gpt-4o-mini" },
|
|
|
+ { id: 'google/gemini-3-flash', variant: 'low' },
|
|
|
+ { id: 'openai/gpt-4o-mini' },
|
|
|
]);
|
|
|
expect(explorer?.config.model).toBeUndefined();
|
|
|
});
|
|
|
|
|
|
- test("top-level variant preserved alongside per-model variants", () => {
|
|
|
+ test('top-level variant preserved alongside per-model variants', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
orchestrator: {
|
|
|
model: [
|
|
|
- { id: "google/gemini-3-pro", variant: "high" },
|
|
|
- "openai/gpt-4",
|
|
|
+ { id: 'google/gemini-3-pro', variant: 'high' },
|
|
|
+ 'openai/gpt-4',
|
|
|
],
|
|
|
- variant: "low",
|
|
|
+ variant: 'low',
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
// top-level variant still set as default
|
|
|
- expect(orchestrator?.config.variant).toBe("low");
|
|
|
+ expect(orchestrator?.config.variant).toBe('low');
|
|
|
// per-model variants stored in _modelArray
|
|
|
- expect(orchestrator?._modelArray?.[0]?.variant).toBe("high");
|
|
|
+ expect(orchestrator?._modelArray?.[0]?.variant).toBe('high');
|
|
|
expect(orchestrator?._modelArray?.[1]?.variant).toBeUndefined();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("skill permissions", () => {
|
|
|
- test("orchestrator gets codemap skill allowed by default", () => {
|
|
|
+describe('skill permissions', () => {
|
|
|
+ test('orchestrator gets codemap skill allowed by default', () => {
|
|
|
const agents = createAgents();
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
expect(orchestrator).toBeDefined();
|
|
|
const skillPerm = (
|
|
|
orchestrator?.config.permission as Record<string, unknown>
|
|
|
)?.skill as Record<string, string>;
|
|
|
// orchestrator gets wildcard allow (from RECOMMENDED_SKILLS wildcard entry)
|
|
|
- expect(skillPerm?.["*"]).toBe("allow");
|
|
|
+ expect(skillPerm?.['*']).toBe('allow');
|
|
|
// CUSTOM_SKILLS loop must also add a named codemap entry for orchestrator
|
|
|
- expect(skillPerm?.codemap).toBe("allow");
|
|
|
+ expect(skillPerm?.codemap).toBe('allow');
|
|
|
});
|
|
|
|
|
|
- test("fixer does not get codemap skill allowed by default", () => {
|
|
|
+ test('fixer does not get codemap skill allowed by default', () => {
|
|
|
const agents = createAgents();
|
|
|
- const fixer = agents.find((a) => a.name === "fixer");
|
|
|
+ const fixer = agents.find((a) => a.name === 'fixer');
|
|
|
expect(fixer).toBeDefined();
|
|
|
const skillPerm = (fixer?.config.permission as Record<string, unknown>)
|
|
|
?.skill as Record<string, string>;
|
|
|
- expect(skillPerm?.codemap).not.toBe("allow");
|
|
|
+ expect(skillPerm?.codemap).not.toBe('allow');
|
|
|
});
|
|
|
|
|
|
- test("oracle gets requesting-code-review skill allowed by default", () => {
|
|
|
+ test('oracle gets requesting-code-review skill allowed by default', () => {
|
|
|
const agents = createAgents();
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
expect(oracle).toBeDefined();
|
|
|
const skillPerm = (oracle?.config.permission as Record<string, unknown>)
|
|
|
?.skill as Record<string, string>;
|
|
|
- expect(skillPerm?.["requesting-code-review"]).toBe("allow");
|
|
|
+ expect(skillPerm?.['requesting-code-review']).toBe('allow');
|
|
|
});
|
|
|
|
|
|
- test("oracle gets simplify skill allowed by default", () => {
|
|
|
+ test('oracle gets simplify skill allowed by default', () => {
|
|
|
const agents = createAgents();
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
expect(oracle).toBeDefined();
|
|
|
const skillPerm = (oracle?.config.permission as Record<string, unknown>)
|
|
|
?.skill as Record<string, string>;
|
|
|
- expect(skillPerm?.simplify).toBe("allow");
|
|
|
+ expect(skillPerm?.simplify).toBe('allow');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("tool permissions", () => {
|
|
|
- test("council agent is allowed to invoke council_session", () => {
|
|
|
+describe('tool permissions', () => {
|
|
|
+ test('council agent is allowed to invoke council_session', () => {
|
|
|
const agents = createAgents({
|
|
|
council: councilConfig(),
|
|
|
});
|
|
|
- const council = agents.find((a) => a.name === "council");
|
|
|
- expect((council?.config.permission as any).council_session).toBe("allow");
|
|
|
+ const council = agents.find((a) => a.name === 'council');
|
|
|
+ expect((council?.config.permission as any).council_session).toBe('allow');
|
|
|
});
|
|
|
|
|
|
- test("oracle is denied access to council_session", () => {
|
|
|
+ test('oracle is denied access to council_session', () => {
|
|
|
const agents = createAgents();
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
- expect((oracle?.config.permission as any).council_session).toBe("deny");
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
+ expect((oracle?.config.permission as any).council_session).toBe('deny');
|
|
|
});
|
|
|
|
|
|
- test("explorer is denied access to council_session", () => {
|
|
|
+ test('explorer is denied access to council_session', () => {
|
|
|
const agents = createAgents();
|
|
|
- const explorer = agents.find((a) => a.name === "explorer");
|
|
|
- expect((explorer?.config.permission as any).council_session).toBe("deny");
|
|
|
+ const explorer = agents.find((a) => a.name === 'explorer');
|
|
|
+ expect((explorer?.config.permission as any).council_session).toBe('deny');
|
|
|
});
|
|
|
|
|
|
- test("councillor is denied access to council_session", () => {
|
|
|
+ test('councillor is denied access to council_session', () => {
|
|
|
const agents = createAgents();
|
|
|
- const councillor = agents.find((a) => a.name === "councillor");
|
|
|
- expect((councillor?.config.permission as any).council_session).toBe("deny");
|
|
|
+ const councillor = agents.find((a) => a.name === 'councillor');
|
|
|
+ expect((councillor?.config.permission as any).council_session).toBe('deny');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("isSubagent type guard", () => {
|
|
|
- test("returns true for valid subagent names", () => {
|
|
|
- expect(isSubagent("explorer")).toBe(true);
|
|
|
- expect(isSubagent("librarian")).toBe(true);
|
|
|
- expect(isSubagent("oracle")).toBe(true);
|
|
|
- expect(isSubagent("designer")).toBe(true);
|
|
|
- expect(isSubagent("fixer")).toBe(true);
|
|
|
+describe('isSubagent type guard', () => {
|
|
|
+ test('returns true for valid subagent names', () => {
|
|
|
+ expect(isSubagent('explorer')).toBe(true);
|
|
|
+ expect(isSubagent('librarian')).toBe(true);
|
|
|
+ expect(isSubagent('oracle')).toBe(true);
|
|
|
+ expect(isSubagent('designer')).toBe(true);
|
|
|
+ expect(isSubagent('fixer')).toBe(true);
|
|
|
});
|
|
|
|
|
|
- test("returns false for orchestrator", () => {
|
|
|
- expect(isSubagent("orchestrator")).toBe(false);
|
|
|
+ test('returns false for orchestrator', () => {
|
|
|
+ expect(isSubagent('orchestrator')).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("returns false for invalid agent names", () => {
|
|
|
- expect(isSubagent("invalid-agent")).toBe(false);
|
|
|
- expect(isSubagent("")).toBe(false);
|
|
|
- expect(isSubagent("explore")).toBe(false); // old alias, not actual agent name
|
|
|
+ test('returns false for invalid agent names', () => {
|
|
|
+ expect(isSubagent('invalid-agent')).toBe(false);
|
|
|
+ expect(isSubagent('')).toBe(false);
|
|
|
+ expect(isSubagent('explore')).toBe(false); // old alias, not actual agent name
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("agent classification", () => {
|
|
|
- test("SUBAGENT_NAMES excludes orchestrator", () => {
|
|
|
- expect(SUBAGENT_NAMES).not.toContain("orchestrator");
|
|
|
- expect(SUBAGENT_NAMES).toContain("explorer");
|
|
|
- expect(SUBAGENT_NAMES).toContain("fixer");
|
|
|
+describe('agent classification', () => {
|
|
|
+ test('SUBAGENT_NAMES excludes orchestrator', () => {
|
|
|
+ expect(SUBAGENT_NAMES).not.toContain('orchestrator');
|
|
|
+ expect(SUBAGENT_NAMES).toContain('explorer');
|
|
|
+ expect(SUBAGENT_NAMES).toContain('fixer');
|
|
|
});
|
|
|
|
|
|
- test("getAgentConfigs applies correct classification visibility and mode", () => {
|
|
|
+ test('getAgentConfigs applies correct classification visibility and mode', () => {
|
|
|
// Enable all agents (including observer) for classification testing
|
|
|
const configs = getAgentConfigs({ disabled_agents: [] });
|
|
|
|
|
|
// Primary agent
|
|
|
- expect(configs.orchestrator.mode).toBe("primary");
|
|
|
+ expect(configs.orchestrator.mode).toBe('primary');
|
|
|
|
|
|
// Subagents
|
|
|
for (const name of SUBAGENT_NAMES) {
|
|
|
// Council is a dual-mode agent ("all"), rest are subagents
|
|
|
- if (name === "council") {
|
|
|
+ if (name === 'council') {
|
|
|
expect(configs[name]).toBeUndefined();
|
|
|
} else {
|
|
|
- expect(configs[name].mode).toBe("subagent");
|
|
|
+ expect(configs[name].mode).toBe('subagent');
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("createAgents", () => {
|
|
|
- test("creates all agents without config", () => {
|
|
|
+describe('createAgents', () => {
|
|
|
+ test('creates all agents without config', () => {
|
|
|
const agents = createAgents();
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).toContain("orchestrator");
|
|
|
- expect(names).toContain("explorer");
|
|
|
- expect(names).toContain("designer");
|
|
|
- expect(names).toContain("oracle");
|
|
|
- expect(names).toContain("librarian");
|
|
|
- expect(names).toContain("fixer");
|
|
|
+ expect(names).toContain('orchestrator');
|
|
|
+ expect(names).toContain('explorer');
|
|
|
+ expect(names).toContain('designer');
|
|
|
+ expect(names).toContain('oracle');
|
|
|
+ expect(names).toContain('librarian');
|
|
|
+ expect(names).toContain('fixer');
|
|
|
});
|
|
|
|
|
|
- test("creates exactly 7 agents by default (observer disabled, council unconfigured)", () => {
|
|
|
+ test('creates exactly 7 agents by default (observer disabled, council unconfigured)', () => {
|
|
|
const agents = createAgents();
|
|
|
expect(agents.length).toBe(7);
|
|
|
});
|
|
|
|
|
|
- test("does not create council when council is not configured", () => {
|
|
|
+ test('does not create council when council is not configured', () => {
|
|
|
const agents = createAgents();
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
|
|
|
- expect(names).not.toContain("council");
|
|
|
- expect(orchestrator?.config.prompt).not.toContain("@council");
|
|
|
+ expect(names).not.toContain('council');
|
|
|
+ expect(orchestrator?.config.prompt).not.toContain('@council');
|
|
|
});
|
|
|
|
|
|
- test("creates council when council is configured", () => {
|
|
|
+ test('creates council when council is configured', () => {
|
|
|
const agents = createAgents({
|
|
|
council: councilConfig(),
|
|
|
});
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- const orchestrator = agents.find((a) => a.name === "orchestrator");
|
|
|
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
|
|
|
|
|
|
- expect(names).toContain("council");
|
|
|
- expect(orchestrator?.config.prompt).toContain("@council");
|
|
|
+ expect(names).toContain('council');
|
|
|
+ expect(orchestrator?.config.prompt).toContain('@council');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("getAgentConfigs", () => {
|
|
|
- test("returns config record keyed by agent name", () => {
|
|
|
+describe('getAgentConfigs', () => {
|
|
|
+ test('returns config record keyed by agent name', () => {
|
|
|
const configs = getAgentConfigs();
|
|
|
expect(configs.orchestrator).toBeDefined();
|
|
|
expect(configs.explorer).toBeDefined();
|
|
|
@@ -399,80 +399,80 @@ describe("getAgentConfigs", () => {
|
|
|
expect(configs.explorer.model).toBeDefined();
|
|
|
});
|
|
|
|
|
|
- test("includes description in SDK config", () => {
|
|
|
+ test('includes description in SDK config', () => {
|
|
|
const configs = getAgentConfigs();
|
|
|
expect(configs.orchestrator.description).toBeDefined();
|
|
|
expect(configs.explorer.description).toBeDefined();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("council agent model resolution", () => {
|
|
|
- test("council agent uses default model", () => {
|
|
|
+describe('council agent model resolution', () => {
|
|
|
+ test('council agent uses default model', () => {
|
|
|
const agents = createAgents({
|
|
|
council: councilConfig(),
|
|
|
});
|
|
|
- const council = agents.find((a) => a.name === "council");
|
|
|
+ const council = agents.find((a) => a.name === 'council');
|
|
|
expect(council?.config.model).toBe(DEFAULT_MODELS.council);
|
|
|
});
|
|
|
|
|
|
- test("councillor agent uses default model", () => {
|
|
|
+ test('councillor agent uses default model', () => {
|
|
|
const agents = createAgents();
|
|
|
- const councillor = agents.find((a) => a.name === "councillor");
|
|
|
+ const councillor = agents.find((a) => a.name === 'councillor');
|
|
|
expect(councillor?.config.model).toBe(DEFAULT_MODELS.councillor);
|
|
|
});
|
|
|
|
|
|
- test("council falls back to legacy master.model when no preset override", () => {
|
|
|
+ test('council falls back to legacy master.model when no preset override', () => {
|
|
|
// Simulates a pre-1.0.0 config with council.master.model but no council
|
|
|
// entry in the agent preset — the exact scenario from issue #369.
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- oracle: { model: "openai/gpt-5.5" },
|
|
|
+ oracle: { model: 'openai/gpt-5.5' },
|
|
|
},
|
|
|
council: {
|
|
|
...councilConfig(),
|
|
|
- _legacyMasterModel: "anthropic/claude-opus-4-6",
|
|
|
+ _legacyMasterModel: 'anthropic/claude-opus-4-6',
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const council = agents.find((a) => a.name === "council");
|
|
|
- expect(council?.config.model).toBe("anthropic/claude-opus-4-6");
|
|
|
+ const council = agents.find((a) => a.name === 'council');
|
|
|
+ expect(council?.config.model).toBe('anthropic/claude-opus-4-6');
|
|
|
});
|
|
|
|
|
|
- test("council preset override takes precedence over legacy master.model", () => {
|
|
|
+ test('council preset override takes precedence over legacy master.model', () => {
|
|
|
// If user has explicit council in preset, that wins — legacy is ignored.
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- council: { model: "google/gemini-3-pro" },
|
|
|
+ council: { model: 'google/gemini-3-pro' },
|
|
|
},
|
|
|
council: {
|
|
|
...councilConfig(),
|
|
|
- _legacyMasterModel: "anthropic/claude-opus-4-6",
|
|
|
+ _legacyMasterModel: 'anthropic/claude-opus-4-6',
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const council = agents.find((a) => a.name === "council");
|
|
|
- expect(council?.config.model).toBe("google/gemini-3-pro");
|
|
|
+ const council = agents.find((a) => a.name === 'council');
|
|
|
+ expect(council?.config.model).toBe('google/gemini-3-pro');
|
|
|
});
|
|
|
|
|
|
- test("council uses default when no legacy master and no preset override", () => {
|
|
|
+ test('council uses default when no legacy master and no preset override', () => {
|
|
|
// No legacy master, no preset override → standard default
|
|
|
const config: PluginConfig = {
|
|
|
council: councilConfig(),
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const council = agents.find((a) => a.name === "council");
|
|
|
+ const council = agents.find((a) => a.name === 'council');
|
|
|
expect(council?.config.model).toBe(DEFAULT_MODELS.council);
|
|
|
});
|
|
|
|
|
|
- test("end-to-end: raw master.model config flows through schema to council agent", () => {
|
|
|
+ test('end-to-end: raw master.model config flows through schema to council agent', () => {
|
|
|
// Integration test: start from raw user config with deprecated master.model,
|
|
|
// parse through CouncilConfigSchema, then pass to createAgents.
|
|
|
// This validates the full seam between schema transform and agent resolution.
|
|
|
const rawCouncilConfig = {
|
|
|
- master: { model: "anthropic/claude-opus-4-6" },
|
|
|
+ master: { model: 'anthropic/claude-opus-4-6' },
|
|
|
presets: {
|
|
|
default: {
|
|
|
- alpha: { model: "openai/gpt-5.4-mini" },
|
|
|
+ alpha: { model: 'openai/gpt-5.4-mini' },
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
@@ -485,147 +485,147 @@ describe("council agent model resolution", () => {
|
|
|
council: parsed.data,
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const council = agents.find((a) => a.name === "council");
|
|
|
+ const council = agents.find((a) => a.name === 'council');
|
|
|
// Legacy master.model should flow through schema → agent
|
|
|
- expect(council?.config.model).toBe("anthropic/claude-opus-4-6");
|
|
|
+ expect(council?.config.model).toBe('anthropic/claude-opus-4-6');
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("options passthrough", () => {
|
|
|
- test("options are applied to agent config via overrides", () => {
|
|
|
+describe('options passthrough', () => {
|
|
|
+ test('options are applied to agent config via overrides', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
oracle: {
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- options: { textVerbosity: "low" },
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ options: { textVerbosity: 'low' },
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
- expect(oracle?.config.options).toEqual({ textVerbosity: "low" });
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
+ expect(oracle?.config.options).toEqual({ textVerbosity: 'low' });
|
|
|
});
|
|
|
|
|
|
- test("options with nested objects are passed through", () => {
|
|
|
+ test('options with nested objects are passed through', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
oracle: {
|
|
|
- model: "anthropic/claude-sonnet-4-6",
|
|
|
+ model: 'anthropic/claude-sonnet-4-6',
|
|
|
options: {
|
|
|
- thinking: { type: "enabled", budgetTokens: 16000 },
|
|
|
+ thinking: { type: 'enabled', budgetTokens: 16000 },
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
expect(oracle?.config.options).toEqual({
|
|
|
- thinking: { type: "enabled", budgetTokens: 16000 },
|
|
|
+ thinking: { type: 'enabled', budgetTokens: 16000 },
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- test("options work with other overrides", () => {
|
|
|
+ test('options work with other overrides', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
oracle: {
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- variant: "high",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ variant: 'high',
|
|
|
temperature: 0.7,
|
|
|
- options: { textVerbosity: "low", reasoningEffort: "medium" },
|
|
|
+ options: { textVerbosity: 'low', reasoningEffort: 'medium' },
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
- expect(oracle?.config.model).toBe("openai/gpt-5.5");
|
|
|
- expect(oracle?.config.variant).toBe("high");
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
+ expect(oracle?.config.model).toBe('openai/gpt-5.5');
|
|
|
+ expect(oracle?.config.variant).toBe('high');
|
|
|
expect(oracle?.config.temperature).toBe(0.7);
|
|
|
expect(oracle?.config.options).toEqual({
|
|
|
- textVerbosity: "low",
|
|
|
- reasoningEffort: "medium",
|
|
|
+ textVerbosity: 'low',
|
|
|
+ reasoningEffort: 'medium',
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- test("options are absent when not configured", () => {
|
|
|
+ test('options are absent when not configured', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
- oracle: { model: "openai/gpt-5.5" },
|
|
|
+ oracle: { model: 'openai/gpt-5.5' },
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
expect(oracle?.config.options).toBeUndefined();
|
|
|
});
|
|
|
|
|
|
- test("options flow through getAgentConfigs to SDK output", () => {
|
|
|
+ test('options flow through getAgentConfigs to SDK output', () => {
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
oracle: {
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- options: { textVerbosity: "low" },
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ options: { textVerbosity: 'low' },
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const configs = getAgentConfigs(config);
|
|
|
- expect(configs.oracle.options).toEqual({ textVerbosity: "low" });
|
|
|
+ expect(configs.oracle.options).toEqual({ textVerbosity: 'low' });
|
|
|
});
|
|
|
|
|
|
- test("options are shallow-merged with existing agent config options", () => {
|
|
|
+ test('options are shallow-merged with existing agent config options', () => {
|
|
|
// Simulate an agent factory setting default options
|
|
|
const config: PluginConfig = {
|
|
|
agents: {
|
|
|
oracle: {
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- options: { reasoningEffort: "medium" },
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ options: { reasoningEffort: 'medium' },
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
- const oracle = agents.find((a) => a.name === "oracle");
|
|
|
+ const oracle = agents.find((a) => a.name === 'oracle');
|
|
|
// Override options should merge with (not replace) any factory defaults
|
|
|
- expect(oracle?.config.options).toEqual({ reasoningEffort: "medium" });
|
|
|
+ expect(oracle?.config.options).toEqual({ reasoningEffort: 'medium' });
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("AgentOverrideConfigSchema options validation", () => {
|
|
|
- test("accepts valid options object", () => {
|
|
|
+describe('AgentOverrideConfigSchema options validation', () => {
|
|
|
+ test('accepts valid options object', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- options: { textVerbosity: "low" },
|
|
|
+ options: { textVerbosity: 'low' },
|
|
|
});
|
|
|
expect(result.success).toBe(true);
|
|
|
});
|
|
|
|
|
|
- test("accepts empty options object", () => {
|
|
|
+ test('accepts empty options object', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({ options: {} });
|
|
|
expect(result.success).toBe(true);
|
|
|
});
|
|
|
|
|
|
- test("accepts nested values in options", () => {
|
|
|
+ test('accepts nested values in options', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
options: {
|
|
|
- thinking: { type: "enabled", budgetTokens: 16000 },
|
|
|
+ thinking: { type: 'enabled', budgetTokens: 16000 },
|
|
|
},
|
|
|
});
|
|
|
expect(result.success).toBe(true);
|
|
|
});
|
|
|
|
|
|
- test("accepts options alongside other fields", () => {
|
|
|
+ test('accepts options alongside other fields', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- variant: "high",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ variant: 'high',
|
|
|
temperature: 0.7,
|
|
|
- options: { textVerbosity: "low" },
|
|
|
+ options: { textVerbosity: 'low' },
|
|
|
});
|
|
|
expect(result.success).toBe(true);
|
|
|
if (result.success) {
|
|
|
- expect(result.data.options).toEqual({ textVerbosity: "low" });
|
|
|
+ expect(result.data.options).toEqual({ textVerbosity: 'low' });
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- test("config without options is valid", () => {
|
|
|
+ test('config without options is valid', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- model: "openai/gpt-5.5",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
});
|
|
|
expect(result.success).toBe(true);
|
|
|
if (result.success) {
|
|
|
@@ -633,67 +633,67 @@ describe("AgentOverrideConfigSchema options validation", () => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- test("rejects non-object options", () => {
|
|
|
+ test('rejects non-object options', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- options: "not-an-object",
|
|
|
+ options: 'not-an-object',
|
|
|
});
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("rejects empty model arrays", () => {
|
|
|
+ test('rejects empty model arrays', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
model: [],
|
|
|
});
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("accepts prompt and orchestratorPrompt override fields", () => {
|
|
|
+ test('accepts prompt and orchestratorPrompt override fields', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- prompt: "You are a specialized reviewer.",
|
|
|
- orchestratorPrompt: "@reviewer\n- Role: Specialized reviewer",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ prompt: 'You are a specialized reviewer.',
|
|
|
+ orchestratorPrompt: '@reviewer\n- Role: Specialized reviewer',
|
|
|
});
|
|
|
expect(result.success).toBe(true);
|
|
|
if (result.success) {
|
|
|
- expect(result.data.prompt).toBe("You are a specialized reviewer.");
|
|
|
+ expect(result.data.prompt).toBe('You are a specialized reviewer.');
|
|
|
expect(result.data.orchestratorPrompt).toBe(
|
|
|
- "@reviewer\n- Role: Specialized reviewer"
|
|
|
+ '@reviewer\n- Role: Specialized reviewer',
|
|
|
);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- test("rejects empty prompt fields", () => {
|
|
|
+ test('rejects empty prompt fields', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- prompt: "",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ prompt: '',
|
|
|
});
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("rejects empty orchestratorPrompt fields", () => {
|
|
|
+ test('rejects empty orchestratorPrompt fields', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- orchestratorPrompt: "",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ orchestratorPrompt: '',
|
|
|
});
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("rejects description field on overrides", () => {
|
|
|
+ test('rejects description field on overrides', () => {
|
|
|
const result = AgentOverrideConfigSchema.safeParse({
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- description: "not supported for custom agents",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ description: 'not supported for custom agents',
|
|
|
} as Record<string, unknown>);
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("PluginConfigSchema custom-agent-only prompt fields", () => {
|
|
|
- test("rejects prompt on built-in top-level agent overrides", () => {
|
|
|
+describe('PluginConfigSchema custom-agent-only prompt fields', () => {
|
|
|
+ test('rejects prompt on built-in top-level agent overrides', () => {
|
|
|
const result = PluginConfigSchema.safeParse({
|
|
|
agents: {
|
|
|
oracle: {
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- prompt: "ignored built-in prompt override",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ prompt: 'ignored built-in prompt override',
|
|
|
},
|
|
|
},
|
|
|
});
|
|
|
@@ -701,12 +701,12 @@ describe("PluginConfigSchema custom-agent-only prompt fields", () => {
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("rejects orchestratorPrompt on built-in top-level agent overrides", () => {
|
|
|
+ test('rejects orchestratorPrompt on built-in top-level agent overrides', () => {
|
|
|
const result = PluginConfigSchema.safeParse({
|
|
|
agents: {
|
|
|
explorer: {
|
|
|
- model: "openai/gpt-5.4-mini",
|
|
|
- orchestratorPrompt: "@explorer\n- Role: should be invalid here",
|
|
|
+ model: 'openai/gpt-5.4-mini',
|
|
|
+ orchestratorPrompt: '@explorer\n- Role: should be invalid here',
|
|
|
},
|
|
|
},
|
|
|
});
|
|
|
@@ -714,13 +714,13 @@ describe("PluginConfigSchema custom-agent-only prompt fields", () => {
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("rejects custom-only prompt fields on built-in preset agents", () => {
|
|
|
+ test('rejects custom-only prompt fields on built-in preset agents', () => {
|
|
|
const result = PluginConfigSchema.safeParse({
|
|
|
presets: {
|
|
|
openai: {
|
|
|
oracle: {
|
|
|
- model: "openai/gpt-5.5",
|
|
|
- prompt: "ignored preset built-in prompt override",
|
|
|
+ model: 'openai/gpt-5.5',
|
|
|
+ prompt: 'ignored preset built-in prompt override',
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
@@ -729,13 +729,13 @@ describe("PluginConfigSchema custom-agent-only prompt fields", () => {
|
|
|
expect(result.success).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("allows prompt fields on custom agents", () => {
|
|
|
+ test('allows prompt fields on custom agents', () => {
|
|
|
const result = PluginConfigSchema.safeParse({
|
|
|
agents: {
|
|
|
janitor: {
|
|
|
- model: "openai/gpt-5.4-mini",
|
|
|
- prompt: "You are Janitor.",
|
|
|
- orchestratorPrompt: "@janitor\n- Role: Cleanup specialist",
|
|
|
+ model: 'openai/gpt-5.4-mini',
|
|
|
+ prompt: 'You are Janitor.',
|
|
|
+ orchestratorPrompt: '@janitor\n- Role: Cleanup specialist',
|
|
|
},
|
|
|
},
|
|
|
});
|
|
|
@@ -743,7 +743,7 @@ describe("PluginConfigSchema custom-agent-only prompt fields", () => {
|
|
|
expect(result.success).toBe(true);
|
|
|
});
|
|
|
|
|
|
- test("accepts sessionManager config", () => {
|
|
|
+ test('accepts sessionManager config', () => {
|
|
|
const result = PluginConfigSchema.safeParse({
|
|
|
sessionManager: {
|
|
|
maxSessionsPerAgent: 2,
|
|
|
@@ -756,136 +756,136 @@ describe("PluginConfigSchema custom-agent-only prompt fields", () => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("disabled_agents", () => {
|
|
|
- test("disabled agents are not created", () => {
|
|
|
+describe('disabled_agents', () => {
|
|
|
+ test('disabled agents are not created', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["designer", "fixer"],
|
|
|
+ disabled_agents: ['designer', 'fixer'],
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).not.toContain("designer");
|
|
|
- expect(names).not.toContain("fixer");
|
|
|
- expect(names).toContain("orchestrator");
|
|
|
- expect(names).toContain("explorer");
|
|
|
- expect(names).toContain("oracle");
|
|
|
- expect(names).toContain("librarian");
|
|
|
+ expect(names).not.toContain('designer');
|
|
|
+ expect(names).not.toContain('fixer');
|
|
|
+ expect(names).toContain('orchestrator');
|
|
|
+ expect(names).toContain('explorer');
|
|
|
+ expect(names).toContain('oracle');
|
|
|
+ expect(names).toContain('librarian');
|
|
|
});
|
|
|
|
|
|
- test("protected agents cannot be disabled", () => {
|
|
|
+ test('protected agents cannot be disabled', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["orchestrator", "councillor"],
|
|
|
+ disabled_agents: ['orchestrator', 'councillor'],
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).toContain("orchestrator");
|
|
|
- expect(names).toContain("councillor");
|
|
|
+ expect(names).toContain('orchestrator');
|
|
|
+ expect(names).toContain('councillor');
|
|
|
});
|
|
|
|
|
|
- test("disabling council disables council agent", () => {
|
|
|
+ test('disabling council disables council agent', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["council"],
|
|
|
+ disabled_agents: ['council'],
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).not.toContain("council");
|
|
|
+ expect(names).not.toContain('council');
|
|
|
// councillor is protected, it stays
|
|
|
- expect(names).toContain("councillor");
|
|
|
+ expect(names).toContain('councillor');
|
|
|
});
|
|
|
|
|
|
- test("agent count decreases when agents are disabled", () => {
|
|
|
+ test('agent count decreases when agents are disabled', () => {
|
|
|
const agents = createAgents();
|
|
|
expect(agents.length).toBe(7); // observer disabled, council unconfigured
|
|
|
|
|
|
const disabledConfig: PluginConfig = {
|
|
|
- disabled_agents: ["observer", "designer"],
|
|
|
+ disabled_agents: ['observer', 'designer'],
|
|
|
};
|
|
|
const disabledAgents = createAgents(disabledConfig);
|
|
|
expect(disabledAgents.length).toBe(6);
|
|
|
});
|
|
|
|
|
|
- test("getDisabledAgents respects protection rules", () => {
|
|
|
+ test('getDisabledAgents respects protection rules', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["orchestrator", "designer", "councillor"],
|
|
|
+ disabled_agents: ['orchestrator', 'designer', 'councillor'],
|
|
|
};
|
|
|
const disabled = getDisabledAgents(config);
|
|
|
- expect(disabled.has("designer")).toBe(true);
|
|
|
- expect(disabled.has("orchestrator")).toBe(false);
|
|
|
- expect(disabled.has("councillor")).toBe(false);
|
|
|
+ expect(disabled.has('designer')).toBe(true);
|
|
|
+ expect(disabled.has('orchestrator')).toBe(false);
|
|
|
+ expect(disabled.has('councillor')).toBe(false);
|
|
|
});
|
|
|
|
|
|
- test("getEnabledAgentNames filters correctly", () => {
|
|
|
+ test('getEnabledAgentNames filters correctly', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["designer", "fixer"],
|
|
|
+ disabled_agents: ['designer', 'fixer'],
|
|
|
};
|
|
|
const enabled = getEnabledAgentNames(config);
|
|
|
- expect(enabled).not.toContain("designer");
|
|
|
- expect(enabled).not.toContain("fixer");
|
|
|
- expect(enabled).toContain("orchestrator");
|
|
|
- expect(enabled).toContain("explorer");
|
|
|
+ expect(enabled).not.toContain('designer');
|
|
|
+ expect(enabled).not.toContain('fixer');
|
|
|
+ expect(enabled).toContain('orchestrator');
|
|
|
+ expect(enabled).toContain('explorer');
|
|
|
});
|
|
|
|
|
|
- test("getEnabledAgentNames includes enabled custom agents", () => {
|
|
|
+ test('getEnabledAgentNames includes enabled custom agents', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["janitor"],
|
|
|
+ disabled_agents: ['janitor'],
|
|
|
agents: {
|
|
|
- janitor: { model: "openai/gpt-5.4-mini" },
|
|
|
- reviewer: { model: "openai/gpt-5.4-mini" },
|
|
|
+ janitor: { model: 'openai/gpt-5.4-mini' },
|
|
|
+ reviewer: { model: 'openai/gpt-5.4-mini' },
|
|
|
},
|
|
|
};
|
|
|
|
|
|
const enabled = getEnabledAgentNames(config);
|
|
|
- expect(enabled).toContain("reviewer");
|
|
|
- expect(enabled).not.toContain("janitor");
|
|
|
+ expect(enabled).toContain('reviewer');
|
|
|
+ expect(enabled).not.toContain('janitor');
|
|
|
});
|
|
|
|
|
|
- test("empty disabled_agents creates observer but not unconfigured council", () => {
|
|
|
+ test('empty disabled_agents creates observer but not unconfigured council', () => {
|
|
|
const config: PluginConfig = {
|
|
|
disabled_agents: [],
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
const names = agents.map((a) => a.name);
|
|
|
expect(agents.length).toBe(8);
|
|
|
- expect(names).toContain("observer");
|
|
|
- expect(names).not.toContain("council");
|
|
|
+ expect(names).toContain('observer');
|
|
|
+ expect(names).not.toContain('council');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-describe("observer agent", () => {
|
|
|
- test("observer is disabled by default", () => {
|
|
|
+describe('observer agent', () => {
|
|
|
+ test('observer is disabled by default', () => {
|
|
|
const agents = createAgents();
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).not.toContain("observer");
|
|
|
+ expect(names).not.toContain('observer');
|
|
|
});
|
|
|
|
|
|
- test("observer is enabled when removed from disabled_agents", () => {
|
|
|
+ test('observer is enabled when removed from disabled_agents', () => {
|
|
|
const config: PluginConfig = {
|
|
|
disabled_agents: [],
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).toContain("observer");
|
|
|
+ expect(names).toContain('observer');
|
|
|
});
|
|
|
|
|
|
- test("observer is disabled when explicitly listed", () => {
|
|
|
+ test('observer is disabled when explicitly listed', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["observer"],
|
|
|
+ disabled_agents: ['observer'],
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).not.toContain("observer");
|
|
|
+ expect(names).not.toContain('observer');
|
|
|
});
|
|
|
|
|
|
- test("observer can be enabled alongside other disabled agents", () => {
|
|
|
+ test('observer can be enabled alongside other disabled agents', () => {
|
|
|
const config: PluginConfig = {
|
|
|
- disabled_agents: ["designer"],
|
|
|
+ disabled_agents: ['designer'],
|
|
|
};
|
|
|
const agents = createAgents(config);
|
|
|
const names = agents.map((a) => a.name);
|
|
|
- expect(names).toContain("observer");
|
|
|
- expect(names).not.toContain("designer");
|
|
|
+ expect(names).toContain('observer');
|
|
|
+ expect(names).not.toContain('designer');
|
|
|
});
|
|
|
|
|
|
- test("DEFAULT_DISABLED_AGENTS contains observer", () => {
|
|
|
- expect(DEFAULT_DISABLED_AGENTS).toContain("observer");
|
|
|
+ test('DEFAULT_DISABLED_AGENTS contains observer', () => {
|
|
|
+ expect(DEFAULT_DISABLED_AGENTS).toContain('observer');
|
|
|
});
|
|
|
});
|