Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions desktop/src-tauri/src/commands/agent_models.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::collections::{BTreeMap, HashSet};

use super::agent_model_process::run_agent_models_command;
use nostr::Keys;
use serde::Deserialize;
use tauri::{AppHandle, State};

use super::agent_model_process::run_agent_models_command;
// The map-only lookup is reached solely from the base-URL helpers that exist for
// their unit tests; discovery itself always goes through the process-env variant.
#[cfg(test)]
Expand All @@ -13,7 +12,6 @@ use super::agent_models_env::{
effective_discovery_provider, env_or_process_value, redaction_env_with_value, DiscoveryProvider,
};
use super::agent_update_rollback::{rollback_failed_agent_update, AgentUpdateRollback};

use crate::{
app_state::AppState,
managed_agents::{
Expand Down Expand Up @@ -727,9 +725,9 @@ fn apply_model_provider_prompt_update(

/// Update mutable fields on an existing managed agent record.
///
/// Does NOT auto-restart the agent. Runtime config changes (system prompt,
/// parallelism, commands, toolsets) take effect on the next agent spawn.
/// Name changes are synced to the relay immediately via a kind:0 re-publish.
/// Local runtime config changes take effect on the next spawn. Provider-backed
/// agents are re-deployed after a successful save. Name changes are synced to
/// the relay immediately via a kind:0 re-publish.
#[tauri::command]
pub async fn update_managed_agent(
input: UpdateManagedAgentRequest,
Expand Down Expand Up @@ -931,6 +929,8 @@ pub async fn update_managed_agent(
}
}

super::agent_update_deploy::apply_update(&app, &state, &input.pubkey).await?;

Ok(UpdateManagedAgentResponse {
agent: summary,
profile_sync_error: None,
Expand Down
47 changes: 47 additions & 0 deletions desktop/src-tauri/src/commands/agent_update_deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use tauri::AppHandle;

use crate::{
app_state::AppState,
managed_agents::{load_managed_agents, BackendKind},
};

/// Apply an edited provider-backed record to its remote provider. Local records
/// return without action because their next spawn consumes the saved change.
pub(super) async fn apply_update(
app: &AppHandle,
state: &AppState,
pubkey: &str,
) -> Result<(), String> {
let provider_deploy = {
let _store_guard = state
.managed_agents_store_lock
.lock()
.map_err(|e| e.to_string())?;
let records = load_managed_agents(app)?;
let record = records
.iter()
.find(|record| record.pubkey == pubkey)
.ok_or_else(|| format!("agent {pubkey} not found"))?;
let BackendKind::Provider { id, config } = &record.backend else {
return Ok(());
};
(
id.clone(),
config.clone(),
record.provider_binary_path.clone(),
super::agents::build_deploy_payload(app, state, record)?,
)
};

let (provider_id, provider_config, cached_binary_path, agent_json) = provider_deploy;
super::agents::deploy_to_provider(
app,
state,
pubkey,
&provider_id,
&provider_config,
agent_json,
cached_binary_path.as_deref(),
)
.await
}
4 changes: 2 additions & 2 deletions desktop/src-tauri/src/commands/agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ pub(super) async fn start_local_agent_with_preflight(
///
/// Returns Ok(()) on success, Err(message) on failure. Either way the record is
/// updated and saved before returning.
async fn deploy_to_provider(
pub(super) async fn deploy_to_provider(
app: &AppHandle,
state: &AppState,
pubkey: &str,
Expand Down Expand Up @@ -1358,7 +1358,7 @@ pub async fn delete_managed_agent(
#[path = "agents_deploy.rs"]
mod deploy;
pub(super) mod provider_access;
use deploy::build_deploy_payload;
pub(super) use deploy::build_deploy_payload;
#[cfg(test)]
use deploy::{deploy_payload_json, DeployProjections};
#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/src/commands/agents_deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub(super) fn ensure_remote_provider_supported(provider: Option<&str>) -> Result
}

/// Build the standard agent JSON payload for provider deploy calls.
pub(super) fn build_deploy_payload(
pub(crate) fn build_deploy_payload(
app: &AppHandle,
state: &AppState,
record: &ManagedAgentRecord,
Expand Down
1 change: 1 addition & 0 deletions desktop/src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod agent_models;
mod agent_models_env;
mod agent_providers;
mod agent_settings;
mod agent_update_deploy;
mod agent_update_rollback;
mod agents;
mod canvas;
Expand Down
6 changes: 6 additions & 0 deletions desktop/src/features/agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ with a TypeScript lookup table or an id comparison in a component.
`getAgentAccessOwnerOnly()` is true, every managed agent's access control is
locked to owner-only, including provider-backed agents. A provider backend
does not prove remote execution and must never create a policy carve-out.
12. **Provider-backed edits re-deploy immediately.** `update_managed_agent`
persists the Desktop record, completes any required relay profile sync, and
then calls the provider's idempotent deploy operation with the revised
launch payload. Never require a local Stop/Start transition to apply a
remote agent's updated instruction, model, harness, or policy: provider
agents have no local runtime pair to restart.

## The tests that enforce this

Expand Down