refactor(desktop): keep agent storage within size gate

On-behalf-of: mahanti <arjun.mahanti@gmail.com>
Signed-off-by: Fizz <fizz@buzz.local>
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Fizz
2026-08-28 11:18:05 -04:00
parent 831dcc4738
commit 8b69b411cd
2 changed files with 140 additions and 125 deletions

View File

@@ -13,6 +13,15 @@ use crate::managed_agents::{
};
use crate::secret_store::{KeyringProbe, SecretStore};
mod build_filter;
use build_filter::instances_for_save;
pub use build_filter::load_managed_agents;
pub(crate) use build_filter::{
filter_agent_definitions_for_build, filter_managed_agents_for_build, load_agent_definitions,
load_agent_definitions_unfiltered, load_agent_store_from_path,
};
/// Keyring key name for an agent's nsec, namespaced from the human identity
/// key (`"identity"`) which shares the service.
fn agent_keyring_name(pubkey: &str) -> String {
@@ -245,89 +254,6 @@ fn load_agent_store<R: tauri::Runtime>(
load_agent_store_from_path(&path)
}
pub(crate) fn load_agent_store_from_path(path: &Path) -> Result<Vec<ManagedAgentRecord>, String> {
if !path.exists() {
return Ok(Vec::new());
}
let content =
fs::read_to_string(path).map_err(|error| format!("failed to read agent store: {error}"))?;
serde_json::from_str(&content).map_err(|error| {
// Fail loudly and preserve the evidence: a later in-app save rewrites
// this file wholesale, which would silently destroy a malformed hand
// edit. Best-effort file-authoring contract (see managed_agents::
// reconcile): the broken content survives as `.invalid` for the user
// to recover, and the parse error propagates instead of being
// swallowed into an empty store.
backup_invalid_store(path);
format!("failed to parse agent store (preserved as .invalid): {error}")
})
}
/// Load the keyed agent *instances*. Key-less definitions (former personas,
/// folded into the same store) are filtered out so every pre-fold call site
/// keeps seeing exactly the records it always did.
pub fn load_managed_agents<R: tauri::Runtime>(
app: &AppHandle<R>,
) -> Result<Vec<ManagedAgentRecord>, String> {
let mut records = load_agent_store(app)?;
filter_managed_agents_for_build(
&mut records,
crate::managed_agents::personas::bestie_build_enabled(),
);
hydrate_keys(&mut records);
Ok(records)
}
pub(crate) fn filter_managed_agents_for_build(
records: &mut Vec<ManagedAgentRecord>,
include_bestie: bool,
) {
records.retain(|record| {
!record.pubkey.is_empty()
&& record.persona_id.as_deref().is_none_or(|persona_id| {
crate::managed_agents::personas::persona_available_in_build(
persona_id,
include_bestie,
)
})
});
}
/// Load the key-less agent *definitions* (former personas) from the unified
/// store. The persona compatibility shim (`load_personas`) presents these in
/// the legacy shape via `to_definition_view`.
pub(crate) fn load_agent_definitions<R: tauri::Runtime>(
app: &AppHandle<R>,
) -> Result<Vec<ManagedAgentRecord>, String> {
let mut records = load_agent_definitions_unfiltered(app)?;
filter_agent_definitions_for_build(
&mut records,
crate::managed_agents::personas::bestie_build_enabled(),
);
Ok(records)
}
pub(crate) fn filter_agent_definitions_for_build(
records: &mut Vec<ManagedAgentRecord>,
include_bestie: bool,
) {
records.retain(|record| {
record.pubkey.is_empty()
&& record.slug.as_deref().is_none_or(|slug| {
crate::managed_agents::personas::persona_available_in_build(slug, include_bestie)
})
});
}
pub(crate) fn load_agent_definitions_unfiltered<R: tauri::Runtime>(
app: &AppHandle<R>,
) -> Result<Vec<ManagedAgentRecord>, String> {
let mut records = load_agent_store(app)?;
records.retain(|record| record.pubkey.is_empty());
Ok(records)
}
/// Preserve a malformed store file as `<name>.invalid` before the error path
/// unwinds. Copy, not rename: the original stays in place so repeated boots
/// keep failing loudly (rename would make the next launch look like a fresh
@@ -443,48 +369,6 @@ pub fn save_managed_agents<R: tauri::Runtime>(
write_agent_store(app, definitions, sorted)
}
fn instances_for_save(
records: &[ManagedAgentRecord],
existing: &[ManagedAgentRecord],
include_bestie: bool,
) -> Vec<ManagedAgentRecord> {
let mut complete: Vec<_> = records
.iter()
.filter(|record| {
!record.pubkey.is_empty()
&& record.persona_id.as_deref().is_none_or(|persona_id| {
crate::managed_agents::personas::persona_available_in_build(
persona_id,
include_bestie,
)
})
})
.cloned()
.collect();
if !include_bestie {
let hidden: Vec<_> = existing
.iter()
.filter(|record| {
!record.pubkey.is_empty()
&& record.persona_id.as_deref().is_some_and(|persona_id| {
!crate::managed_agents::personas::persona_available_in_build(
persona_id,
include_bestie,
)
})
&& !complete
.iter()
.any(|candidate| candidate.pubkey == record.pubkey)
})
.cloned()
.collect();
complete.extend(hidden);
}
complete
}
/// Save the key-less agent *definitions*, preserving the keyed instances —
/// the definition-side mirror of [`save_managed_agents`].
pub(crate) fn save_agent_definitions<R: tauri::Runtime>(

View File

@@ -0,0 +1,131 @@
use std::{fs, path::Path};
use crate::managed_agents::ManagedAgentRecord;
use tauri::AppHandle;
use super::{backup_invalid_store, hydrate_keys, load_agent_store};
pub(crate) fn load_agent_store_from_path(path: &Path) -> Result<Vec<ManagedAgentRecord>, String> {
if !path.exists() {
return Ok(Vec::new());
}
let content =
fs::read_to_string(path).map_err(|error| format!("failed to read agent store: {error}"))?;
serde_json::from_str(&content).map_err(|error| {
// Fail loudly and preserve the evidence: a later in-app save rewrites
// this file wholesale, which would silently destroy a malformed hand
// edit. Best-effort file-authoring contract (see managed_agents::
// reconcile): the broken content survives as `.invalid` for the user
// to recover, and the parse error propagates instead of being
// swallowed into an empty store.
backup_invalid_store(path);
format!("failed to parse agent store (preserved as .invalid): {error}")
})
}
/// Load the keyed agent *instances*. Key-less definitions (former personas,
/// folded into the same store) are filtered out so every pre-fold call site
/// keeps seeing exactly the records it always did.
pub fn load_managed_agents<R: tauri::Runtime>(
app: &AppHandle<R>,
) -> Result<Vec<ManagedAgentRecord>, String> {
let mut records = load_agent_store(app)?;
filter_managed_agents_for_build(
&mut records,
crate::managed_agents::personas::bestie_build_enabled(),
);
hydrate_keys(&mut records);
Ok(records)
}
/// Load the key-less agent *definitions* (former personas) from the unified
/// store. The persona compatibility shim (`load_personas`) presents these in
/// the legacy shape via `to_definition_view`.
pub(crate) fn load_agent_definitions<R: tauri::Runtime>(
app: &AppHandle<R>,
) -> Result<Vec<ManagedAgentRecord>, String> {
let mut records = load_agent_definitions_unfiltered(app)?;
filter_agent_definitions_for_build(
&mut records,
crate::managed_agents::personas::bestie_build_enabled(),
);
Ok(records)
}
pub(crate) fn load_agent_definitions_unfiltered<R: tauri::Runtime>(
app: &AppHandle<R>,
) -> Result<Vec<ManagedAgentRecord>, String> {
let mut records = load_agent_store(app)?;
records.retain(|record| record.pubkey.is_empty());
Ok(records)
}
pub(crate) fn filter_managed_agents_for_build(
records: &mut Vec<ManagedAgentRecord>,
include_bestie: bool,
) {
records.retain(|record| {
!record.pubkey.is_empty()
&& record.persona_id.as_deref().is_none_or(|persona_id| {
crate::managed_agents::personas::persona_available_in_build(
persona_id,
include_bestie,
)
})
});
}
pub(crate) fn filter_agent_definitions_for_build(
records: &mut Vec<ManagedAgentRecord>,
include_bestie: bool,
) {
records.retain(|record| {
record.pubkey.is_empty()
&& record.slug.as_deref().is_none_or(|slug| {
crate::managed_agents::personas::persona_available_in_build(slug, include_bestie)
})
});
}
pub(super) fn instances_for_save(
records: &[ManagedAgentRecord],
existing: &[ManagedAgentRecord],
include_bestie: bool,
) -> Vec<ManagedAgentRecord> {
let mut complete: Vec<_> = records
.iter()
.filter(|record| {
!record.pubkey.is_empty()
&& record.persona_id.as_deref().is_none_or(|persona_id| {
crate::managed_agents::personas::persona_available_in_build(
persona_id,
include_bestie,
)
})
})
.cloned()
.collect();
if !include_bestie {
let hidden: Vec<_> = existing
.iter()
.filter(|record| {
!record.pubkey.is_empty()
&& record.persona_id.as_deref().is_some_and(|persona_id| {
!crate::managed_agents::personas::persona_available_in_build(
persona_id,
include_bestie,
)
})
&& !complete
.iter()
.any(|candidate| candidate.pubkey == record.pubkey)
})
.cloned()
.collect();
complete.extend(hidden);
}
complete
}