Skip to content

Commit 33bbd6e

Browse files
committed
fix(agent): match muse-wonder models and scope muse rules to leaves
1 parent afa244c commit 33bbd6e

6 files changed

Lines changed: 80 additions & 32 deletions

File tree

‎src/agent/model-family-policy.test.ts‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,30 @@ describe("resolveModelFamilyPolicy", () => {
9090
}
9191
});
9292

93-
test("muse spark carries tool-discipline rules; other families do not", () => {
94-
const muse = resolveModelFamilyPolicy({
95-
providerName: "opencode-go/acme",
96-
model: "muse-spark-1.3-contributor",
97-
});
93+
test("muse leaves carry tool-discipline rules; muse orchestrators and other families do not", () => {
94+
for (const model of [
95+
"muse-spark-1.3-contributor",
96+
"muse-wonder-1.0",
97+
] as const) {
98+
const leaf = resolveModelFamilyPolicy({
99+
providerName: "meta-api",
100+
model,
101+
orchestrator: false,
102+
});
103+
const orchestrator = resolveModelFamilyPolicy({
104+
providerName: "meta-api",
105+
model,
106+
orchestrator: true,
107+
});
108+
expect(leaf.family).toBe("muse");
109+
expect(leaf.toolDisciplineRules?.length).toBeGreaterThan(0);
110+
expect(orchestrator.family).toBe("muse");
111+
expect(orchestrator.toolDisciplineRules).toBeUndefined();
112+
}
98113
const base = resolveModelFamilyPolicy({
99114
providerName: "anthropic",
100115
model: "claude-sonnet-4",
101116
});
102-
expect(muse.family).toBe("muse");
103-
expect(muse.toolDisciplineRules?.length).toBeGreaterThan(0);
104117
expect(base.toolDisciplineRules).toBeUndefined();
105118
});
106119

‎src/agent/model-family-policy.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface ModelFamilyPolicy {
3838
* not self-terminate a tool loop. Appended at the tail to keep the cached
3939
* prompt prefix intact.
4040
*/
41-
toolDisciplineRules?: string;
41+
toolDisciplineRules?: string | undefined;
4242
/**
4343
* Provider-family residual appended once to the leaf system prompt
4444
* (tool-budget text for grok, XML task_guidance for claude, narrate
@@ -224,7 +224,13 @@ export function resolveModelFamilyPolicy(input: {
224224
...KIMI_POLICY,
225225
};
226226
case "muse":
227-
return { family, ...MUSE_POLICY };
227+
return {
228+
family,
229+
...MUSE_POLICY,
230+
toolDisciplineRules: orchestrator
231+
? undefined
232+
: MUSE_POLICY.toolDisciplineRules,
233+
};
228234
case "claude":
229235
// Like grok's finish-bias, task_guidance only makes sense on leaf
230236
// workers — orchestrators get the permissive default.

‎src/provider/reasoning-effort.test.ts‎

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -127,27 +127,33 @@ describe("supportedEfforts", () => {
127127
expect(supportedEfforts("deepseek-v3")).toEqual(["low", "medium", "high"]);
128128
});
129129

130-
// Every catalog Muse Spark id reaches supportedEfforts unnormalized; two
131-
// ids pin the family rule.
132-
test.each(["muse-spark-1.3-contributor", "muse-spark-1.3-contributor-free"])(
133-
"Muse Spark id %s supports minimal through high",
134-
(model) => {
135-
expect(supportedEfforts(model)).toEqual([
136-
"minimal",
137-
"low",
138-
"medium",
139-
"high",
140-
]);
141-
expect(defaultEffortForModel(model)).toBe("low");
142-
},
143-
);
130+
// Every catalog Muse id reaches supportedEfforts unnormalized; spark and
131+
// wonder ids pin the family rule.
132+
test.each([
133+
"muse-spark-1.3-contributor",
134+
"muse-spark-1.3-contributor-free",
135+
"muse-wonder-1.0",
136+
])("Muse id %s supports minimal through high", (model) => {
137+
expect(supportedEfforts(model)).toEqual([
138+
"minimal",
139+
"low",
140+
"medium",
141+
"high",
142+
]);
143+
expect(defaultEffortForModel(model)).toBe("low");
144+
});
144145

145-
test("a model merely containing muse-spark is not matched", () => {
146+
test("a model merely containing muse- is not matched", () => {
146147
expect(supportedEfforts("not-muse-spark-1.3")).toEqual([
147148
"low",
148149
"medium",
149150
"high",
150151
]);
152+
expect(supportedEfforts("not-muse-wonder-1.0")).toEqual([
153+
"low",
154+
"medium",
155+
"high",
156+
]);
151157
});
152158
});
153159

@@ -175,11 +181,13 @@ describe("validateEffort", () => {
175181
expect(validateEffort("grok-4.5", "xhigh").ok).toBe(false);
176182
});
177183

178-
test("accepts minimal on Muse Spark and rejects none", () => {
179-
expect(validateEffort("muse-spark-1.3-contributor", "minimal")).toEqual({
180-
ok: true,
181-
});
182-
expect(validateEffort("muse-spark-1.3-contributor", "none").ok).toBe(false);
184+
test("accepts minimal on Muse models and rejects none", () => {
185+
for (const model of ["muse-spark-1.3-contributor", "muse-wonder-1.0"]) {
186+
expect(validateEffort(model, "minimal")).toEqual({
187+
ok: true,
188+
});
189+
expect(validateEffort(model, "none").ok).toBe(false);
190+
}
183191
});
184192

185193
test("rejects medium on glm-5.3 family", () => {

‎src/provider/reasoning-effort.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const MUSE_SPARK_EFFORTS: readonly ReasoningEffort[] = DEFAULT_EFFORTS;
8888
// and nothing normalizes the model string first. Mirrors the grok/kimi checks
8989
// in src/subagent/provider-family.ts.
9090
function isMuseSparkModel(model: string): boolean {
91-
return /^muse-spark/i.test(model.trim());
91+
return /^muse-/i.test(model.trim());
9292
}
9393

9494
// grok-4.6/4.7 accept xhigh (CODEX_EFFORTS); grok-4.5 and composer stay on

‎src/subagent/provider-family.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
isDeepSeekLeafProvider,
77
isGptProvider,
88
isKimiLeafProvider,
9+
isMuseSparkLeafProvider,
910
isXaiGrokLeafProvider,
1011
shouldApplyGrokAntiThrash,
1112
} from "./provider-family.js";
@@ -67,13 +68,33 @@ describe("isKimiLeafProvider", () => {
6768
);
6869
});
6970

71+
describe("isMuseSparkLeafProvider", () => {
72+
test.each<[ProviderRow, boolean]>([
73+
[
74+
{ providerName: "opencode-go", model: "muse-spark-1.3-contributor" },
75+
true,
76+
],
77+
[{ providerName: "meta-api", model: "muse-wonder-1.0" }, true],
78+
[{ providerName: "meta/default", model: "MUSE-WONDER-1.0" }, true],
79+
[{ providerName: "meta-api", model: "not-muse-wonder-1.0" }, false],
80+
[{ providerName: "meta-api" }, false],
81+
])("matches %j -> %s", (row, expected) => {
82+
expect(isMuseSparkLeafProvider(row)).toBe(expected);
83+
});
84+
});
85+
7086
describe("detectModelFamily", () => {
7187
test.each<[ProviderRow, ReturnType<typeof detectModelFamily>]>([
7288
[{ providerName: "xai/default", model: "grok-4.5" }, "grok"],
7389
[{ providerName: "xai/default", model: "grok-4.6" }, "grok"],
7490
[{ providerName: "xai/default", model: "grok-4.7" }, "grok"],
7591
[{ providerName: "moonshot", model: "kimi-k2" }, "kimi"],
7692
[{ providerName: "opencode-go", model: "kimi-k3" }, "kimi"],
93+
[
94+
{ providerName: "opencode-go", model: "muse-spark-1.3-contributor" },
95+
"muse",
96+
],
97+
[{ providerName: "meta-api", model: "muse-wonder-1.0" }, "muse"],
7798
[{ providerName: "anthropic", model: "claude-sonnet-4" }, "claude"],
7899
[{ providerName: "openai-compat", model: "claude-opus-4-6" }, "claude"],
79100
[{ providerName: "openai", model: "gpt-5.6" }, "gpt"],

‎src/subagent/provider-family.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ export function isKimiLeafProvider(input: {
4343
return false;
4444
}
4545

46-
/** True when the provider/model is the OpenCode Go Muse Spark family. */
46+
/** True when the provider/model is the Meta Muse family (muse-spark, muse-wonder). */
4747
export function isMuseSparkLeafProvider(input: {
4848
providerName: string;
4949
model?: string;
5050
}): boolean {
51-
return input.model !== undefined && /^muse-spark/i.test(input.model.trim());
51+
return input.model !== undefined && /^muse-/i.test(input.model.trim());
5252
}
5353

5454
/** True when the provider/model is Anthropic's Claude family. */

0 commit comments

Comments
 (0)