-
Notifications
You must be signed in to change notification settings - Fork 1k
fix(desktop): apply agent model edits on first restart #2235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -451,12 +451,14 @@ pub fn persona_snapshot_with_agent_config_fallback( | |
| } | ||
| } | ||
|
|
||
| /// Re-pin `record` to `persona`: build the snapshot (via | ||
| /// [`persona_snapshot_with_agent_config_fallback`], so blank persona | ||
| /// `model`/`provider` preserve the record's own values) and mirror it onto the | ||
| /// Re-pin `record` to `persona`: build the snapshot and mirror it onto the | ||
| /// record — the definition quad (`system_prompt`/`model`/`provider`/`runtime`), | ||
| /// the env-override self-heal, and the `persona_source_version` drift basis. | ||
| /// | ||
| /// Model is definition-authoritative: a blank definition model clears stale | ||
| /// materialized bytes so spawn can resolve the current global default. Provider | ||
| /// retains its existing blank-definition fallback behavior. | ||
| /// | ||
| /// This is the single apply used by every snapshot-apply site: the spawn | ||
| /// re-pin (`start_local_agent_with_preflight`), the launch backfill and | ||
| /// restore re-snapshot (`restore.rs`), and the prospective re-snapshot inside | ||
|
|
@@ -469,13 +471,13 @@ pub fn persona_snapshot_with_agent_config_fallback( | |
| pub fn apply_persona_snapshot(record: &mut ManagedAgentRecord, persona: &AgentDefinition) { | ||
| let snapshot = persona_snapshot_with_agent_config_fallback( | ||
| persona, | ||
| record.model.as_deref(), // fallback: record.model | ||
| record.provider.as_deref(), // fallback: record.provider | ||
| record.model.as_deref(), | ||
| record.provider.as_deref(), | ||
| ); | ||
| if let Some(prompt) = snapshot.system_prompt { | ||
| record.system_prompt = Some(prompt); | ||
| } | ||
| record.model = snapshot.model; | ||
| record.model = persona.model.clone(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocking: this assignment can't distinguish stale materialized inheritance bytes from an explicit per-instance model the user set via There's also an asymmetry hazard: Either preserve explicit instance models (needs provenance the record can't currently express — the #1968 territory this PR carves around), or gate/redirect the ModelPicker persist path for definition-linked agents in the same PR. Worth tests for both cases: stale inherited model converges to the new global default, and an explicit instance model survives restart. |
||
| record.provider = snapshot.provider; | ||
| record.runtime = snapshot.runtime; | ||
| // env_vars stay overrides-only. Self-heal records written before the env | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,7 @@ import { | |
| } from "@/features/agents/lib/instanceInputForDefinition"; | ||
| import { | ||
| isManagedAgentActive, | ||
| respawnManagedAgentWithRules, | ||
| startManagedAgentWithRules, | ||
| stopManagedAgentWithRules, | ||
| } from "@/features/agents/lib/managedAgentControlActions"; | ||
|
|
@@ -451,10 +452,19 @@ export function UserProfilePanel({ | |
| ], | ||
| ); | ||
|
|
||
| const handleAgentPrimaryAction = React.useCallback(async () => { | ||
| const handleAgentPrimaryAction = async (restart = false) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocking: this function is passed unchanged as the primary-action prop (line 834) and ends up installed directly as the button's Suggest splitting into two zero-arg named callbacks (or wrapping the prop: |
||
| if (!managedAgent) return; | ||
|
|
||
| try { | ||
| if (restart && managedAgent.backend.type === "local") { | ||
| await respawnManagedAgentWithRules({ | ||
| agent: managedAgent, | ||
| startManagedAgent: startAgentMutation.mutateAsync, | ||
| stopManagedAgent: stopAgentMutation.mutateAsync, | ||
| }); | ||
| toast.success(`Restarted ${managedAgent.name}.`); | ||
| return; | ||
| } | ||
| if (isManagedAgentActive(managedAgent)) { | ||
| const result = await stopManagedAgentWithRules({ | ||
| agent: managedAgent, | ||
|
|
@@ -480,13 +490,7 @@ export function UserProfilePanel({ | |
| error instanceof Error ? error.message : "Agent action failed.", | ||
| ); | ||
| } | ||
| }, [ | ||
| channelsQuery.data, | ||
| managedAgent, | ||
| relayAgentsQuery.data, | ||
| startAgentMutation.mutateAsync, | ||
| stopAgentMutation.mutateAsync, | ||
| ]); | ||
| }; | ||
|
|
||
| const handleInstantiateAgent = React.useCallback(async () => { | ||
| if (!resolvedPersona) return; | ||
|
|
@@ -828,6 +832,7 @@ export function UserProfilePanel({ | |
| followMutation={followMutation} | ||
| agentInstruction={agentInstruction} | ||
| handleAgentPrimaryAction={handleAgentPrimaryAction} | ||
| handleAgentRestart={() => void handleAgentPrimaryAction(true)} | ||
| handleEditAgent={handleEditAgent} | ||
| handleEditPersona={canEditPersona ? handleEditPersona : undefined} | ||
| handleInstantiateAgent={handleInstantiateAgent} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking:
persona_snapshot_with_agent_config_fallbackstill computessnapshot.modelfrom the record fallback, but it's now dead — onlysnapshot.provideris consumed below. Dropping the model arg (or passingNone) would make the code match the new doc comment instead of computing-then-discarding.