Repository navigation
Helper model list for spawned agents - #37
Conversation
Agents named paid routes in their spawn calls: a model named there wins over every default, and the find_models roster showed every catalog model. The settings key subagentModels now limits what spawned agents may run on, while every model stays in the catalog for a person to pick. - Settings: subagentModels, an ordered list of provider/model-id refs, read from the global settings file only (a project file cannot widen it). Absent or empty: no limit. - Spawn check: an rlm.spawn or rlm.create_session call naming a model off the list is refused with the allowed models named. A settings default or the parent's model off the list falls to the first entry, with a warning once. Both hosts check it: the daemon (resolve_helper) and the in-process print host, its scripted lane included. - A person's choice is allowed: the session override (--subagent-model, PRIME_SUBAGENT_MODEL, /subagent-model) may sit off the list. A person can widen the list for one session: --subagent-models, PRIME_SUBAGENT_MODELS, TUI /subagent-models, daemon command set_subagent_models (capability subagent_models). The additions ride RLM child creates (subagentModels) and the subagent_model_state row. - Roster: rlm.find_models lists only the allowed models, through the new RlmSubagentHost::helper_model_list. - Status: get_subagent_model_status adds models, modelsSource and modelAllowed; the TUI shows the list and warns when the default is off it. - Docs: docs/oneiron/HELPER-MODEL.md, RUST-BASE.md, crate READMEs.
…t ignore it Review fixes for the helper model list: - The first-entry fallback resolved its reference like a spawn reference, so a short form or a nested-provider match could run a route off the list. The resolved fallback must now be on the list, else the spawn fails with a message naming the entry. - A listed model the catalog lacks failed with the catalog's close matches, which could name models off the list. It now fails with the allowed models named. - A person's short-form helper model joined the list as typed, so an agent naming its full selector was refused. The hosts now add its catalog selector (rlm_child_model::helper_model_list, shared by the spawn check, the roster and the status). - A running daemon without subagent_models ignores the settings list. --daemon-hosted refuses one when a list is set; the TUI warns at open. - A spawn resolved its model from one identity snapshot and created the child from another, so a concurrent set_subagent_models could tear them. The admission now uses one snapshot for both.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
PR SummaryMedium Risk Overview When the list is set, Daemon wire adds capability Reviewed by Cursor Bugbot for commit 73c5155. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 73c5155. Configure here.
| return; | ||
| None | ||
| } | ||
| }; |
There was a problem hiding this comment.
TUI drops helper override on old daemon
Medium Severity
When a settings subagentModels list is present and the running daemon lacks subagent_models, apply_launch_subagent_model warns and returns before sending set_subagent_model. Launch --subagent-model/--subagent-thinking never land, even though that older daemon still advertises subagent_model and can apply them. Hosted print checks the two capabilities separately and still applies the model override.
Reviewed by Cursor Bugbot for commit 73c5155. Configure here.
| ) | ||
| .unwrap_or_else(|_| reference.to_string()), | ||
| ) | ||
| }), |
There was a problem hiding this comment.
Status misreports unknown default fallback
Low Severity
modelAllowed treats an unresolvable subagentDefaultModel as off the helper list. Spawn resolution first falls back to the parent, and only then clamps to the list's first entry if that parent is also off the list. The TUI therefore warns that spawns use the first list entry while also warning that they use the parent's model.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 73c5155. Configure here.


