diff --git a/frontend/src-tauri/src/agent.rs b/frontend/src-tauri/src/agent.rs index 00ace89d..2d6c8144 100644 --- a/frontend/src-tauri/src/agent.rs +++ b/frontend/src-tauri/src/agent.rs @@ -59,7 +59,7 @@ const MAPLE_GOOSE_PERMISSION_CONFIG: &str = r#"user: never_allow: [] "#; const RUN_SHUTDOWN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); -const DEFAULT_AGENT_SESSION_TITLE: &str = "New agent session"; +const DEFAULT_AGENT_SESSION_TITLE: &str = "New task"; const DEFAULT_MCP_TIMEOUT_SECONDS: u64 = 300; const MAX_AGENT_SESSION_TITLE_CHARS: usize = 80; const MAX_AGENT_ERROR_CHARS: usize = 1_200; @@ -84,7 +84,7 @@ fn validate_session_model_lock( return Ok(()); } Err(format!( - "This Agent session is locked to model {persisted_model}. Start a new session to use {requested_model}." + "This task is locked to model {persisted_model}. Start a new task to use {requested_model}." )) } @@ -1026,7 +1026,7 @@ pub async fn agent_create_session( let session = session_manager .create_session(root.clone(), title, SessionType::User, permission_mode) .await - .map_err(|e| format!("Failed to create Goose session: {e}"))?; + .map_err(|e| format!("Failed to create Agent task: {e}"))?; permission_modes .lock() @@ -1109,7 +1109,7 @@ pub async fn agent_list_sessions( let mut sessions = session_manager .list_all_sessions() .await - .map_err(|e| format!("Failed to list Goose sessions: {e}"))? + .map_err(|e| format!("Failed to list Agent tasks: {e}"))? .into_iter() .filter(|session| { if let Some(root) = filter_root.as_ref() { @@ -1148,11 +1148,11 @@ pub async fn agent_load_session( let session = session_manager .get_session(&session_id, true) .await - .map_err(|e| format!("Failed to load Goose session: {e}"))?; + .map_err(|e| format!("Failed to load Agent task: {e}"))?; let conversation = session .conversation .as_ref() - .ok_or_else(|| "Goose session history was not loaded".to_string())?; + .ok_or_else(|| "Agent task history was not loaded".to_string())?; let timeline = conversation_to_timeline_items(conversation); let timeline = overlay_live_timeline(&state.live_timelines, &session_id, conversation, timeline).await; @@ -1188,7 +1188,7 @@ pub async fn agent_list_session_mcp_servers( let session = session_manager .get_session(session_id.trim(), false) .await - .map_err(|error| format!("Failed to load Goose session: {error}"))?; + .map_err(|error| format!("Failed to load Agent task: {error}"))?; let configured = normalize_mcp_servers( load_agent_config_inner(&app_handle, &user_id) .map_err(|error| format!("Failed to load MCP servers: {error}"))? @@ -1212,7 +1212,7 @@ pub async fn agent_set_session_mcp_server_enabled( let session_id = request.session_id.trim().to_string(); let requested_key = goose::config::extensions::name_to_key(request.name.trim()); if session_id.is_empty() { - return Err("Agent session ID cannot be empty".to_string()); + return Err("Agent task ID cannot be empty".to_string()); } if requested_key.is_empty() || requested_key == "developer" { return Err("That MCP server cannot be changed".to_string()); @@ -1240,7 +1240,7 @@ pub async fn agent_set_session_mcp_server_enabled( let session = session_manager .get_session(&session_id, false) .await - .map_err(|error| format!("Failed to load Goose session: {error}"))?; + .map_err(|error| format!("Failed to load Agent task: {error}"))?; let session_mcp_keys = session_mcp_extension_keys(&session); let manager_result = agent_manager .get_or_create_agent_with_runtime_context( @@ -1300,13 +1300,13 @@ pub async fn agent_set_session_mcp_server_enabled( agent .persist_extension_state(&session_id) .await - .map_err(|error| format!("Failed to save MCP session state: {error}"))?; + .map_err(|error| format!("Failed to save task MCP settings: {error}"))?; } let refreshed = session_manager .get_session(&session_id, false) .await - .map_err(|error| format!("Failed to reload Goose session: {error}"))?; + .map_err(|error| format!("Failed to reload Agent task: {error}"))?; Ok(session_mcp_servers(&configured, &refreshed)) } @@ -1323,7 +1323,7 @@ pub async fn agent_delete_session( ensure_account_generation(&state, &account_scope, generation).await?; let session_id = session_id.trim().to_string(); if session_id.is_empty() { - return Err("Agent session ID cannot be empty".to_string()); + return Err("Agent task ID cannot be empty".to_string()); } let _session_lifecycle_guard = state.session_lifecycle.lock().await; @@ -1333,7 +1333,7 @@ pub async fn agent_delete_session( Some(current) => { ensure_runtime_account(current, &account_scope)?; if has_active_session_run(¤t.active_runs, &session_id) { - return Err("Stop the running agent before deleting this chat".to_string()); + return Err("Stop the running agent before deleting this task".to_string()); } ( Some(Arc::clone(¤t.agent_manager)), @@ -1375,11 +1375,11 @@ async fn delete_persisted_agent_session( session_manager .get_session(session_id, false) .await - .map_err(|e| format!("Failed to find Goose session {session_id}: {e}"))?; + .map_err(|e| format!("Failed to find Agent task {session_id}: {e}"))?; session_manager .delete_session(session_id) .await - .map_err(|e| format!("Failed to delete Goose session {session_id}: {e}"))?; + .map_err(|e| format!("Failed to delete Agent task {session_id}: {e}"))?; live_timelines.lock().await.remove(session_id); pending_permissions @@ -1411,7 +1411,7 @@ async fn rollback_cancelled_agent_turn( let session = session_manager .get_session(session_id, false) .await - .map_err(|error| format!("Failed to inspect cancelled Agent session: {error}"))?; + .map_err(|error| format!("Failed to inspect cancelled Agent task: {error}"))?; if !session.user_set_name { session_manager .update(session_id) @@ -1419,7 +1419,7 @@ async fn rollback_cancelled_agent_turn( .apply() .await .map_err(|error| { - format!("Failed to restore cancelled Agent session title: {error}") + format!("Failed to restore cancelled Agent task title: {error}") })?; } } @@ -1492,7 +1492,7 @@ pub async fn agent_send_message( agent_manager .try_register_cancel_token(&request.session_id, cancel_token.clone()) .await - .map_err(|e| format!("Agent session is already running: {e}"))?; + .map_err(|e| format!("Agent task is already running: {e}"))?; // A rejected or delayed send must not be able to change a live policy that // the mode command already made authoritative. Seed only sessions that do @@ -1510,7 +1510,7 @@ pub async fn agent_send_message( let mut session = session_manager .get_session(&request.session_id, true) .await - .map_err(|e| format!("Failed to load Goose session: {e}"))?; + .map_err(|e| format!("Failed to load Agent task: {e}"))?; validate_session_model_lock( session.message_count, session @@ -1534,11 +1534,11 @@ pub async fn agent_send_message( .system_generated_name(prompt_title) .apply() .await - .map_err(|e| format!("Failed to name Agent session: {e}"))?; + .map_err(|e| format!("Failed to name Agent task: {e}"))?; session = session_manager .get_session(&session.id, false) .await - .map_err(|e| format!("Failed to load named Goose session: {e}"))?; + .map_err(|e| format!("Failed to load named Agent task: {e}"))?; emit_agent_event( &app_handle, AgentEventEnvelope { @@ -1820,7 +1820,7 @@ pub async fn agent_set_permission_mode( let session_id = request.session_id.trim().to_string(); if session_id.is_empty() { - return Err("Agent permission mode update requires a session ID".to_string()); + return Err("Agent permission mode update requires a task ID".to_string()); } let goose_mode = parse_user_permission_mode(&request.mode)?; let (agent_manager, session_manager, permission_modes) = { @@ -1986,12 +1986,12 @@ pub async fn agent_permission_respond( ensure_runtime_account(current, &account_scope)?; let session_id = response.session_id.trim().to_string(); if session_id.is_empty() { - return Err("Agent permission response requires a session ID".to_string()); + return Err("Agent permission response requires a task ID".to_string()); } let key = (session_id.clone(), response.request_id.clone()); if !state.pending_permissions.lock().await.contains_key(&key) { return Err(format!( - "No pending Agent Mode permission request found for {} in session {}", + "No pending Agent Mode permission request found for {} in task {}", response.request_id, session_id )); } @@ -2325,7 +2325,7 @@ async fn run_agent_prompt(run: AgentPromptRun) -> Result { const activeSession = sessions.find((session) => session.id === activeSessionId); - return activeSession ? sessionTitle(activeSession) : "Agent session"; + return activeSession ? sessionTitle(activeSession) : "New task"; }, [activeSessionId, sessions]); const activeRunId = activeSessionId ? (activeRunsBySession[activeSessionId] ?? null) : null; const activePendingSendKey = activeSessionId || NEW_SESSION_PENDING_KEY; @@ -1234,7 +1234,7 @@ export function AgentMode({ userId }: { userId: string }) { if (!sessionId) { const detail = await agentRuntimeService.createSession(userId, { projectRoot, - title: "New agent session", + title: "New task", model: model || DEFAULT_MODEL, mode: selectedModeRef.current, mcpServerNames: selectedNewChatMcpServerNames @@ -1294,7 +1294,7 @@ export function AgentMode({ userId }: { userId: string }) { } return await agentRuntimeService.createSession(userId, { projectRoot, - title: "New agent session", + title: "New task", model: model || DEFAULT_MODEL, mode: selectedModeRef.current, mcpServerNames: selectedNewChatMcpServerNames @@ -1364,7 +1364,7 @@ export function AgentMode({ userId }: { userId: string }) { return { detail, timelineRevision }; } } - throw new Error("This Agent session is still updating. Try selecting it again shortly."); + throw new Error("This task is still updating. Try selecting it again shortly."); }); const { detail, timelineRevision } = loaded; if ( @@ -1381,7 +1381,7 @@ export function AgentMode({ userId }: { userId: string }) { // that case leave the previous chat intact instead of overwriting the // newer timeline with a stale snapshot. if (!replaceSessionTimeline(detail.session.id, detail.timeline, timelineRevision)) { - throw new Error("This Agent session changed while loading. Try selecting it again."); + throw new Error("This task changed while loading. Try selecting it again."); } // Commit the selected session and all of its settings together. Until @@ -1604,7 +1604,7 @@ export function AgentMode({ userId }: { userId: string }) { async (item: AgentTimelineItem, decision: AgentPermissionDecision) => { const sessionId = activeSessionIdRef.current; try { - if (!sessionId) throw new Error("No active Agent session for this permission request"); + if (!sessionId) throw new Error("No active task for this permission request"); await agentRuntimeService.respondToPermission( userId, sessionId, @@ -1892,6 +1892,7 @@ export function AgentMode({ userId }: { userId: string }) { onSessionSelect={(sessionId) => void loadSession(sessionId)} /> } + onNewItem={areAgentSettingsLocked ? undefined : () => void createSession()} onToggle={toggleSidebar} /> @@ -1902,7 +1903,8 @@ export function AgentMode({ userId }: { userId: string }) { if (!open) setSessionToDelete(null); }} chatTitle={sessionTitle(sessionToDelete)} - description={`This will delete "${sessionTitle(sessionToDelete)}" from Agent Mode. This action cannot be undone.`} + itemLabel="task" + description={`This will permanently delete the task "${sessionTitle(sessionToDelete)}". This action cannot be undone.`} onConfirm={() => void deleteSession(sessionToDelete.id)} /> ) : null} @@ -1931,6 +1933,7 @@ export function AgentMode({ userId }: { userId: string }) { title={activeSessionTitle} isSidebarOpen={isSidebarOpen} onNewChat={() => void createSession()} + newItemLabel="New Task" /> ) : null} @@ -1944,7 +1947,7 @@ export function AgentMode({ userId }: { userId: string }) {

