Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d6fe9de
Refine agent catalog sharing
klopez4212 Jul 22, 2026
ea89b93
Stack team card avatars
klopez4212 Jul 23, 2026
d41e6cc
Simplify team avatar stack styling
klopez4212 Jul 23, 2026
1320d64
Add catalog update publishing
klopez4212 Jul 23, 2026
eddd919
Clarify catalog copy behavior
klopez4212 Jul 23, 2026
b561105
Use community in catalog sharing copy
klopez4212 Jul 23, 2026
691b305
Update agent creation test selectors
klopez4212 Jul 23, 2026
40301c3
Constrain shared agent catalog details
klopez4212 Jul 23, 2026
c7aa81a
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 23, 2026
f08ab69
Align catalog publish option with dialog footer
klopez4212 Jul 23, 2026
6699102
Place catalog publish action before toggle
klopez4212 Jul 23, 2026
a1570f1
Group catalog sharing with link controls
klopez4212 Jul 23, 2026
20afa18
Use catalog sharing access menu
klopez4212 Jul 23, 2026
5dfc793
Keep copy link action last
klopez4212 Jul 23, 2026
597bdf0
Default catalog sharing to off
klopez4212 Jul 23, 2026
4873d35
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 23, 2026
0520fe0
Publish catalog updates on save
klopez4212 Jul 23, 2026
453dd7a
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 23, 2026
2b8f1d7
Publish agent catalog entries to relay
klopez4212 Jul 23, 2026
d89b7db
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 23, 2026
e17d283
Fix catalog snapshot test after main merge
klopez4212 Jul 23, 2026
2a780d4
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 24, 2026
a7c5be1
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 24, 2026
e2abf33
fix(desktop): update agent creation e2e selector
klopez4212 Jul 24, 2026
b72e024
fix(desktop): align agent header with card grid
klopez4212 Jul 24, 2026
e09fa64
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 24, 2026
dd22a7c
fix(desktop): harden catalog publication imports
klopez4212 Jul 24, 2026
e549945
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 25, 2026
d211ace
Use persona events for catalog sharing
klopez4212 Jul 26, 2026
a305bc9
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 26, 2026
ec4ec14
Stabilize agent catalog E2E coverage
klopez4212 Jul 27, 2026
fe36d15
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 27, 2026
d1005cd
Keep merged agent files within size limits
klopez4212 Jul 27, 2026
53c3245
Fix portable descendant probe fixture
klopez4212 Jul 27, 2026
8eba922
Scope catalog sharing to communities
klopez4212 Jul 27, 2026
9f082be
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
klopez4212 Jul 27, 2026
fbc9d51
fix(desktop): migrate the legacy global retention db into the active …
wpfleger96 Jul 27, 2026
4340f83
fix(desktop): keep agent list and edits working in recovery mode
wpfleger96 Jul 27, 2026
b6439c7
feat(desktop): add update_persona_and_publish for save-and-publish
wpfleger96 Jul 27, 2026
5fffd16
feat(desktop): track catalog provenance on a copied persona
wpfleger96 Jul 27, 2026
ac9f33d
fix(desktop): scope an inbound persona event to the relay it arrived on
wpfleger96 Jul 27, 2026
89b055e
fix(desktop): honor publishCatalogUpdates on "Save and publish"
wpfleger96 Jul 27, 2026
90cba05
fix(desktop): stop re-adding an already-added foreign catalog agent
wpfleger96 Jul 27, 2026
97ab227
fix(desktop): page the persona catalog past the relay's 1,000-row clamp
wpfleger96 Jul 27, 2026
954cc23
test(desktop): stop the e2e mock publishing from update_persona
wpfleger96 Jul 27, 2026
09afe06
Merge branch 'main' into kennylopez-agent-catalog-sharing
wpfleger96 Jul 27, 2026
184deb2
Merge branch 'main' into kennylopez-agent-catalog-sharing
wpfleger96 Jul 27, 2026
ccba0c1
fix(desktop): label the catalog share toggle Shared instead of Agent …
wpfleger96 Jul 27, 2026
36af667
fix(desktop): unify the share dialog memory level into one selector
wpfleger96 Jul 27, 2026
b248eee
fix(desktop): group the share dialog's delivery rows above its options
wpfleger96 Jul 28, 2026
2a38069
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
wpfleger96 Jul 28, 2026
9080a79
refactor(desktop): remove the Active in communities settings card
wpfleger96 Jul 28, 2026
0b3e93b
fix(desktop): keep emoji avatars on catalog entries
wpfleger96 Jul 28, 2026
7980428
test(desktop): cover emoji avatars on catalog entries
wpfleger96 Jul 28, 2026
cd3e8c2
Merge remote-tracking branch 'origin/main' into kennylopez-agent-cata…
wpfleger96 Jul 28, 2026
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
15 changes: 11 additions & 4 deletions desktop/scripts/check-file-sizes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ const overrides = new Map([
// 3-phase (stage/stop/commit) + commit_cascade_agents injectable helper for
// retry-safety. Load-bearing reviewer-required change; queued to split.
// Consolidation removed the legacy persona-card import/export codecs.
["src-tauri/src/commands/personas/mod.rs", 984],
// #1418 read-path fix: get_thread_replies' blocker fix (shared TIMELINE_KINDS
// const + build_thread_replies_filter helper, mirroring the channel sibling so
// the two p-gate filters can't drift) plus two guard unit tests. The file was
Expand Down Expand Up @@ -257,7 +256,11 @@ const overrides = new Map([
// (#2680) to indicate runtimes that need a separate CLI install.
// +6: ManagedAgent.runtime record-level pin + JSDoc so the harness delete
// confirmation can count referencing agents (review fix for #2773).
["src/shared/api/types.ts", 1058],
// +21: CatalogSourceCoordinate + the `catalogSource` fields on AgentPersona
// and CreatePersonaInput. The coordinate is the only identifier a catalog
// copy keeps, so it is what stops the catalog re-offering "Add" for an
// already-added foreign entry. Queued to split.
["src/shared/api/types.ts", 1079],
// harness-persona-sync feature growth, queued to split in the resolver-unify
// refactor followup. discovery.rs is dominated by the new test module
// (the effective_agent_command / divergent / create-time override matrix);
Expand Down Expand Up @@ -398,7 +401,9 @@ const overrides = new Map([
// the full (major, minor, patch) triple instead of a bare major, plus
// below-the-floor and uncomparable-version (partial / prerelease) classification
// regressions for the fail-closed parse.
["src-tauri/src/managed_agents/discovery/tests.rs", 1922],
// +2 (1922 -> 1924): the AgentDefinition and ManagedAgentRecord fixtures each
// set the new mandatory `catalog_source` field.
["src-tauri/src/managed_agents/discovery/tests.rs", 1924],
// identity-import-keyring: the identity resolution state machine's behavioral
// matrix (46 tests over FakeIdentityStore — probe × marker × file cells,
// adoption / read-back-corruption / marker-failure arms, recovery-mode
Expand Down Expand Up @@ -593,7 +598,9 @@ const overrides = new Map([
// computing had_* so stale materialized snapshot bytes can never be tagged
// BuzzExplicit and shadow the definition/global fallthrough; the dead
// persona-model re-tag branch replaced; two new regression tests added.
["src-tauri/src/commands/agent_config.rs", 1110],
// +2 (1110 -> 1112): the agent_record and persona_with_model test fixtures
// each set the new mandatory `catalog_source` field.
["src-tauri/src/commands/agent_config.rs", 1112],
// codex-install-auto-restart review-fixes: should_restart_after_install
// takes pid_alive:bool (pure predicate, no OS-dependent call); 3 racy
// cache tests replaced with 6 pure availability_drift predicate tests;
Expand Down
7 changes: 5 additions & 2 deletions desktop/src-tauri/src/commands/agent_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ use crate::{

/// Subset of the goose file config exposed to the frontend for gate evaluation.
///
/// Only the fields the dialog gate needs — not the full `RuntimeConfigSurface`.
/// The gate uses this to know which requirements are already satisfied in the
/// Only the fields the dialog gate needs. This tracks which requirements are already satisfied in the
/// harness config file, so it can show "Set in goose config" rather than
/// surfacing a false missing-key marker.
#[derive(Debug, Serialize)]
Expand Down Expand Up @@ -685,8 +684,10 @@ mod tests {
name_pool: Vec::new(),
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source: None,
definition_respond_to: None,
definition_respond_to_allowlist: Vec::new(),
definition_parallelism: None,
Expand All @@ -709,8 +710,10 @@ mod tests {
name_pool: Vec::new(),
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source: None,
env_vars: Default::default(),
respond_to: None,
respond_to_allowlist: Vec::new(),
Expand Down
2 changes: 2 additions & 0 deletions desktop/src-tauri/src/commands/agent_models_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,10 @@ fn model_discovery_ignores_stale_record_for_linked_agent() {
name_pool: Vec::new(),
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source: None,
env_vars: BTreeMap::new(),
respond_to: None,
respond_to_allowlist: Vec::new(),
Expand Down
41 changes: 18 additions & 23 deletions desktop/src-tauri/src/commands/agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::{
managed_agents::{
build_managed_agent_summary, current_instance_id, discover_provider_candidates,
ensure_persona_is_active, find_managed_agent_mut, load_managed_agents, load_personas,
load_teams, managed_agent_avatar_url, managed_agents_base_dir, normalize_agent_args,
provider_deploy, resolve_provider_binary, save_managed_agents, start_managed_agent_process,
load_teams, managed_agent_avatar_url, normalize_agent_args, provider_deploy,
resolve_provider_binary, save_managed_agents, start_managed_agent_process,
stop_managed_agent_process, stop_managed_agent_workspace_pair,
sync_managed_agent_processes, try_regenerate_nest, validate_provider_config, BackendKind,
CreateManagedAgentRequest, CreateManagedAgentResponse, ManagedAgentRecord,
Expand All @@ -18,8 +18,7 @@ use crate::{
util::now_iso,
};

/// Read the workspace owner's pubkey hex from app state without holding the
/// lock for longer than necessary. Used to populate `BUZZ_ACP_AGENT_OWNER`
/// Read the workspace owner pubkey without holding the lock. Used to populate `BUZZ_ACP_AGENT_OWNER`
/// as a fallback for legacy agent records that have no NIP-OA `auth_tag`.
pub(super) fn workspace_owner_hex(state: &AppState) -> Result<String, String> {
let keys = state.keys.lock().map_err(|e| e.to_string())?;
Expand Down Expand Up @@ -48,12 +47,12 @@ pub(super) fn retain_managed_agent_pending(
use crate::managed_agents::{reconcile::retain_agent_record, retention::open_retention_db};

let result = (|| -> Result<(), String> {
let conn = open_retention_db(&managed_agents_base_dir(app)?.join("retention.db"))?;
let keys = state.signing_keys()?;
let scope = crate::managed_agents::retention::active_retention_scope(app, state)?;
let conn = open_retention_db(&scope.db_path)?;
// Shared engine with the boot-time reconcile: projection content diff
// (no republish for runtime-only churn) + monotonic created_at bump
// past the retained head (NIP-AP step 3).
retain_agent_record(&conn, &keys, record).map(|_| ())
retain_agent_record(&conn, &scope.owner_keys, record).map(|_| ())
})();
if let Err(e) = result {
eprintln!("buzz-desktop: agent-retain: {e}");
Expand Down Expand Up @@ -89,15 +88,12 @@ pub(super) fn tombstone_managed_agent_pending(
const KIND_DELETE: u32 = 5;

let result = (|| -> Result<(), String> {
let (owner_pubkey, event) = {
let keys = state.signing_keys()?;
let owner_pubkey = keys.public_key().to_hex();
let event = build_agent_delete(agent_pubkey, &owner_pubkey)?
.sign_with_keys(&keys)
.map_err(|e| format!("failed to sign managed-agent tombstone: {e}"))?;
(owner_pubkey, event)
};
let conn = open_retention_db(&managed_agents_base_dir(app)?.join("retention.db"))?;
let scope = crate::managed_agents::retention::active_retention_scope(app, state)?;
let owner_pubkey = scope.owner_keys.public_key().to_hex();
let event = build_agent_delete(agent_pubkey, &owner_pubkey)?
.sign_with_keys(&scope.owner_keys)
.map_err(|e| format!("failed to sign managed-agent tombstone: {e}"))?;
let conn = open_retention_db(&scope.db_path)?;
delete_retained_event(&conn, KIND_MANAGED_AGENT, &owner_pubkey, agent_pubkey)?;
retain_event(
&conn,
Expand Down Expand Up @@ -183,13 +179,10 @@ pub(super) fn archive_managed_agent_pending(app: &AppHandle, state: &AppState, a
use nostr::JsonUtil;

let result = (|| -> Result<(), String> {
let (owner_pubkey, event) = {
let keys = state.signing_keys()?;
let owner_pubkey = keys.public_key().to_hex();
let event = build_agent_archive_request(&keys, agent_pubkey)?;
(owner_pubkey, event)
};
let conn = open_retention_db(&managed_agents_base_dir(app)?.join("retention.db"))?;
let scope = crate::managed_agents::retention::active_retention_scope(app, state)?;
let owner_pubkey = scope.owner_keys.public_key().to_hex();
let event = build_agent_archive_request(&scope.owner_keys, agent_pubkey)?;
let conn = open_retention_db(&scope.db_path)?;
retain_event(
&conn,
&RetainedEvent {
Expand Down Expand Up @@ -904,8 +897,10 @@ pub async fn create_managed_agent(
name_pool: Vec::new(),
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source: None,
definition_respond_to: None,
definition_respond_to_allowlist: Vec::new(),
definition_parallelism: None,
Expand Down
4 changes: 4 additions & 0 deletions desktop/src-tauri/src/commands/agents_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ fn bare_agent_record(
name_pool: vec![],
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source: None,
relay_mesh: None,
auto_restart_on_config_change: false,
definition_respond_to: None,
Expand All @@ -75,8 +77,10 @@ fn persona_record(id: &str, model: Option<&str>, provider: Option<&str>) -> Agen
name_pool: vec![],
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source: None,
env_vars: BTreeMap::new(),
respond_to: None,
respond_to_allowlist: vec![],
Expand Down
3 changes: 3 additions & 0 deletions desktop/src-tauri/src/commands/media_download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ mod tests {
version: FORMAT_VERSION,
definition: AgentSnapshotDefinition {
name: "test".to_string(),
source_is_builtin: false,
system_prompt: None,
runtime: None,
model: None,
Expand Down Expand Up @@ -659,6 +660,7 @@ mod tests {
version: FORMAT_VERSION,
definition: AgentSnapshotDefinition {
name: "test".to_string(),
source_is_builtin: false,
system_prompt: None,
runtime: None,
model: None,
Expand Down Expand Up @@ -704,6 +706,7 @@ mod tests {
version: FORMAT_VERSION,
definition: AgentSnapshotDefinition {
name: "test".to_string(),
source_is_builtin: false,
system_prompt: None,
runtime: None,
model: None,
Expand Down
1 change: 1 addition & 0 deletions desktop/src-tauri/src/commands/media_snapshot_png.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ mod tests {
version: 1,
definition: AgentSnapshotDefinition {
name: "Tree Trunks".to_string(),
source_is_builtin: false,
system_prompt: Some("You are a helpful agent.".to_string()),
runtime: Some("goose".to_string()),
model: None,
Expand Down
85 changes: 85 additions & 0 deletions desktop/src-tauri/src/commands/personas/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! The persona creation command surface, split from `mod.rs` (file-size cap)
//! as the sibling of [`super::update`].

use tauri::AppHandle;
use uuid::Uuid;

use crate::{
app_state::AppState,
managed_agents::{
apply_persona_behavior, load_personas, save_personas, try_regenerate_nest, AgentDefinition,
CatalogSource, CreatePersonaRequest,
},
util::now_iso,
};

use super::{pending, retain_persona_pending, trim_optional, trim_required};

#[tauri::command]
pub async fn create_persona(
input: CreatePersonaRequest,
app: AppHandle,
) -> Result<AgentDefinition, String> {
use tauri::Manager;
tokio::task::spawn_blocking(move || {
let state = app.state::<AppState>();
let display_name = trim_required(&input.display_name, "Display name")?;
// System prompt optional: core memory is auto-injected. Empty is valid.
let system_prompt = input.system_prompt.trim().to_string();
let avatar_url = trim_optional(input.avatar_url);
let runtime = trim_optional(input.runtime);
let model = trim_optional(input.model);
let provider = trim_optional(input.provider);
// Normalized before the store is touched: a coordinate that can't match
// a publication is worse than no coordinate, because it silently
// re-enables the duplicate add it exists to prevent.
let catalog_source = input
.catalog_source
.map(CatalogSource::normalized)
.transpose()?;
let now = now_iso();
let _store_guard = state
.managed_agents_store_lock
.lock()
.map_err(|error| error.to_string())?;
let mut personas = load_personas(&app)?;
pending::project_active_persona_sharing(&app, &state, &mut personas);
let name_pool: Vec<String> = input
.name_pool
.into_iter()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
crate::managed_agents::validate_user_env_keys(&input.env_vars)?;
let mut persona = AgentDefinition {
id: Uuid::new_v4().to_string(),
display_name,
avatar_url,
system_prompt,
runtime,
model,
provider,
name_pool,
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source,
env_vars: input.env_vars,
respond_to: None,
respond_to_allowlist: Vec::new(),
parallelism: None,
created_at: now.clone(),
updated_at: now,
};
apply_persona_behavior(&mut persona, input.behavior)?;
personas.push(persona.clone());
save_personas(&app, &personas)?;
retain_persona_pending(&app, &state, &persona);
try_regenerate_nest(&app);
Ok(persona)
})
.await
.map_err(|e| format!("spawn_blocking failed: {e}"))?
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ fn make_agent(
name_pool: vec![],
is_builtin: false,
is_active: true,
shared: false,
source_team: None,
source_team_persona_slug: None,
catalog_source: None,
relay_mesh: None,
auto_restart_on_config_change: false,
definition_respond_to: None,
Expand Down
Loading
Loading