
















@@ -7,29 +7,37 @@ import {
77splitModelRef,
88} from "./subagent-spawn-plan.js";
9910+type SubagentModelPlan = ReturnType<typeof resolveSubagentModelAndThinkingPlan>;
11+type OkSubagentModelPlan = Extract<SubagentModelPlan, { status: "ok" }>;
12+1013function createConfig(overrides?: Record<string, unknown>): OpenClawConfig {
1114return {
1215session: { mainKey: "main", scope: "per-sender" },
1316 ...overrides,
1417} as OpenClawConfig;
1518}
161920+function expectOkPlan(plan: SubagentModelPlan): OkSubagentModelPlan {
21+expect(plan.status).toBe("ok");
22+if (plan.status !== "ok") {
23+throw new Error(`Expected ok plan, received ${plan.status}`);
24+}
25+return plan;
26+}
27+1728describe("subagent spawn model + thinking plan", () => {
1829it("includes explicit model overrides in the initial patch", () => {
19-const plan = resolveSubagentModelAndThinkingPlan({
20-cfg: createConfig(),
21-targetAgentId: "research",
22-modelOverride: "claude-haiku-4-5",
23-});
24-expect(plan).toMatchObject({
25-status: "ok",
26-resolvedModel: "claude-haiku-4-5",
27-modelApplied: true,
28-initialSessionPatch: {
29-model: "claude-haiku-4-5",
30-modelOverrideSource: "user",
31-},
32-});
30+const plan = expectOkPlan(
31+resolveSubagentModelAndThinkingPlan({
32+cfg: createConfig(),
33+targetAgentId: "research",
34+modelOverride: "claude-haiku-4-5",
35+}),
36+);
37+expect(plan.resolvedModel).toBe("claude-haiku-4-5");
38+expect(plan.modelApplied).toBe(true);
39+expect(plan.initialSessionPatch.model).toBe("claude-haiku-4-5");
40+expect(plan.initialSessionPatch.modelOverrideSource).toBe("user");
3341});
34423543it("preserves model ids containing slashes", () => {
@@ -40,18 +48,15 @@ describe("subagent spawn model + thinking plan", () => {
4048});
41494250it("normalizes thinking overrides into the initial patch", () => {
43-const plan = resolveSubagentModelAndThinkingPlan({
44-cfg: createConfig(),
45-targetAgentId: "research",
46-thinkingOverrideRaw: "high",
47-});
48-expect(plan).toMatchObject({
49-status: "ok",
50-thinkingOverride: "high",
51-initialSessionPatch: {
52-thinkingLevel: "high",
53-},
54-});
51+const plan = expectOkPlan(
52+resolveSubagentModelAndThinkingPlan({
53+cfg: createConfig(),
54+targetAgentId: "research",
55+thinkingOverrideRaw: "high",
56+}),
57+);
58+expect(plan.thinkingOverride).toBe("high");
59+expect(plan.initialSessionPatch.thinkingLevel).toBe("high");
5560});
56615762it("rejects invalid thinking levels before any runtime work", () => {
@@ -60,38 +65,37 @@ describe("subagent spawn model + thinking plan", () => {
6065targetAgentId: "research",
6166thinkingOverrideRaw: "banana",
6267});
63-expect(plan).toMatchObject({
64-status: "error",
65-});
68+expect(plan.status).toBe("error");
6669if (plan.status === "error") {
6770expect(plan.error).toMatch(/Invalid thinking level/i);
6871}
6972});
70737174it("applies default subagent model from defaults config", () => {
72-const plan = resolveSubagentModelAndThinkingPlan({
73-cfg: createConfig({
74-agents: { defaults: { subagents: { model: "minimax/MiniMax-M2.7" } } },
75+const plan = expectOkPlan(
76+resolveSubagentModelAndThinkingPlan({
77+cfg: createConfig({
78+agents: { defaults: { subagents: { model: "minimax/MiniMax-M2.7" } } },
79+}),
80+targetAgentId: "research",
7581}),
76-targetAgentId: "research",
77-});
78-expect(plan).toMatchObject({
79-status: "ok",
80-resolvedModel: "minimax/MiniMax-M2.7",
81-initialSessionPatch: { model: "minimax/MiniMax-M2.7", modelOverrideSource: "auto" },
82-});
82+);
83+expect(plan.resolvedModel).toBe("minimax/MiniMax-M2.7");
84+expect(plan.initialSessionPatch.model).toBe("minimax/MiniMax-M2.7");
85+expect(plan.initialSessionPatch.modelOverrideSource).toBe("auto");
8386});
84878588it("falls back to runtime default model when no model config is set", () => {
86-const plan = resolveSubagentModelAndThinkingPlan({
87-cfg: createConfig(),
88-targetAgentId: "research",
89-});
90-expect(plan).toMatchObject({
91-status: "ok",
92-resolvedModel: `${DEFAULT_PROVIDER}/${DEFAULT_MODEL}`,
93-initialSessionPatch: { model: `${DEFAULT_PROVIDER}/${DEFAULT_MODEL}` },
94-});
89+const plan = expectOkPlan(
90+resolveSubagentModelAndThinkingPlan({
91+cfg: createConfig(),
92+targetAgentId: "research",
93+}),
94+);
95+const defaultModelRef = `${DEFAULT_PROVIDER}/${DEFAULT_MODEL}`;
96+expect(plan.resolvedModel).toBe(defaultModelRef);
97+expect(plan.initialSessionPatch.model).toBe(defaultModelRef);
98+expect(plan.initialSessionPatch.modelOverrideSource).toBe("auto");
9599});
9610097101it("prefers per-agent subagent model over defaults", () => {
@@ -105,16 +109,16 @@ describe("subagent spawn model + thinking plan", () => {
105109id: "research",
106110subagents: { model: "opencode/claude" },
107111};
108-const plan = resolveSubagentModelAndThinkingPlan({
109-cfg,
110-targetAgentId: "research",
111-targetAgentConfig,
112-});
113-expect(plan).toMatchObject({
114- status: "ok",
115- resolvedModel: "opencode/claude",
116- initialSessionPatch: { model: "opencode/claude" },
117-});
112+const plan = expectOkPlan(
113+resolveSubagentModelAndThinkingPlan({
114+ cfg,
115+ targetAgentId: "research",
116+ targetAgentConfig,
117+ }),
118+);
119+expect(plan.resolvedModel).toBe("opencode/claude");
120+expect(plan.initialSessionPatch.model).toBe("opencode/claude");
121+expect(plan.initialSessionPatch.modelOverrideSource).toBe("auto");
118122});
119123120124it("prefers target agent primary model over global default", () => {
@@ -128,16 +132,16 @@ describe("subagent spawn model + thinking plan", () => {
128132id: "research",
129133model: { primary: "opencode/claude" },
130134};
131-const plan = resolveSubagentModelAndThinkingPlan({
132-cfg,
133-targetAgentId: "research",
134-targetAgentConfig,
135-});
136-expect(plan).toMatchObject({
137- status: "ok",
138- resolvedModel: "opencode/claude",
139- initialSessionPatch: { model: "opencode/claude" },
140-});
135+const plan = expectOkPlan(
136+resolveSubagentModelAndThinkingPlan({
137+ cfg,
138+ targetAgentId: "research",
139+ targetAgentConfig,
140+ }),
141+);
142+expect(plan.resolvedModel).toBe("opencode/claude");
143+expect(plan.initialSessionPatch.model).toBe("opencode/claude");
144+expect(plan.initialSessionPatch.modelOverrideSource).toBe("auto");
141145});
142146143147it("uses config default timeout when agent omits runTimeoutSeconds", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。