Skip to content

Commit e4726cb

Browse files
committed
fix(subagents): inherit parent model when recommended model is an auto router alias
Builtin subagents like history-analyzer pin 'model: auto' in their frontmatter. resolveSubagentModel treated any non-inherit recommended model as an explicit pin that beats parent inheritance, so subagents spawned by /init ran on the server-side auto router's pick instead of the session's selected model, contradicting the Task tool's documented 'inherits from parent' contract. Treat a recommended model that resolves to a router alias (letta/auto, letta/auto-fast, letta/auto-chat, letta/auto-memory) as non-pinning: prefer the parent's model when one exists. The alias still applies when no parent model is set, free-tier defaults are unchanged, and the reflection carve-out is untouched. Fixes #3248
1 parent 2bee18f commit e4726cb

3 files changed

Lines changed: 58 additions & 2 deletions

File tree

src/agent/subagent-model-resolution.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,46 @@ describe("resolveSubagentModel", () => {
644644
expect(result).toBe("anthropic/test-model");
645645
});
646646

647+
test("recommended auto router alias inherits parent model instead", async () => {
648+
const result = await resolveSubagentModel({
649+
recommendedModel: "auto",
650+
parentModelHandle: "openai/gpt-5.4-mini",
651+
availableHandles: new Set(["letta/auto", "openai/gpt-5.4-mini"]),
652+
});
653+
654+
expect(result).toBe("openai/gpt-5.4-mini");
655+
});
656+
657+
test("recommended auto-fast router alias inherits parent model instead", async () => {
658+
const result = await resolveSubagentModel({
659+
recommendedModel: "auto-fast",
660+
parentModelHandle: "anthropic/parent-model",
661+
availableHandles: new Set(["letta/auto-fast", "anthropic/parent-model"]),
662+
});
663+
664+
expect(result).toBe("anthropic/parent-model");
665+
});
666+
667+
test("recommended auto router alias is used when no parent model exists", async () => {
668+
const result = await resolveSubagentModel({
669+
recommendedModel: "auto",
670+
availableHandles: new Set(["letta/auto"]),
671+
});
672+
673+
expect(result).toBe("letta/auto");
674+
});
675+
676+
test("free tier keeps auto-fast default despite parent model", async () => {
677+
const result = await resolveSubagentModel({
678+
recommendedModel: "auto",
679+
billingTier: "free",
680+
parentModelHandle: "openai/gpt-5.4-mini",
681+
availableHandles: new Set(["letta/auto-fast", "openai/gpt-5.4-mini"]),
682+
});
683+
684+
expect(result).toBe("letta/auto-fast");
685+
});
686+
647687
test("explicit user model overrides all other resolution", async () => {
648688
const result = await resolveSubagentModel({
649689
userModel: "lc-openrouter/custom-model",

src/agent/subagents/manager.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,19 @@ function isInheritModel(model: string | null | undefined): boolean {
235235
return model?.trim().toLowerCase() === "inherit";
236236
}
237237

238+
// Server-side router aliases. A subagent recommending one of these means
239+
// "let the system pick", so it must not override a parent's selected model.
240+
const ROUTER_ALIAS_HANDLES = new Set([
241+
"letta/auto",
242+
"letta/auto-fast",
243+
"letta/auto-chat",
244+
"letta/auto-memory",
245+
]);
246+
247+
function isRouterAliasHandle(handle: string): boolean {
248+
return ROUTER_ALIAS_HANDLES.has(handle);
249+
}
250+
238251
export async function resolveSubagentModel(options: {
239252
userModel?: string;
240253
recommendedModel?: string;
@@ -336,7 +349,10 @@ export async function resolveSubagentModel(options: {
336349
return parentModelHandle;
337350
}
338351

339-
if (await isAvailable(recommendedHandle)) {
352+
if (
353+
!isRouterAliasHandle(recommendedHandle) &&
354+
(await isAvailable(recommendedHandle))
355+
) {
340356
return recommendedHandle;
341357
}
342358
}

src/skills/builtin/initializing-memory/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ The goal is to extract user personality, preferences, coding patterns, and proje
239239
#### Prerequisites
240240

241241
- `letta.js` must be built (`bun run build`) — subagents spawn via this binary
242-
- Use `subagent_type: "history-analyzer"`cheaper model (sonnet), has `bypassPermissions`, creates its own worktree
242+
- Use `subagent_type: "history-analyzer"`inherits the session's selected model, has `bypassPermissions`, creates its own worktree
243243
- The `history-analyzer` subagent has data format docs inlined (Claude/Codex JSONL field mappings, jq queries)
244244

245245
#### Steps

0 commit comments

Comments
 (0)