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
1 change: 1 addition & 0 deletions desktop/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export default defineConfig({
"**/sidebar.spec.ts",
"**/tokens.spec.ts",
"**/persona-env-vars.spec.ts",
"**/persona-instantiation.spec.ts",
"**/mesh-compute.spec.ts",
],
use: {
Expand Down
3 changes: 2 additions & 1 deletion desktop/scripts/check-file-sizes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const rules = [
// Do not add to this list; split the file instead. Remove each entry as its
// file is broken up. Tracked as a follow-up.
const overrides = new Map([
["src-tauri/src/commands/agents.rs", 1294],
["src-tauri/src/commands/agents.rs", 1370],
["src-tauri/src/managed_agents/teams.rs", 1020],
["src-tauri/src/managed_agents/nest.rs", 1420],
["src-tauri/src/managed_agents/runtime.rs", 1940],
["src-tauri/src/managed_agents/personas.rs", 1080],
Expand Down
3 changes: 2 additions & 1 deletion desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ name = "buzz_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

[features]
default = []
default = ["legacy_team_sync"]
mesh-llm = ["dep:mesh-llm-sdk", "dep:mesh-llm-host-runtime"]
legacy_team_sync = []

[build-dependencies]
tauri-build = { version = "2", features = [] }
Expand Down
21 changes: 21 additions & 0 deletions desktop/src-tauri/src/commands/agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,27 @@ pub async fn create_managed_agent(
.await)
.err();

// ── Phase 4b: write persona engram (async, best-effort) ─────────────────
// If the agent was created from a persona, snapshot it into a `mem/persona`
// engram. This is provenance-only for now (the live resolve path still reads
// the persona catalog at spawn); fleet update keeps it current.
if requested_persona_id.is_some() {
if let Err(e) = crate::managed_agents::fleet_update::write_persona_engram_at_creation(
&state,
&agent_keys,
&resolved_relay_url,
requested_persona_id.as_deref().unwrap(),
&app,
)
.await
{
eprintln!(
"buzz-desktop: create-agent: persona engram write failed for {}: {e}",
&pubkey[..pubkey.len().min(8)]
);
}
}

// ── Phase 5: provider deploy (async, outside lock) ───────────────────────
let spawn_error = if input.spawn_after_create && input.backend != BackendKind::Local {
if let BackendKind::Provider { ref id, ref config } = input.backend {
Expand Down
13 changes: 13 additions & 0 deletions desktop/src-tauri/src/commands/personas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,19 @@ pub fn update_persona(
.find(|record| record.id == input.id)
.ok_or_else(|| format!("persona {} disappeared unexpectedly", input.id))?;
try_regenerate_nest(&app);

// Fleet update: propagate persona edits to agent engrams (best-effort).
let fleet_app = app.clone();
let persona_id = input.id.clone();
tauri::async_runtime::spawn(async move {
if let Err(e) =
crate::managed_agents::fleet_update::fleet_update_for_persona(&fleet_app, &persona_id)
.await
{
eprintln!("buzz-desktop: fleet-update-on-save: {e}");
}
});

Ok(result)
}

Expand Down
13 changes: 9 additions & 4 deletions desktop/src-tauri/src/commands/teams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ use tauri::{AppHandle, State};
use uuid::Uuid;

use super::export_util::save_json_with_dialog;
#[cfg(feature = "legacy_team_sync")]
use crate::managed_agents::{
import_team_from_directory as do_import_team, sync_team_from_dir as do_sync_team, SyncResult,
};
use crate::{
app_state::AppState,
managed_agents::{
delete_team_with_cascade, encode_team_json, ensure_persona_ids_are_active,
import_team_from_directory as do_import_team, load_personas, load_teams, parse_team_json,
save_teams, sync_team_from_dir as do_sync_team, try_regenerate_nest, CreateTeamRequest,
ParsedTeamPreview, SyncResult, TeamRecord, UpdateTeamRequest,
delete_team_with_cascade, encode_team_json, ensure_persona_ids_are_active, load_personas,
load_teams, parse_team_json, save_teams, try_regenerate_nest, CreateTeamRequest,
ParsedTeamPreview, TeamRecord, UpdateTeamRequest,
},
util::now_iso,
};
Expand Down Expand Up @@ -114,6 +117,7 @@ pub fn delete_team(id: String, app: AppHandle, state: State<'_, AppState>) -> Re
Ok(())
}

#[cfg(feature = "legacy_team_sync")]
#[tauri::command]
pub fn install_team_from_directory(
app: AppHandle,
Expand All @@ -134,6 +138,7 @@ pub fn install_team_from_directory(
Ok(result)
}

#[cfg(feature = "legacy_team_sync")]
#[tauri::command]
pub fn sync_team_directory(
app: AppHandle,
Expand Down
19 changes: 16 additions & 3 deletions desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,9 +552,20 @@ pub fn run() {
.map_err(|e| -> Box<dyn std::error::Error> { e.to_string().into() })?;
migration::migrate_personas_to_events(&app_handle, &owner_keys);

if let Err(e) = managed_agents::sync_team_personas(&app_handle) {
eprintln!("buzz-desktop: sync-team-personas: {e}");
}
// Fleet update: reconcile every agent's `mem/persona` engram against
// its persona's current content. Replaces the old directory-based
// `sync_team_personas`. Best-effort and relay-dependent, so it runs
// off-thread — engram drift is non-fatal and resolves next launch.
let fleet_app = app_handle.clone();
tauri::async_runtime::spawn(async move {
match managed_agents::fleet_update::check_fleet_updates(&fleet_app).await {
Ok(0) => {}
Ok(n) => {
eprintln!("buzz-desktop: fleet-update: rewrote {n} persona engram(s)")
}
Err(e) => eprintln!("buzz-desktop: fleet-update: {e}"),
}
});

// Store the AppHandle so huddle commands can emit `huddle-state-changed`
// events via `huddle::emit_huddle_state` without threading the handle
Expand Down Expand Up @@ -793,7 +804,9 @@ pub fn run() {
create_team,
update_team,
delete_team,
#[cfg(feature = "legacy_team_sync")]
install_team_from_directory,
#[cfg(feature = "legacy_team_sync")]
sync_team_directory,
pick_team_directory,
export_team_to_json,
Expand Down
3 changes: 3 additions & 0 deletions desktop/src-tauri/src/managed_agents/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use std::collections::BTreeMap;
///
/// Non-structured knobs (`GOOSE_TEMPERATURE`, `GOOSE_CONTEXT_LIMIT`) are NOT
/// in this list — they have no structured counterpart and must be preserved.
#[cfg_attr(not(feature = "legacy_team_sync"), allow(dead_code))]
pub(crate) const DERIVED_PROVIDER_MODEL_ENV_KEYS: &[&str] = &[
"GOOSE_MODEL",
"GOOSE_PROVIDER",
Expand All @@ -34,6 +35,7 @@ pub(crate) const DERIVED_PROVIDER_MODEL_ENV_KEYS: &[&str] = &[

/// Returns `true` if `key` is a derived provider/model env key that should be
/// filtered out of persisted `PersonaRecord.env_vars` at pack import time.
#[cfg(feature = "legacy_team_sync")]
pub(crate) fn is_derived_provider_model_key(key: &str) -> bool {
DERIVED_PROVIDER_MODEL_ENV_KEYS
.iter()
Expand All @@ -46,6 +48,7 @@ pub(crate) fn is_derived_provider_model_key(key: &str) -> bool {
/// The structured `PersonaRecord.provider` / `PersonaRecord.model` fields are
/// the authoritative source of truth. Keeping the derived copies would cause
/// stale env values to override updated structured fields at spawn/deploy time.
#[cfg(feature = "legacy_team_sync")]
pub(crate) fn filter_derived_provider_model_env_vars(
env_vars: impl IntoIterator<Item = (String, String)>,
) -> BTreeMap<String, String> {
Expand Down
Loading