+24
-22
@@ -31,24 +31,29 @@ const agents = {
|
||||
"deep-lo": "test/gpt-6-astra#low",
|
||||
"deep-hi": "test/gpt-6-astra#high",
|
||||
};
|
||||
const defaultOptions = { agents };
|
||||
const defaultLocation = { directory: "/fixture" };
|
||||
async function flush() {
|
||||
await Bun.sleep(10);
|
||||
}
|
||||
async function host(
|
||||
catalog: ModelInfo[] = models,
|
||||
options: Plugin.Context["options"] = { agents },
|
||||
options: Plugin.Context["options"] = defaultOptions,
|
||||
existingSkill?: Skill.Info,
|
||||
) {
|
||||
const agents = new Map<string, DeepMutable<Agent.Info>>();
|
||||
const registry = new Map<string, DeepMutable<Agent.Info>>();
|
||||
let build: DeepMutable<Agent.Info> = Agent.Info.default(Agent.ID.make("build"));
|
||||
const skills = new Map<string, Skill.Info>(
|
||||
existingSkill ? [[existingSkill.id, existingSkill]] : [],
|
||||
);
|
||||
let transform: (editor: AgentEditor) => void;
|
||||
let transform: ((editor: AgentEditor) => void) | undefined;
|
||||
let contextHook: (event: Pick<SessionContext, "agent" | "tools">) => Promise<void> | void;
|
||||
let promptHook: (event: { sessionID: string }) => Promise<void> | void;
|
||||
let before: (event: { tool: string; agent: string; input: unknown }) => Promise<void> | void;
|
||||
let parent = "build";
|
||||
let child = "fast-lo";
|
||||
let tools: string[] = [];
|
||||
let toolTransform: (editor: ToolEditor) => void;
|
||||
let toolTransform: ((editor: ToolEditor) => void) | undefined;
|
||||
let permissions: () => Promise<Agent.Info["permissions"]>;
|
||||
let controller: ReadableStreamDefaultController<unknown>;
|
||||
const stream = new ReadableStream({
|
||||
@@ -58,20 +63,20 @@ async function host(
|
||||
});
|
||||
const registration = { async dispose() {} };
|
||||
const replay = () => {
|
||||
agents.clear();
|
||||
agents.set("build", structuredClone(build));
|
||||
registry.clear();
|
||||
registry.set("build", structuredClone(build));
|
||||
transform?.({
|
||||
get: (id) => agents.get(id),
|
||||
list: () => [...agents.values()],
|
||||
get: (id) => registry.get(id),
|
||||
list: () => [...registry.values()],
|
||||
default() {},
|
||||
remove: (id) => {
|
||||
agents.delete(id);
|
||||
registry.delete(id);
|
||||
},
|
||||
update(id, update) {
|
||||
const draft =
|
||||
agents.get(id) ?? structuredClone(Agent.Info.default(Agent.ID.make(id)));
|
||||
registry.get(id) ?? structuredClone(Agent.Info.default(Agent.ID.make(id)));
|
||||
update(draft);
|
||||
agents.set(id, draft);
|
||||
registry.set(id, draft);
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -110,7 +115,7 @@ async function host(
|
||||
},
|
||||
agent: {
|
||||
async list() {
|
||||
return { data: [...agents.values()] };
|
||||
return { data: [...registry.values()] };
|
||||
},
|
||||
async reload() {
|
||||
replay();
|
||||
@@ -182,11 +187,8 @@ async function host(
|
||||
replay();
|
||||
const cleanup = await setup(context);
|
||||
if (cleanup) cleanups.push(cleanup);
|
||||
const flush = async () => {
|
||||
await Bun.sleep(10);
|
||||
};
|
||||
return {
|
||||
agents,
|
||||
agents: registry,
|
||||
skills,
|
||||
permissions: () => permissions(),
|
||||
async configure(rules: Agent.Info["permissions"]) {
|
||||
@@ -195,7 +197,7 @@ async function host(
|
||||
controller.enqueue({ type: "agent.updated" });
|
||||
await flush();
|
||||
},
|
||||
async models(next: ModelInfo[], location = { directory: "/fixture" }) {
|
||||
async models(next: ModelInfo[], location = defaultLocation) {
|
||||
catalog = next;
|
||||
controller.enqueue({ type: "model.updated", location });
|
||||
await flush();
|
||||
@@ -205,10 +207,10 @@ async function host(
|
||||
replayTools();
|
||||
await flush();
|
||||
},
|
||||
async delegate(agent: string, target: string) {
|
||||
delegate: async (agent: string, target: string) => {
|
||||
await before({ tool: "subagent", agent, input: { agent: target } });
|
||||
},
|
||||
async command(owner: string, target: string) {
|
||||
command: async (owner: string, target: string) => {
|
||||
parent = owner;
|
||||
child = target;
|
||||
await promptHook({ sessionID: "child" });
|
||||
@@ -231,7 +233,7 @@ async function host(
|
||||
|
||||
test("worker availability, deep fallback, and model refresh remain provider aware", async () => {
|
||||
const h = await host();
|
||||
expect([...h.skills.keys()].sort()).toEqual(["anti-overengineering", "task-planning"]);
|
||||
expect([...h.skills.keys()].toSorted()).toEqual(["anti-overengineering", "task-planning"]);
|
||||
expect(String(h.agents.get("coordinate")?.model?.id)).toBe("gpt-6-astra");
|
||||
await h.models(models.slice(0, 2));
|
||||
expect(String(h.agents.get("coordinate")?.model?.id)).toBe("gpt-6.1-sol");
|
||||
@@ -256,7 +258,7 @@ test("assignments are provider-qualified with optional deep pair and variant; pa
|
||||
const shallow = Object.fromEntries(
|
||||
Object.entries(agents).filter(([name]) => !name.startsWith("deep-")),
|
||||
);
|
||||
const duplicate = models.map((model) => ({ ...model, providerID: Provider.ID.make("other") }));
|
||||
const duplicate = models.map((entry) => ({ ...entry, providerID: Provider.ID.make("other") }));
|
||||
const h = await host([...duplicate, ...models], { agents: shallow });
|
||||
expect(String(h.agents.get("coordinate")?.model?.providerID)).toBe("test");
|
||||
expect(String(h.agents.get("coordinate")?.model?.id)).toBe("gpt-6.1-sol");
|
||||
@@ -295,6 +297,6 @@ test("native tool and command paths retain core delegation restrictions", async
|
||||
expect(
|
||||
z
|
||||
.object({ agent: z.object({ type: z.string(), enum: z.array(z.string()) }) })
|
||||
.parse((await h.request("coordinate")).subagent?.input.properties).agent,
|
||||
.parse((await h.request("coordinate"))["subagent"]?.input["properties"]).agent,
|
||||
).toEqual({ type: "string", enum: ["fast-lo", "fast-hi", "good-lo", "good-hi"] });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user