Repository navigation
Helper model setting for spawned agents - #36
Conversation
|
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 ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
PR SummaryMedium Risk Overview CLI and session control: Daemon: Capability Reviewed by Cursor Bugbot for commit 48e05f3. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 48e05f3. Configure here.
| let thinking = match (plan.thinking.value, plan.thinking.source) { | ||
| (Some(level), HelperSource::Spawn) => { | ||
| assert_thinking_supported(agent_dir, Some(&level), &selector)?; | ||
| Some(level) |
There was a problem hiding this comment.
Spawn thinking parent not rejected
Medium Severity
A spawn-pinned thinking of parent (or any string that is not a real level) passes the supposed strict spawn check and is written onto the child as its thinking level. assert_thinking_supported returns success for an unparseable level, unlike a spawn-pinned model, which fails catalog resolution. The new parent sentinel makes this a reachable helper-model value, so a daemon child can start with thinking parent instead of inheriting or being refused.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 48e05f3. Configure here.
A spawned agent (rlm.spawn, rlm.create_session) that names no model or thinking level ran on its parent's. Each field now resolves in order: the spawn call, the session override, the settings file, the parent. - Settings: subagentDefaultModel (already parsed, never read) and the new subagentDefaultThinking. A spawn reads them when it runs. - Session override: --subagent-model/--subagent-thinking, else PRIME_SUBAGENT_MODEL/PRIME_SUBAGENT_THINKING, for print/json and --daemon-hosted runs and the interactive launch; /subagent-model in the TUI. The value parent keeps the parent's value. RLM children carry the override (create keys subagentModel/subagentThinking) and a daemon session persists it as a subagent_model_state row. - An unknown default model warns once and falls back to the parent's model; a default thinking level clamps to the helper's model. The spawn call's own values keep their strict checks; the allowlist still applies. - Paths: the daemon host's spawn and create_session, the direct print host's spawn. Factory states and goal runs go through them. - Daemon wire: get_subagent_model_status/set_subagent_model behind the subagent_model capability. - /settings Models tab: Helper model and Helper thinking. - Visibility: spawn and create-session handles carry thinking beside model; /subagent-model names each value's layer.
Review fixes for the helper-model setting: - The override row went into the engine's in-memory session manager, which never reaches disk. The worker now writes it to the session file it owns: every set_subagent_model (a clear included) and an RLM child's inherited override once, as its birth row. A resume, respawn or idle wake reads the latest row ahead of the create keys. - Spawn and create-session handles reported the parent's raw thinking level. The inherited level now clamps to the child's catalog model, so the handle shows the level the child runs at. - subagentDefaultThinking is validated text, so a project file's parent can override a global level. - The fork's two daemon commands sit at the tail of the accept list, and the hello's capability list names subagent_model.
Second review round: - set_subagent_model applied the live override, awaited, then appended the row, so two concurrent setters could leave the file's latest row different from the live value. The live apply and the durable append now happen together under the worker's core lock; the status is read after. - An RLM child whose inherited birth row failed to write was still created, and lost the override on its next file-only open. The create now fails instead. A session without a file is unaffected.
48e05f3 to
0c6c3f2
Compare


Stacked on #35 (the 2026-10-24 upstream sync): branched from its commit
22ed07464. It retargets torust-oneiron, rebased, once #35 merges.What changed
An agent that spawns a helper (
rlm.spawn,rlm.create_session) without naming a model ran the helper on its own model. Now a helper model can be set, per settings file and per session, and the helper's real model and thinking level are visible.subagentDefaultModel(parsed before, read by nothing) and the newsubagentDefaultThinking(off...max, orparent, so a project file can override a global level). A spawn reads the file when it runs.--subagent-model/--subagent-thinking, elsePRIME_SUBAGENT_MODEL/PRIME_SUBAGENT_THINKING, for-p,--mode json,--autonomous,--daemon-hosted, and the session an interactive launch opens. In the TUI,/subagent-model [<provider/model>|parent|default] [<thinking>|parent]sets it for the current session; with no argument it shows the current value and where each field comes from.parentplus the signed-in providers' models) and "Helper thinking" (parent,off...max). Both persist to the settings file;parentremoves the key.thinking(the level the child runs at, clamped to its model) besidemodel; the agents view already lists each child'smodel:level;/subagent-modelwarns when the model is not in the catalog.docs/oneiron/HELPER-MODEL.md, a row inRUST-BASE.md, the pa-core/pa-cli/pa-daemon READMEs, the CLI help rows, therlm.spawndocstring.Resolution order
Each field (model, thinking level) resolves on its own; the first layer that names a value wins:
parentat layer 2 or 3 stops the walk at layer 4.allowedModelsallowlist applies to the helper model as to any model.Spawn paths covered: the daemon host's
rlm.spawnandrlm.create_session(crates/pa-daemon/src/rlm_children/host.rs) and the direct print host'srlm.spawn(crates/pa-cli/src/print_rlm.rs; itsrlm.create_sessionstays daemonless-refused). Factory states and goal runs spawn through these hosts. Side questions, compaction, branch summaries and refine are one-off model calls on the session or auxiliary model, not agents, and are unchanged.The override covers the agent tree: an RLM child's create carries its parent's override (
subagentModel/subagentThinking, kept in the durable create command). The daemon worker writes the override to the session file it owns as asubagent_model_staterow: on everyset_subagent_model(a clear included; the live value and the row change together under the worker's core lock) and once at an RLM child's birth (a failed birth write fails the create). A resume, respawn or idle wake reads the latest row ahead of the create keys.Daemon wire
Fork-only, behind the new server capability
subagent_model(no schema bump; a client checks it before it sends either command):get_subagent_model_statusandset_subagent_model { model?, thinking? }, both session-plane, the getter read-only.--daemon-hostedrefuses a daemon without the capability instead of dropping the override.Test evidence
Linux (nelly, Rust 1.98.1) through the fleet, each run on a packed snapshot of the named commit:
56e7382dc(fmt,cargo clippy --workspace --all-targets -- -D warnings,scripts/oneiron/gate.sh test), job1024-060058-714a: 6141 passed, 2 failed, 20 ignored. The two failures were list assertions the feature had to extend (the accept list's fork tail, the hello's capability list); fixed in1ceeca484.1ceeca484, job1024-064051-94cb: 239 test binaries, 6135 passed, 0 failed, 20 ignored.48e05f3ca(this head), job1024-070837-3b2a: 240 test binaries, 6145 passed, 0 failed, 20 ignored; fmt and clippy clean.session_engine::helper_model(order per field,parent, validation, create-key round trip, settings layer incl. projectparentover global, persisted row, warn-once),rlm_child_model(order through the catalog, unknown-default fallback vs strict pinned values, inherited-level clamp, allowlist),agent_engine::tests::rlm_children(status/set, file row beats create keys, clear),rlm_surface(set_subagent_modelwrites its row to the session file, a clear too, refused engines write nothing; an inherited override is written once as the birth row),rlm_children_e2e::an_unpinned_spawn_runs_on_the_helper_model(real supervisor: settings default, session override, pinned model), settings menu (the_helper_model_rows_persist_a_catalog_model_and_a_thinking_level), CLI (subagent_flags_carry_the_helper_override,subagent_flags_beat_their_env_vars_field_by_field).parentcould not override a global level), fixed in1ceeca484. Round 2 confirmed them and found 2 more (concurrent setters; an unsaved birth row), fixed in48e05f3ca.Review notes
session_engine::helper_model), catalog resolution in pa-daemon (rlm_child_model::resolve_helper, shared by the print host asresolve_child_model_uncheckedwas), wire vocabulary in pa-types, UI in pa-tui over theClientSettingsseam. Dependency direction unchanged.pubsurface (pa_core::session_engine::helper_model,pa_daemon::rlm_child_model::{HelperRequest, ResolvedHelper, resolve_helper, resolve_helper_unchecked, helper_model_known, unknown_helper_model_notice}, twoDaemonCommandvariants andSUBAGENT_MODEL_CAPABILITY, four defaultedClientSettingsmethods, the menu'sHELPER_PARENT). No new dependency.subagentDefaultModel, where TS failed the spawn on an unavailable default; the fork warns and falls back to the parent's model (owner ruling, 2026-10-24). No adoption telemetry event: fork-only.