Why
Agents in the kernel challenge spawned helpers on
cpa-a/claude-opus-5, a paid reseller route, on their own. A model named in a spawn call wins over every default (#36), andrlm.find_modelsshowed agents every model in the catalog. The owner asked for a helper model list that is separate from the full list: a person can still pick any model, but agents pick their helpers only from the list.What changed
subagentModels: an ordered list ofprovider/model-idreferences that spawned agents may run on. It is read from the global settings file only, likeallowedModels, so a project file (which an agent can write) cannot widen it. Absent or empty means no limit, which is the old behavior. An array of strings is the documented form. A single comma-separated string also works, and a non-string entry drops alone instead of disabling the whole list.rlm.spawnorrlm.create_sessioncall that names a model off the list is refused. The error names the allowed models so the agent can retry with one:Requested subagent model "cpa-a/claude-opus-5" is not on the helper model list; use one of: cpa-r/deepseek-flash-1017, ... (or omit model= for the default).An unresolvable name gets the same refusal, so catalog close matches off the list are never shown; a listed model the catalog lacks fails with the allowed models named. A settings default or a parent model off the list falls to the list's first entry, with a warning once on stderr. The fallback must resolve to that exact entry: a short form or nested-provider match that lands on another route fails the spawn instead. A refused spawn starts no child.
--subagent-model,PRIME_SUBAGENT_MODEL,/subagent-model) may name a model off the list, and an agent may then name that model too. A short form the person typed joins the list as its catalog selector. A person can widen the list for one session with--subagent-models a,b,PRIME_SUBAGENT_MODELS=a,b, or/subagent-models a,bin the TUI (/subagent-models defaultclears it). The session's entries come after the settings file's, so the fallback stays the settings file's first entry. With no settings list there is nothing to widen.--modeland/modelare unchanged.rlm.find_modelslists only the allowed models when a list is set. It does this through a new defaulted trait method,RlmSubagentHost::helper_model_list.set_subagent_modelscannot leave the child with a different override than the one its model was checked against.resolve_helperincrates/pa-daemon/src/rlm_child_model.rs, throughrlm_children/host.rs) and the in-process print host (crates/pa-cli/src/print_rlm.rs, its scripted lane included) apply the same rule. Factory states spawn throughrlm.spawn(model=...), so the host check covers them.subagentModels(kept in the durable create command). They persist in thesubagent_model_staterow asdata.models. A row without the field reads as no additions.get_subagent_model_statusaddsmodels(the active list,nullwhen there is no limit),modelsSource(settings, orsettings+session), andmodelAllowed(falsewhen the spawns' default is off the list)./subagent-modeland/subagent-modelsprint the list and its source, and warn when the default is off it. The TUI also runs this check when a session opens with a list configured.docs/oneiron/HELPER-MODEL.md(new "Helper model list" section and settings row), theRUST-BASE.mdrow, and the pa-core/pa-cli/pa-daemon READMEs. Also the CLI help row and the slash-command entry.Daemon wire
This is fork-only, with no schema bump. New server capability
subagent_models. New session-plane, mutating commandset_subagent_models { activeSessionId, models? }, which replaces only the session's list additions.set_subagent_modelnow replaces only the model and thinking level and keeps the additions. Each write still persists the whole override under the worker's core lock. A daemon without the capability ignores the list, so--daemon-hostedrefuses one when a settings list or--subagent-modelsis set, and the TUI warns when the session opens.Test evidence
Mac (Oletybook), Rust 1.98.1 (the CI pin), head
73c5155a4:cargo fmt --all --check: clean.cargo clippy --workspace --all-targets --locked -- -D warnings: clean.scripts/oneiron/gate.sh policy: passed.scripts/oneiron/gate.sh crates pa-types pa-core pa-tui pa-daemon pa-cli(sandboxed HOME and TMPDIR, its own kernel venv): 5390 passed, 72 failed, 17 ignored.kitty_verdict_time_e2ehung both times on a child stuck in the kernel's exit path (stateE), and I killed it.fc8c2b429, on this Mac. I ran the same suites through the same gate sandbox:compaction_unblocked_tests, which race on a shared faux registration when run in parallel. They also fail on base in other runs, and all four pass serialized on both trees./proc, GNUld --build-id, Linux trash) and the mock-daemon headless suites, which fail on macOS at base too.session_engine::helper_model: list order and widening,parent, exact case-insensitive matching, the refusal text, parsing, the global-only settings read (array and comma string), and the create-key and persisted-row round trips.rlm_host::find_models_lists_only_the_helper_model_list.rlm_child_model::the_helper_model_list_refuses_off_list_spawns_and_keeps_defaults_on_it.agent_engine::tests::rlm_children(statusmodels/modelsSource/modelAllowed).rlm_surface::the_list_and_the_model_override_replace_only_their_own_fields.rlm_children_e2e::a_spawn_off_the_helper_model_list_is_refused. It runs against a real supervisor: an off-list spawn is refused and starts no child, a listed model starts, an unpinned spawn falls to the first entry,create_sessionis refused, and a session addition widens both the roster and the spawn.print_inline_subagents_e2e::print_inline_spawns_off_the_helper_model_list_are_refused. It runs the real binary and a real kernel: the off-list spawn fails in the agent's tool result, the roster shows only the listed model, and the listed spawn starts.73c5155a4, with tests.Review notes
session_engine::helper_model::HelperModelList). Catalog resolution and enforcement live in pa-daemon (resolve_helper_unchecked, which the print host shares). The wire vocabulary is in pa-types. The UI is in pa-tui over theClientSettingsseam (pa-tui does not depend on pa-core; it splits the typed list itself and the daemon normalizes it). Dependency direction is unchanged.pubsurface:HelperModelList,normalize_model_list,parse_model_list,HelperDefaults::models/with_models,HelperSource::List,first_helper_notice(replacesfirst_unknown_helper_model),SUBAGENT_MODELS_ENV/SUBAGENT_MODELS_CONFIG_KEY,SettingsManager::get_subagent_models, the defaultedRlmSubagentHost::helper_model_list, the defaultedSessionEngine::subagent_override,DaemonCommand::SetSubagentModels,SUBAGENT_MODELS_CAPABILITY, and the defaultedClientSettings::subagent_models. No new dependency.subagentModels.