Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
376b5cb
Revamp new agent dialog
klopez4212 Jun 23, 2026
c1fcc13
Fix imported persona avatar handling
klopez4212 Jun 25, 2026
28d5b6c
Fix persona dialog create-agent edge cases
klopez4212 Jun 25, 2026
7e642bd
Fix persona dialog pending states
klopez4212 Jun 25, 2026
70ee7f7
Filter imported avatars and allow custom models
klopez4212 Jun 25, 2026
be8e9a1
Filter unsafe imported avatar refs
klopez4212 Jun 25, 2026
612db90
Fix persona dialog review feedback
klopez4212 Jun 25, 2026
5f1d8cc
Restore persona avatar clearing
klopez4212 Jun 25, 2026
37ef610
Reveal secrets for persona-created agents
klopez4212 Jun 25, 2026
d389252
Preserve persona runtime aliases on create
klopez4212 Jun 25, 2026
aebc73d
Restore persona name pool editing
klopez4212 Jun 25, 2026
c384179
Keep persona create dialog pending during avatar resolution
klopez4212 Jun 25, 2026
1e2f5a6
Polish new agent dialog
klopez4212 Jun 26, 2026
88b7af5
Fix persona catalog e2e selector
klopez4212 Jun 26, 2026
b237269
Wait for dialog animations before env var screenshot
klopez4212 Jun 26, 2026
8f054f6
Merge remote-tracking branch 'origin/main' into kennylopez-new-agent-…
klopez4212 Jun 26, 2026
7d614ef
Filter unresolved imported avatar refs
klopez4212 Jun 26, 2026
97150fa
Upload imported persona avatars before save
klopez4212 Jun 26, 2026
c1e0425
Keep model picker visible for auto provider
klopez4212 Jun 26, 2026
1a93f6e
Update model picker E2E expectation
klopez4212 Jun 26, 2026
d4ecc0a
Preserve model when provider returns to auto
klopez4212 Jun 26, 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
4 changes: 3 additions & 1 deletion desktop/scripts/check-file-sizes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ const overrides = new Map([
// 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);
// alias-preservation coverage extends that matrix so create-time persona
// agents keep an installed runtime alias when the primary command is absent.
// types.rs adds the persona/instance harness fields. Load-bearing, not
// generic debt.
["src-tauri/src/managed_agents/discovery.rs", 1043],
["src-tauri/src/managed_agents/discovery.rs", 1085],
["src-tauri/src/managed_agents/types.rs", 1037],
// migration_tests.rs carries the harness-sync migration coverage plus the
// patch_json_records owner-only writeback regression test (SECURITY.md:90
Expand Down
47 changes: 44 additions & 3 deletions desktop/src-tauri/src/managed_agents/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,9 @@ pub fn divergent_agent_command_override(
/// distinct cases that the backend MUST tell apart:
///
/// - DELIBERATE OVERRIDE (`harness_override` true): the user explicitly picked a
/// non-persona runtime in a deploy dialog that exposes a runtime selector (e.g.
/// `AddChannelBotDialog`, "overriding persona preferences"). This is a real pin
/// and is preserved via `divergent_agent_command_override`.
/// runtime command in UI that exposes a runtime selector. This is a real pin
/// and is preserved when it differs from the command inheritance would spawn,
/// including installed aliases such as `claude-code-acp`.
/// - MISSING-RUNTIME FALLBACK (`harness_override` false): the persona's runtime
/// isn't installed locally, so `resolvePersonaRuntime` substitutes a fallback
/// default. This is NOT a pin — baking it would freeze the agent on the fallback
Expand All @@ -341,6 +341,15 @@ pub fn create_time_agent_command_override(
if persona_id.is_some() && !harness_override {
return None;
}

if persona_id.is_some() && harness_override {
let picked = picked_command
.map(str::trim)
.filter(|value| !value.is_empty())?;
let inherited_command = effective_agent_command(persona_id, personas, None);
return (picked != inherited_command).then(|| picked.to_string());
}

divergent_agent_command_override(persona_id, personas, picked_command)
}

Expand Down Expand Up @@ -1027,6 +1036,38 @@ mod tests {
);
}

#[test]
fn create_time_override_preserves_selected_runtime_alias() {
// A `claude` persona inherits the primary command `claude-agent-acp`,
// but discovery may select an installed alias such as `claude-code-acp`.
// When UI marks that create-time selection as explicit, preserve the
// alias so the first spawn uses a command known to be installed.
let personas = vec![persona_with_runtime("p1", Some("claude"))];
assert_eq!(
create_time_agent_command_override(
Some("p1"),
&personas,
Some("claude-code-acp"),
true
),
Some("claude-code-acp".to_string())
);
}

#[test]
fn create_time_override_inherits_exact_persona_command() {
let personas = vec![persona_with_runtime("p1", Some("claude"))];
assert_eq!(
create_time_agent_command_override(
Some("p1"),
&personas,
Some("claude-agent-acp"),
true
),
None
);
}

#[test]
fn create_time_override_preserves_pin_for_persona_less_create() {
// The standalone CreateAgentDialog creates persona-LESS agents. With no
Expand Down
15 changes: 15 additions & 0 deletions desktop/src-tauri/src/managed_agents/persona_card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct ParsedPersonaPreview {
pub display_name: String,
pub system_prompt: String,
pub avatar_data_url: Option<String>,
pub avatar_ref: Option<String>,
pub runtime: Option<String>,
pub model: Option<String>,
pub provider: Option<String>,
Expand Down Expand Up @@ -69,6 +70,7 @@ pub fn parse_png_persona(png_bytes: &[u8]) -> Result<ParsedPersonaPreview, Strin
display_name: fields.display_name,
system_prompt: fields.system_prompt,
avatar_data_url,
avatar_ref: None,
runtime: fields.runtime,
model: fields.model,
provider: fields.provider,
Expand Down Expand Up @@ -224,6 +226,7 @@ pub fn parse_json_persona(json_bytes: &[u8]) -> Result<ParsedPersonaPreview, Str
display_name: fields.display_name,
system_prompt: fields.system_prompt,
avatar_data_url: fields.avatar_url,
avatar_ref: None,
runtime: fields.runtime,
model: fields.model,
provider: fields.provider,
Expand Down Expand Up @@ -284,6 +287,7 @@ pub fn parse_md_persona(md_bytes: &[u8]) -> Result<ParsedPersonaPreview, String>
display_name: config.display_name,
system_prompt: config.prompt,
avatar_data_url: None, // .persona.md avatars are paths, not data URIs
avatar_ref: config.avatar,
runtime: config.runtime,
model,
provider: None, // .persona.md format does not carry llmProvider
Expand Down Expand Up @@ -385,6 +389,7 @@ pub fn parse_zip_pack(zip_bytes: &[u8]) -> Result<ParsePersonaFilesResult, Strin
display_name: p.display_name.clone(),
system_prompt: p.system_prompt.clone(),
avatar_data_url: None,
avatar_ref: p.avatar.clone(),
runtime: p.runtime.clone(),
model: p.model.clone(),
provider: None, // persona packs do not carry llmProvider
Expand Down Expand Up @@ -806,6 +811,16 @@ mod tests {
assert!(result.source_file.is_empty());
}

#[test]
fn parse_md_carries_avatar_ref() {
let md = b"---\nname: goosey\ndisplay_name: Goosey\navatar: app-avatar:gloopies-19\ndescription: A goose persona.\n---\nYou are Goosey.\n";
let result = parse_md_persona(md).unwrap();

assert_eq!(result.display_name, "Goosey");
assert!(result.avatar_data_url.is_none());
assert_eq!(result.avatar_ref.as_deref(), Some("app-avatar:gloopies-19"));
}

#[test]
fn parse_json_round_trip_no_avatar() {
let bytes =
Expand Down
9 changes: 4 additions & 5 deletions desktop/src-tauri/src/managed_agents/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,10 @@ pub struct CreateManagedAgentRequest {
pub relay_url: Option<String>,
pub acp_command: Option<String>,
pub agent_command: Option<String>,
/// True when `agent_command` is a runtime the user deliberately picked to
/// override the linked persona (a deploy-dialog runtime selector). Distinguishes
/// a real pin from a missing-runtime fallback so a persona-backed create only
/// stores an `agent_command_override` for the former. Defaults `false`: callers
/// that don't set it (persona-less creates, fallback divergence) inherit.
/// True when `agent_command` is a runtime command the user deliberately
/// picked for a linked persona. Distinguishes a real selection, including an
/// installed alias, from a missing-runtime fallback so a persona-backed
/// create only stores an `agent_command_override` for the former.
#[serde(default)]
pub harness_override: bool,
#[serde(default)]
Expand Down
Loading