Maple cannot verify that this existing Local OpenAI Proxy key belongs to the signed-in account. This can happen once after upgrading from an older Agent Mode - build. Your chats remain available; replace the saved local setup before sending + build. Your tasks remain available; replace the saved local setup before sending another message. The existing backend key will remain in API Management.

@@ -2629,9 +2632,7 @@ function AgentSidebarContent({ size="icon" className="h-7 w-7 shrink-0 text-muted-foreground hover:text-foreground" onClick={() => toggleProjectCollapsed(root.path)} - aria-label={ - isCollapsed ? "Expand project sessions" : "Collapse project sessions" - } + aria-label={isCollapsed ? "Expand project tasks" : "Collapse project tasks"} > {isCollapsed ? ( @@ -2648,7 +2649,7 @@ function AgentSidebarContent({ className="h-7 w-7 shrink-0 text-muted-foreground hover:text-foreground" onClick={onCreateSession} disabled={disabled || !projectRoot} - aria-label="New agent session" + aria-label="New Task" > @@ -2659,7 +2660,7 @@ function AgentSidebarContent({
{projectSessions.length === 0 ? ( isActive ? ( -

No sessions yet

+

No tasks yet

) : null ) : ( projectSessions.map((session) => { @@ -2726,7 +2727,7 @@ function AgentSidebarContent({ event.preventDefault(); event.stopPropagation(); }} - aria-label={`Open chat menu for ${title}`} + aria-label={`Open task menu for ${title}`} > - {isRunning ? "Stop Agent Before Deleting" : "Delete Chat"} + {isRunning + ? "Stop Agent Before Deleting Task" + : "Delete Task"} @@ -2788,10 +2791,10 @@ function AgentSidebarContent({

- Chats + Tasks

- Folderless agent chats are not available yet. + Folderless Agent tasks are not available yet.

@@ -3751,7 +3754,7 @@ function permissionRequestId(item: AgentTimelineItem): string { } function sessionTitle(session: AgentSessionSummary): string { - return session.title || "Agent session"; + return session.title || "New task"; } function toolTitle(item: AgentTimelineItem): string { diff --git a/frontend/src/components/DeleteChatDialog.tsx b/frontend/src/components/DeleteChatDialog.tsx index b7a71f70..d1bfca61 100644 --- a/frontend/src/components/DeleteChatDialog.tsx +++ b/frontend/src/components/DeleteChatDialog.tsx @@ -15,6 +15,7 @@ interface DeleteChatDialogProps { onConfirm: () => void; chatTitle: string; description?: React.ReactNode; + itemLabel?: "chat" | "task"; } export function DeleteChatDialog({ @@ -22,7 +23,8 @@ export function DeleteChatDialog({ onOpenChange, onConfirm, chatTitle, - description + description, + itemLabel = "chat" }: DeleteChatDialogProps) { const handleConfirm = (e: React.MouseEvent) => { e.preventDefault(); @@ -34,7 +36,7 @@ export function DeleteChatDialog({ - Are you sure you want to delete this chat? + Are you sure you want to delete this {itemLabel}? {description ?? ( <>This will permanently delete "{chatTitle}". This action cannot be undone. diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 722032c3..b8a1bb92 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -44,12 +44,14 @@ export function Sidebar({ isOpen, mode = "chat", navigationContent, + onNewItem, onToggle }: { chatId?: string; isOpen: boolean; mode?: WorkspaceMode; navigationContent?: ReactNode; + onNewItem?: () => void; onToggle: () => void; }) { const router = useRouter(); @@ -226,6 +228,18 @@ export function Sidebar({ const showAgentMode = agentModeAvailable && billingStatus !== null && agentModeEnabled === true; const isAgentMode = mode === "agent"; + async function addItem() { + if (!isAgentMode) { + await addChat(); + return; + } + + if (isOpen && isCompactLayout) { + onToggle(); + } + onNewItem?.(); + } + useEffect(() => { if (!agentModeAvailable || !userId) return; @@ -349,11 +363,12 @@ export function Sidebar({
{!isAgentMode && ( diff --git a/frontend/src/components/settings/DeleteAccountSettings.tsx b/frontend/src/components/settings/DeleteAccountSettings.tsx index 498dad84..96ee2aaf 100644 --- a/frontend/src/components/settings/DeleteAccountSettings.tsx +++ b/frontend/src/components/settings/DeleteAccountSettings.tsx @@ -129,8 +129,8 @@ export function DeleteAccountSettings() { - This action cannot be undone. You will lose access to paid features, chat history, and - settings. + This action cannot be undone. You will lose access to paid features, chat and task + history, and settings. diff --git a/frontend/src/components/settings/HistorySettings.tsx b/frontend/src/components/settings/HistorySettings.tsx index 21e41529..1b8f75e0 100644 --- a/frontend/src/components/settings/HistorySettings.tsx +++ b/frontend/src/components/settings/HistorySettings.tsx @@ -51,7 +51,7 @@ export function HistorySettings() { window.dispatchEvent(new CustomEvent("newchat", { detail: { projectId: null } })); } catch (deleteError) { console.error("Error deleting chat history:", deleteError); - setError("Maple could not delete all chat history. Please try again."); + setError("Maple could not delete all chat and task history. Please try again."); } finally { operationBlock?.release(); setIsDeleting(false); @@ -60,19 +60,21 @@ export function HistorySettings() { return (
- {error && } + {error && ( + + )} {isConfirming ? (
-

Delete your entire chat history?

+

Delete your entire chat and task history?

This action cannot be undone.