+176
-28
@@ -9,7 +9,7 @@ import type { Info, ToolContext, ToolEditor } from "@opencode/plugin/promise/too
|
||||
import type { JsonValue, ModelInfo, SessionGetInput } from "@opencode/client";
|
||||
import type { SessionContext } from "@opencode/plugin/promise/session";
|
||||
import { z } from "zod";
|
||||
import { gptAgents } from "../src/config.js";
|
||||
import { gptAgents, settings, workerNames } from "../src/config.js";
|
||||
import { basePlugin as plugin } from "../src/index.js";
|
||||
import { playwrightMcpCommand } from "../src/playwright.js";
|
||||
import { bindingKey } from "../src/multi-work.js";
|
||||
@@ -27,7 +27,31 @@ function reasoningModel(id: string): ModelInfo {
|
||||
})),
|
||||
};
|
||||
}
|
||||
function providerModel(provider: string, id: string): ModelInfo {
|
||||
return {
|
||||
...reasoningModel(id),
|
||||
providerID: Provider.ID.make(provider),
|
||||
};
|
||||
}
|
||||
const allModels = ["gpt-5.6-luna", "gpt-5.6-sol", "gpt-6-astra"].map(reasoningModel);
|
||||
type CustomAssignment = {
|
||||
model: string;
|
||||
effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
||||
};
|
||||
const customAssignments = (provider = "test") => {
|
||||
const assignment = (name: (typeof workerNames)[number]): CustomAssignment => ({
|
||||
...gptAgents[name],
|
||||
model: `${provider}/${gptAgents[name].model}`,
|
||||
});
|
||||
return {
|
||||
"fast-lo": assignment("fast-lo"),
|
||||
"fast-hi": assignment("fast-hi"),
|
||||
"good-lo": assignment("good-lo"),
|
||||
"good-hi": assignment("good-hi"),
|
||||
"deep-lo": assignment("deep-lo"),
|
||||
"deep-hi": assignment("deep-hi"),
|
||||
};
|
||||
};
|
||||
const cleanups: Plugin.Cleanup[] = [];
|
||||
afterEach(async () => {
|
||||
for (const cleanup of cleanups.splice(0)) await cleanup();
|
||||
@@ -352,15 +376,20 @@ test("orchestration inherits deep-lo and falls back to good-lo with available wo
|
||||
expect((await host(allModels.slice(1))).agents.has("coordinate")).toBe(false);
|
||||
|
||||
const customAgents = {
|
||||
...gptAgents,
|
||||
"good-lo": { model: "custom-good", effort: "high" as const },
|
||||
"deep-lo": { model: "custom-deep", effort: "medium" as const },
|
||||
...customAssignments(),
|
||||
"good-lo": {
|
||||
model: "test/custom-good",
|
||||
effort: "high" as const,
|
||||
},
|
||||
"deep-lo": {
|
||||
model: "test/custom-deep",
|
||||
effort: "medium" as const,
|
||||
},
|
||||
};
|
||||
const custom = await host(
|
||||
[...allModels, reasoningModel("custom-good"), reasoningModel("custom-deep")],
|
||||
{
|
||||
mode: "custom",
|
||||
provider: "test",
|
||||
agents: customAgents,
|
||||
},
|
||||
);
|
||||
@@ -370,7 +399,6 @@ test("orchestration inherits deep-lo and falls back to good-lo with available wo
|
||||
});
|
||||
const customFallback = await host([...allModels, reasoningModel("custom-good")], {
|
||||
mode: "custom",
|
||||
provider: "test",
|
||||
agents: customAgents,
|
||||
});
|
||||
expect(customFallback.agents.get("coordinate")?.model).toMatchObject({
|
||||
@@ -382,13 +410,106 @@ test("orchestration inherits deep-lo and falls back to good-lo with available wo
|
||||
(
|
||||
await host(allModels, {
|
||||
mode: "custom",
|
||||
provider: "test",
|
||||
agents: customAgents,
|
||||
})
|
||||
).agents.has("coordinate"),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test("custom assignments resolve duplicate model IDs by provider and refresh provider-aware fallback", async () => {
|
||||
const agents = customAssignments("alpha");
|
||||
agents["fast-lo"] = { ...agents["fast-lo"], model: "beta/shared" };
|
||||
agents["fast-hi"] = { ...agents["fast-hi"], model: "alpha/shared" };
|
||||
agents["deep-lo"] = { ...agents["deep-lo"], model: "beta/deep" };
|
||||
agents["deep-hi"] = { ...agents["deep-hi"], model: "beta/deep" };
|
||||
const base = [
|
||||
providerModel("alpha", "shared"),
|
||||
providerModel("beta", "shared"),
|
||||
providerModel("alpha", "gpt-5.6-sol"),
|
||||
providerModel("alpha", "gpt-6-astra"),
|
||||
];
|
||||
const result = await host(base, { mode: "custom", agents });
|
||||
expect(String(result.agents.get("fast-lo")?.model?.providerID)).toBe("beta");
|
||||
expect(String(result.agents.get("fast-hi")?.model?.providerID)).toBe("alpha");
|
||||
expect(String(result.agents.get("coordinate")?.model?.id)).toBe("gpt-5.6-sol");
|
||||
expect(result.agents.has("deep-lo")).toBe(false);
|
||||
|
||||
await result.refresh([...base, providerModel("beta", "deep")]);
|
||||
expect(result.agents.get("coordinate")?.model).toMatchObject({
|
||||
providerID: "beta",
|
||||
id: "deep",
|
||||
});
|
||||
expect(result.agents.has("deep-lo")).toBe(true);
|
||||
|
||||
const nested = customAssignments("alpha");
|
||||
nested["fast-lo"] = { ...nested["fast-lo"], model: "alpha/group/shared" };
|
||||
const nestedResult = await host(
|
||||
[...base, providerModel("alpha", "gpt-5.6-luna"), providerModel("alpha", "group/shared")],
|
||||
{ mode: "custom", agents: nested },
|
||||
);
|
||||
expect(String(nestedResult.agents.get("fast-lo")?.model?.id)).toBe("group/shared");
|
||||
});
|
||||
|
||||
test("custom configuration requires qualified models and rejects obsolete fields", () => {
|
||||
expect(() => settings.parse({ mode: "custom", agents: customAssignments() })).not.toThrow();
|
||||
for (const model of ["bare", "/model", "provider/"])
|
||||
expect(() =>
|
||||
settings.parse({
|
||||
mode: "custom",
|
||||
agents: {
|
||||
...customAssignments(),
|
||||
"fast-lo": { model, effort: "low" },
|
||||
},
|
||||
}),
|
||||
).toThrow();
|
||||
const nested = customAssignments();
|
||||
nested["fast-lo"] = { model: "provider/group/model", effort: "low" };
|
||||
expect(() => settings.parse({ mode: "custom", agents: nested })).not.toThrow();
|
||||
expect(() =>
|
||||
settings.parse({ mode: "custom", provider: "test", agents: customAssignments() }),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
settings.parse({ mode: "custom", lunaPriority: false, agents: customAssignments() }),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
settings.parse({
|
||||
mode: "custom",
|
||||
agents: {
|
||||
...customAssignments(),
|
||||
"fast-lo": { ...customAssignments()["fast-lo"], provider: "test" },
|
||||
},
|
||||
}),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
settings.parse({
|
||||
mode: "custom",
|
||||
agents: {
|
||||
...customAssignments(),
|
||||
"fast-lo": { ...customAssignments()["fast-lo"], lunaPriority: true },
|
||||
},
|
||||
}),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
settings.parse({
|
||||
mode: "custom",
|
||||
agents: { ...customAssignments(), coordinate: customAssignments()["deep-lo"] },
|
||||
}),
|
||||
).not.toThrow();
|
||||
for (const obsolete of [{ provider: "test" }, { lunaPriority: true }])
|
||||
expect(() =>
|
||||
settings.parse({
|
||||
mode: "custom",
|
||||
agents: {
|
||||
...customAssignments(),
|
||||
coordinate: {
|
||||
...customAssignments()["deep-lo"],
|
||||
...obsolete,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test("worker inheritance and coordinator isolation preserve build and default", async () => {
|
||||
const result = await host();
|
||||
expect(result.defaultAgent).toBe("build");
|
||||
@@ -689,33 +810,58 @@ test("coordinate prompt admission requires owned ready placement; local and move
|
||||
|
||||
test("reasoning settings and Luna priority use native advertised or official support", async () => {
|
||||
const luna = {
|
||||
...model("gpt-5.6-luna"),
|
||||
...providerModel("openai", "gpt-5.6-luna"),
|
||||
package: "@opencode/ai/providers/openai",
|
||||
settings: { baseURL: "https://api.openai.com/v1" },
|
||||
};
|
||||
const ordinary = await host();
|
||||
const openaiModels = [
|
||||
luna,
|
||||
providerModel("openai", "gpt-5.6-sol"),
|
||||
providerModel("openai", "gpt-6-astra"),
|
||||
];
|
||||
const ordinary = await host(
|
||||
[providerModel("openai", "gpt-5.6-luna"), ...openaiModels.slice(1)],
|
||||
{ provider: "openai" },
|
||||
);
|
||||
expect(ordinary.agents.get("fast-lo")?.request.settings).toEqual({});
|
||||
expect(String(ordinary.agents.get("fast-lo")?.model?.variant)).toBe("low");
|
||||
const official = await host([luna, ...allModels.slice(1)]);
|
||||
expect(official.agents.get("fast-hi")?.request.settings).toEqual({
|
||||
reasoningEffort: "high",
|
||||
serviceTier: "priority",
|
||||
});
|
||||
const proxy = await host([
|
||||
{ ...luna, settings: { baseURL: "https://proxy.example/v1" } },
|
||||
...allModels.slice(1),
|
||||
]);
|
||||
expect(proxy.agents.get("fast-lo")?.request.settings).toEqual({ reasoningEffort: "low" });
|
||||
const advertised = await host([
|
||||
{ ...luna, package: "custom", settings: { serviceTier: "fast" } },
|
||||
...allModels.slice(1),
|
||||
]);
|
||||
const official = await host(openaiModels, { provider: "openai" });
|
||||
expect(official.agents.get("fast-hi")?.request.settings).toEqual({ serviceTier: "priority" });
|
||||
const proxy = await host(
|
||||
[{ ...luna, settings: { baseURL: "https://proxy.example/v1" } }, ...openaiModels.slice(1)],
|
||||
{ provider: "openai" },
|
||||
);
|
||||
expect(proxy.agents.get("fast-lo")?.request.settings).toEqual({});
|
||||
const advertised = await host(
|
||||
[
|
||||
{ ...luna, package: "custom", settings: { serviceTier: "fast" } },
|
||||
...openaiModels.slice(1),
|
||||
],
|
||||
{ provider: "openai" },
|
||||
);
|
||||
expect(advertised.agents.get("fast-lo")?.request.settings.serviceTier).toBe("fast");
|
||||
const disabled = await host([luna, ...allModels.slice(1)], {
|
||||
provider: "test",
|
||||
const sdkAlias = await host(
|
||||
[{ ...luna, package: "@ai-sdk/openai" }, ...openaiModels.slice(1)],
|
||||
{ provider: "openai" },
|
||||
);
|
||||
expect(sdkAlias.agents.get("fast-lo")?.request.settings.serviceTier).toBe("priority");
|
||||
const disabled = await host(openaiModels, {
|
||||
provider: "openai",
|
||||
lunaPriority: false,
|
||||
});
|
||||
expect(disabled.agents.get("fast-lo")?.request.settings).toEqual({ reasoningEffort: "low" });
|
||||
expect(disabled.agents.get("fast-lo")?.request.settings).toEqual({});
|
||||
|
||||
const custom = customAssignments("openai");
|
||||
const customResult = await host(openaiModels, {
|
||||
mode: "custom",
|
||||
agents: custom,
|
||||
});
|
||||
expect(customResult.agents.get("fast-lo")?.request.settings.serviceTier).toBeUndefined();
|
||||
expect(customResult.agents.get("fast-hi")?.request.settings.serviceTier).toBeUndefined();
|
||||
|
||||
const other = await host(allModels, { provider: "test" });
|
||||
expect(other.agents.get("fast-lo")?.request.settings.serviceTier).toBeUndefined();
|
||||
expect(() => settings.parse({ mode: "gpt", provider: "test", lunaPriority: true })).toThrow();
|
||||
});
|
||||
|
||||
test("coordinator uses inherited effort and ignores the legacy custom assignment", async () => {
|
||||
@@ -737,8 +883,10 @@ test("coordinator uses inherited effort and ignores the legacy custom assignment
|
||||
|
||||
const custom = await host([...allModels, model("local")], {
|
||||
mode: "custom",
|
||||
provider: "test",
|
||||
agents: { ...gptAgents, coordinate: { model: "local", effort: "xhigh" } },
|
||||
agents: {
|
||||
...customAssignments(),
|
||||
coordinate: { model: "test/local", effort: "xhigh" },
|
||||
},
|
||||
});
|
||||
expect(custom.agents.get("coordinate")?.model).toMatchObject({
|
||||
id: "gpt-6-astra",
|
||||
|
||||
Reference in New Issue
Block a user