From fa939ec1c30a8c95e1e5cfb0bfac78225124655d Mon Sep 17 00:00:00 2001 From: Wes Date: Mon, 16 Mar 2026 11:34:44 -0700 Subject: [PATCH] Polish chat replies and managed agent creation (#81) --- desktop/src-tauri/src/commands/agents.rs | 17 ++++++- .../src-tauri/src/managed_agents/runtime.rs | 8 +++ desktop/src-tauri/src/managed_agents/types.rs | 12 +++++ .../features/agents/ui/CreateAgentDialog.tsx | 16 +++++- .../agents/ui/CreateAgentDialogSections.tsx | 50 ++++++++++++++++++- .../agents/ui/ManagedAgentLogPanel.tsx | 5 +- .../messages/lib/formatTimelineMessages.ts | 6 --- desktop/src/features/messages/types.ts | 2 - .../src/features/messages/ui/MessageRow.tsx | 27 ---------- .../src/features/sidebar/ui/AppSidebar.tsx | 1 + desktop/src/shared/api/tauri.ts | 6 +++ desktop/src/shared/api/types.ts | 4 ++ desktop/src/testing/e2eBridge.ts | 15 +++++- desktop/tests/e2e/messaging.spec.ts | 2 + desktop/tests/e2e/smoke.spec.ts | 30 +++++++++++ 15 files changed, 161 insertions(+), 40 deletions(-) diff --git a/desktop/src-tauri/src/commands/agents.rs b/desktop/src-tauri/src/commands/agents.rs index ef2654851d..fc31acb9f1 100644 --- a/desktop/src-tauri/src/commands/agents.rs +++ b/desktop/src-tauri/src/commands/agents.rs @@ -13,7 +13,7 @@ use crate::{ DiscoverManagedAgentPrereqsRequest, ManagedAgentLogResponse, ManagedAgentPrereqsInfo, ManagedAgentSummary, MintManagedAgentTokenRequest, MintManagedAgentTokenResponse, RelayAgentInfo, DEFAULT_ACP_COMMAND, DEFAULT_AGENT_ARG, DEFAULT_AGENT_COMMAND, - DEFAULT_AGENT_TURN_TIMEOUT_SECONDS, DEFAULT_MCP_COMMAND, + DEFAULT_AGENT_PARALLELISM, DEFAULT_AGENT_TURN_TIMEOUT_SECONDS, DEFAULT_MCP_COMMAND, }, relay::{ build_authed_request, managed_agent_owner_pubkey, relay_ws_url, send_json_request, @@ -94,6 +94,11 @@ pub async fn create_managed_agent( if name.is_empty() { return Err("agent name is required".to_string()); } + if let Some(parallelism) = input.parallelism { + if !(1..=32).contains(¶llelism) { + return Err("parallelism must be between 1 and 32".to_string()); + } + } let owner_pubkey = if input.mint_token { Some(managed_agent_owner_pubkey(&state).await?) @@ -209,6 +214,16 @@ pub async fn create_managed_agent( .turn_timeout_seconds .filter(|seconds| *seconds > 0) .unwrap_or(DEFAULT_AGENT_TURN_TIMEOUT_SECONDS), + parallelism: input + .parallelism + .filter(|count| (1..=32).contains(count)) + .unwrap_or(DEFAULT_AGENT_PARALLELISM), + system_prompt: input + .system_prompt + .as_deref() + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_string), created_at: now_iso(), updated_at: now_iso(), last_started_at: None, diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index b0e27381a5..66bef77bca 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -88,6 +88,8 @@ pub fn build_managed_agent_summary( agent_args: record.agent_args.clone(), mcp_command: record.mcp_command.clone(), turn_timeout_seconds: record.turn_timeout_seconds, + parallelism: record.parallelism, + system_prompt: record.system_prompt.clone(), has_api_token: record.api_token.is_some(), status, pid, @@ -167,10 +169,16 @@ pub fn start_managed_agent_process( "SPROUT_ACP_TURN_TIMEOUT", record.turn_timeout_seconds.to_string(), ); + command.env("SPROUT_ACP_AGENTS", record.parallelism.to_string()); command.env( "GOOSE_MODE", std::env::var("GOOSE_MODE").unwrap_or_else(|_| "auto".to_string()), ); + if let Some(system_prompt) = &record.system_prompt { + command.env("SPROUT_ACP_SYSTEM_PROMPT", system_prompt); + } else { + command.env_remove("SPROUT_ACP_SYSTEM_PROMPT"); + } command.env_remove("SPROUT_ACP_PRIVATE_KEY"); command.env_remove("SPROUT_ACP_API_TOKEN"); diff --git a/desktop/src-tauri/src/managed_agents/types.rs b/desktop/src-tauri/src/managed_agents/types.rs index 374b60ae1d..d7dea01b19 100644 --- a/desktop/src-tauri/src/managed_agents/types.rs +++ b/desktop/src-tauri/src/managed_agents/types.rs @@ -24,6 +24,9 @@ pub struct ManagedAgentRecord { pub agent_args: Vec, pub mcp_command: String, pub turn_timeout_seconds: u64, + #[serde(default = "default_agent_parallelism")] + pub parallelism: u32, + pub system_prompt: Option, pub created_at: String, pub updated_at: String, pub last_started_at: Option, @@ -48,6 +51,8 @@ pub struct ManagedAgentSummary { pub agent_args: Vec, pub mcp_command: String, pub turn_timeout_seconds: u64, + pub parallelism: u32, + pub system_prompt: Option, pub has_api_token: bool, pub status: String, pub pid: Option, @@ -71,6 +76,8 @@ pub struct CreateManagedAgentRequest { pub agent_args: Vec, pub mcp_command: Option, pub turn_timeout_seconds: Option, + pub parallelism: Option, + pub system_prompt: Option, #[serde(default)] pub mint_token: bool, #[serde(default)] @@ -155,3 +162,8 @@ pub const DEFAULT_AGENT_COMMAND: &str = "goose"; pub const DEFAULT_MCP_COMMAND: &str = "sprout-mcp-server"; pub const DEFAULT_AGENT_ARG: &str = "acp"; pub const DEFAULT_AGENT_TURN_TIMEOUT_SECONDS: u64 = 300; +pub const DEFAULT_AGENT_PARALLELISM: u32 = 1; + +fn default_agent_parallelism() -> u32 { + DEFAULT_AGENT_PARALLELISM +} diff --git a/desktop/src/features/agents/ui/CreateAgentDialog.tsx b/desktop/src/features/agents/ui/CreateAgentDialog.tsx index 763c04fb8c..1f6bd00a5e 100644 --- a/desktop/src/features/agents/ui/CreateAgentDialog.tsx +++ b/desktop/src/features/agents/ui/CreateAgentDialog.tsx @@ -53,6 +53,8 @@ export function CreateAgentDialog({ () => new Set(DEFAULT_MANAGED_AGENT_SCOPES), ); const [turnTimeoutSeconds, setTurnTimeoutSeconds] = React.useState("300"); + const [parallelism, setParallelism] = React.useState("1"); + const [systemPrompt, setSystemPrompt] = React.useState(""); const [selectedProviderId, setSelectedProviderId] = React.useState("custom"); const [hasSyncedProviderSelection, setHasSyncedProviderSelection] = @@ -133,6 +135,8 @@ export function CreateAgentDialog({ setAgentArgs("acp"); setMcpCommand("sprout-mcp-server"); setTurnTimeoutSeconds("300"); + setParallelism("1"); + setSystemPrompt(""); setSelectedProviderId("custom"); setHasSyncedProviderSelection(false); setShowAdvanced(false); @@ -202,6 +206,11 @@ export function CreateAgentDialog({ Number.parseInt(turnTimeoutSeconds, 10) > 0 ? Number.parseInt(turnTimeoutSeconds, 10) : undefined, + parallelism: + Number.parseInt(parallelism, 10) > 0 + ? Number.parseInt(parallelism, 10) + : undefined, + systemPrompt: systemPrompt.trim() || undefined, mintToken, tokenName: tokenName.trim() || undefined, tokenScopes: [...selectedScopes], @@ -280,7 +289,8 @@ export function CreateAgentDialog({ Advanced setup

- Relay overrides, raw commands, timeout, and doctor guidance. + Relay overrides, raw commands, timeout, parallelism, prompt + override, and doctor guidance.

@@ -298,14 +308,18 @@ export function CreateAgentDialog({ agentArgs={agentArgs} agentCommand={agentCommand} mcpCommand={mcpCommand} + onParallelismChange={setParallelism} onAcpCommandChange={setAcpCommand} onAgentArgsChange={setAgentArgs} onAgentCommandChange={setAgentCommand} onMcpCommandChange={setMcpCommand} onRelayUrlChange={setRelayUrl} + onSystemPromptChange={setSystemPrompt} onTurnTimeoutChange={setTurnTimeoutSeconds} + parallelism={parallelism} relayUrl={relayUrl} selectedProviderId={selectedProviderId} + systemPrompt={systemPrompt} turnTimeoutSeconds={turnTimeoutSeconds} /> diff --git a/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx b/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx index c787983877..3c17705695 100644 --- a/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx +++ b/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx @@ -6,6 +6,7 @@ import type { import { MANAGED_AGENT_SCOPE_OPTIONS } from "@/features/tokens/lib/scopeOptions"; import { cn } from "@/shared/lib/cn"; import { Input } from "@/shared/ui/input"; +import { Textarea } from "@/shared/ui/textarea"; import { describeResolvedCommand } from "./agentUi"; export function CreateAgentBasicsFields({ @@ -98,28 +99,36 @@ export function CreateAgentRuntimeFields({ agentArgs, agentCommand, mcpCommand, + parallelism, relayUrl, selectedProviderId, + systemPrompt, turnTimeoutSeconds, onAcpCommandChange, onAgentArgsChange, onAgentCommandChange, onMcpCommandChange, + onParallelismChange, onRelayUrlChange, + onSystemPromptChange, onTurnTimeoutChange, }: { acpCommand: string; agentArgs: string; agentCommand: string; mcpCommand: string; + parallelism: string; relayUrl: string; selectedProviderId: string; + systemPrompt: string; turnTimeoutSeconds: string; onAcpCommandChange: (value: string) => void; onAgentArgsChange: (value: string) => void; onAgentCommandChange: (value: string) => void; onMcpCommandChange: (value: string) => void; + onParallelismChange: (value: string) => void; onRelayUrlChange: (value: string) => void; + onSystemPromptChange: (value: string) => void; onTurnTimeoutChange: (value: string) => void; }) { return ( @@ -165,7 +174,7 @@ export function CreateAgentRuntimeFields({ ) : null} -
+
+ +
+ + onParallelismChange(event.target.value)} + placeholder="1" + step="1" + type="number" + value={parallelism} + /> +

+ Number of ACP worker subprocesses. `sprout-acp` allows 1-32. +

+
+
+ +
+ +