#21088: fix: sessions_sspawn model override ignored for sub-agents
commands
size: XS
Cluster:
Model Management Enhancements
Fix bug where sessions_spawn model parameter was ignored, causing sub-agents
to always use the parent's default model.
The allowAny flag from buildAllowedModelSet() was not being captured or used.
🤖 AI-assisted (Claude) - fully tested locally
Fixes #17479, #6295, #10963
## Summary
Summary
Problem: sessions_spawn model override was ignored - sub-agents always ran on parent's default model despite modelApplied: true being returned
Why it matters: Users cannot run sub-agents on different models, breaking multi-model workflows
What changed: Capture and use the allowAny flag from buildAllowedModelSet() instead of checking allowedModelKeys.size === 0
What did NOT change (scope boundary): No changes to model validation, auth, or catalog loading logic
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [x] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [ ] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
Closes #17479
Related #6295, #10963
## User-visible / Behavior Changes
- sessions_spawn with a model parameter now correctly applies the model override to the spawned sub-agent
- Previously: sub-agent would use parent's default model regardless of model parameter
- Now: sub-agent uses the specified model when it's properly configured/authenticated
## Security Impact (required)
- New permissions/capabilities? (`No`)
- Secrets/tokens handling changed? (`No`)
- New/changed network calls? (`No`)
- Command/tool execution surface changed? (`No`)
- Data access scope changed? (`No`)
## Repro + Verification
### Environment
- OS: macOS (Apple Silicon)
- Runtime/container: Node.js via Homebrew
- Model/provider: DeepSeek, Moonshot/Kimi, xAI/Grok
- Integration/channel: Discord
- Relevant config: Multi-provider setup with moonshot, deepseek, xai, claude configured
### Steps
1. Configure agent with default model deepseek/deepseek-chat
2. Use sessions_spawn tool with model: "moonshot/kimi-k2.5"
3. Check which model the sub-agent actually uses
### Expected
- Sub-agent runs on moonshot/kimi-k2.5
### Actual
Actual before fix:
- Sub-agent runs on deepseek/deepseek-chat despite modelApplied: true
Actual after fix:
- Sub-agent correctly runs on moonshot/kimi-k2.5
## Evidence
Attach at least one:
- [ ] Failing test/log before + passing after
- [ ] Trace/log snippets
- [ ] Screenshot/recording
- [ ] Perf numbers (if relevant)
## Human Verification (required)
Human Verification (required)
- Verified scenarios: Telling agent to spawn specific sub-agents with moonshot/kimi-k2.5, instead of deepseek/deepseek-chat (default).
- Edge cases checked: Model not in catalog (correctly fails), default model (works), non-default model (now works)
- What you did NOT verify: Other files with similar pattern (model-selection.ts, directive-handling.*.ts)
## Compatibility / Migration
- Backward compatible? (`Yes`)
- Config/env changes? (`No`)
- Migration needed? (`No`)
## Failure Recovery (if this breaks)
How to disable/revert this change quickly: Revert this commit
Files/config to restore: src/commands/agent.ts
Known bad symptoms: Sub-agents fail to spawn or use wrong model
## Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write `None`.
- Risk and Mitigations:
- Risk: Similar bug pattern may exist in other files (model-selection.ts, directive-handling.*.ts)
- Mitigation: This PR fixes the main sessions_spawn path; other files can be addressed in follow-up PRs if needed.
<img width="544" height="242" alt="Screenshot 2026-02-19 at 16 49 27" src="https://github.com/user-attachments/assets/1e66ab7c-1c8a-4e7d-b122-e4f60064f08f" />
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixed bug where `sessions_spawn` model parameter was ignored, causing sub-agents to always use the parent's default model despite `modelApplied: true` being returned.
**Root cause**: The `allowAny` flag returned by `buildAllowedModelSet()` was not being captured or used. The code was incorrectly checking `allowedModelKeys.size === 0` as a proxy for "allow any model", but this check is semantically wrong - an empty set means no models are allowed when there's an allowlist configured.
**Key changes**:
- Added `allowAnyModel` variable to capture the `allowAny` flag from `buildAllowedModelSet()` (line 402, 414)
- Replaced `allowedModelKeys.size > 0` check with `!allowAnyModel` in validation logic (line 426)
- Replaced `allowedModelKeys.size === 0` check with `allowAnyModel` when applying stored overrides (line 453)
**How `allowAny` works**: According to `buildAllowedModelSet()` in `model-selection.ts:365`, `allowAny` is `true` when no model allowlist is configured (`rawAllowlist.length === 0`), meaning all models are permitted. The function can also return `allowAny: true` in fallback scenarios when the allowlist resolves to an empty set.
**Impact**: Multi-model workflows now work correctly - users can spawn sub-agents on different models as intended.
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge with low risk - it's a targeted bug fix that correctly implements the intended model override behavior
- The fix correctly addresses the root cause by capturing and using the `allowAny` flag instead of relying on the incorrect proxy check `allowedModelKeys.size === 0`. The logic is sound: when no allowlist is configured, `allowAny` is true and all models should be accepted. The changes are minimal, well-scoped, and align with the existing `buildAllowedModelSet()` API. However, there's a risk that similar patterns exist in other files (as noted in PR risks: `model-selection.ts`, `directive-handling.*.ts`), which could still have the same bug. Score is 4 instead of 5 due to: (1) lack of automated test coverage for this specific fix, and (2) similar bug patterns may exist elsewhere in the codebase.
- Pay attention to the files mentioned in the PR risks: `src/auto-reply/reply/model-selection.ts`, `src/auto-reply/reply/directive-handling.model.ts`, and `src/auto-reply/reply/directive-handling.persist.ts` - these files use similar `allowedModelKeys.size` checks that may need the same fix in follow-up PRs
<sub>Last reviewed commit: e313788</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#6673: fix: preserve allowAny flag in createModelSelectionState for custom...
by tenor0 · 2026-02-01
88.7%
#20712: fix(subagents): prioritize agent runtime default model over global ...
by sourcesavant · 2026-02-19
87.6%
#13376: fix: pass model directly to agent for sub-agent runs
by jrbobbyhansen-pixel · 2026-02-10
85.8%
#9583: fix(models): allow models in agents.defaults.models even if not in ...
by hotzen100 · 2026-02-05
85.2%
#6603: fix: use allowAny flag instead of size check for model override val...
by gavinbmoore · 2026-02-01
85.1%
#21556: fix(agents): graceful fallback when spawned model is not in allowlist
by irchelper · 2026-02-20
84.7%
#9822: fix: allow local/custom model providers for sub-agent inference
by stammtobias91 · 2026-02-05
83.8%
#23286: fix: use configured model in llm-slug-generator instead of hardcoded …
by wsman · 2026-02-22
82.7%
#11562: Fix #10883: Enforce subagent model configuration
by divol89 · 2026-02-08
81.7%
#18697: fix: include forward-compat models in model catalog for allowlist val…
by dmitry-orabey · 2026-02-17
81.2%