diff --git a/desktop/src/features/chats/ui/ChatConversationRows.tsx b/desktop/src/features/chats/ui/ChatConversationRows.tsx
index 63335f711..3bae8289b 100644
--- a/desktop/src/features/chats/ui/ChatConversationRows.tsx
+++ b/desktop/src/features/chats/ui/ChatConversationRows.tsx
@@ -232,11 +232,14 @@ export function AgentActivationCard({
- Activate {agentName} to get a response
+ {isActivating
+ ? `Starting ${agentName}…`
+ : `Activate ${agentName} to get a response`}
- Your message was sent, but this agent is not active in this
- chat yet.
+ {isActivating
+ ? "It will pick up your message as soon as it connects."
+ : "Your message was sent, but this agent is not active in this chat yet."}
diff --git a/desktop/src/features/chats/ui/ChatDetail.tsx b/desktop/src/features/chats/ui/ChatDetail.tsx
index 0fcf8c4f5..d1c8c7589 100644
--- a/desktop/src/features/chats/ui/ChatDetail.tsx
+++ b/desktop/src/features/chats/ui/ChatDetail.tsx
@@ -470,6 +470,30 @@ export function ChatDetail({
// message, and start its turn. Without this grace window the card re-shows
// ~1s after activation and reads as "activation didn't work".
const AGENT_ACTIVATION_GRACE_MS = 20_000;
+ // "Activated" isn't done until the agent responds: the card's button holds
+ // its loading state from click until this chat has a live turn (the card
+ // unmounts then) or the give-up window expires — the start API returning
+ // only means the process launched, not that it's connected and replaying
+ // the pending message.
+ const ACTIVATION_PENDING_MS = 60_000;
+ const [isActivationPending, setIsActivationPending] = React.useState(false);
+ const handleActivateAgent = React.useCallback(() => {
+ setIsActivationPending(true);
+ onActivateAgent();
+ }, [onActivateAgent]);
+ React.useEffect(() => {
+ if (!isActivationPending) {
+ return;
+ }
+ if (isChatTurnActive) {
+ setIsActivationPending(false);
+ return;
+ }
+ const timeout = window.setTimeout(() => {
+ setIsActivationPending(false);
+ }, ACTIVATION_PENDING_MS);
+ return () => window.clearTimeout(timeout);
+ }, [isActivationPending, isChatTurnActive]);
const [activationGraceUntil, setActivationGraceUntil] = React.useState(0);
const wasActivatingRef = React.useRef(false);
React.useEffect(() => {
@@ -620,8 +644,10 @@ export function ChatDetail({
>
) : null}
diff --git a/desktop/src/features/chats/ui/ChatsScreen.tsx b/desktop/src/features/chats/ui/ChatsScreen.tsx
index 855cefdcd..b2af641d5 100644
--- a/desktop/src/features/chats/ui/ChatsScreen.tsx
+++ b/desktop/src/features/chats/ui/ChatsScreen.tsx
@@ -516,10 +516,10 @@ export function ChatsScreen({
defaultAgent.status !== "running" &&
defaultAgent.status !== "deployed"
) {
- const updatedAgent = await startManagedAgentMutation.mutateAsync(
- defaultAgent.pubkey,
- );
- toast.success(`${updatedAgent.name || defaultAgent.name} activated`);
+ // No success toast: the process starting is not the same as the
+ // agent responding — the activation card holds its loading state
+ // until the agent's turn actually begins.
+ await startManagedAgentMutation.mutateAsync(defaultAgent.pubkey);
} else {
await managedAgentsQuery.refetch();
toast.success(`${defaultAgent.name} activated`);