diff --git a/desktop/src/components/chat/MessageList.test.tsx b/desktop/src/components/chat/MessageList.test.tsx
index e6b16210..3db66ebd 100644
--- a/desktop/src/components/chat/MessageList.test.tsx
+++ b/desktop/src/components/chat/MessageList.test.tsx
@@ -251,6 +251,36 @@ describe('MessageList nested tool calls', () => {
expect(card.textContent).toContain('45s')
})
+ it('renders stopped background agents as neutral transcript events', () => {
+ useChatStore.setState({
+ sessions: {
+ [ACTIVE_TAB]: makeSessionState({
+ messages: [{
+ id: 'background-task-agent-stopped',
+ type: 'background_task',
+ timestamp: 2,
+ task: {
+ taskId: 'agent-task-stopped',
+ toolUseId: 'agent-tool-stopped',
+ status: 'stopped',
+ taskType: 'local_agent',
+ summary: 'Agent "Code review for todo app" was stopped',
+ startedAt: 1,
+ updatedAt: 2,
+ },
+ }],
+ }),
+ },
+ })
+
+ render()
+
+ const card = screen.getByTestId('background-task-event-card')
+ expect(card.getAttribute('data-status')).toBe('stopped')
+ expect(card.textContent).toContain('stopped')
+ expect(card.querySelector('.text-\\[var\\(--color-error\\)\\]')).toBeNull()
+ })
+
it('restores the full transcript when scrolling away from latest', async () => {
useChatStore.setState({
sessions: {
diff --git a/desktop/src/components/chat/MessageList.tsx b/desktop/src/components/chat/MessageList.tsx
index 35d3335c..20b7e1b1 100644
--- a/desktop/src/components/chat/MessageList.tsx
+++ b/desktop/src/components/chat/MessageList.tsx
@@ -1,5 +1,5 @@
import { useRef, useEffect, useMemo, memo, useState, useCallback, useLayoutEffect, type ReactNode } from 'react'
-import { ArrowDown, BookMarked, Bot, CheckCircle2, ChevronDown, ChevronRight, LoaderCircle, Settings, Target, XCircle } from 'lucide-react'
+import { ArrowDown, BookMarked, Bot, CheckCircle2, ChevronDown, ChevronRight, CircleStop, LoaderCircle, Settings, Target, XCircle } from 'lucide-react'
import { ApiError } from '../../api/client'
import { sessionsApi, type SessionTurnCheckpoint } from '../../api/sessions'
import { useChatStore } from '../../stores/chatStore'
@@ -233,7 +233,8 @@ function BackgroundTaskEventCard({ message }: { message: BackgroundTaskEvent })
const t = useTranslation()
const { task } = message
const isRunning = task.status === 'running'
- const isFailed = task.status === 'failed' || task.status === 'stopped'
+ const isFailed = task.status === 'failed'
+ const isStopped = task.status === 'stopped'
const duration = formatBackgroundTaskDuration(task.usage?.durationMs)
const detail = task.summary || task.lastToolName || task.description || task.outputFile || task.taskId
@@ -241,6 +242,7 @@ function BackgroundTaskEventCard({ message }: { message: BackgroundTaskEvent })
@@ -248,6 +250,8 @@ function BackgroundTaskEventCard({ message }: { message: BackgroundTaskEvent })
) : isFailed ? (
+ ) : isStopped ? (
+
) : (
)}
diff --git a/src/tools/AgentTool/AgentTool.tsx b/src/tools/AgentTool/AgentTool.tsx
index 74d4a1dd..54827895 100644
--- a/src/tools/AgentTool/AgentTool.tsx
+++ b/src/tools/AgentTool/AgentTool.tsx
@@ -1322,7 +1322,8 @@ The agent is now running and will receive instructions via mailbox.`
}
if (data.status === 'async_launched') {
const prefix = `Async agent launched successfully.\nagentId: ${data.agentId} (internal ID - do not mention to user. Use SendMessage with to: '${data.agentId}' to continue this agent.)\nThe agent is working in the background. You will be notified automatically when it completes.`;
- const instructions = data.canReadOutputFile ? `Do not duplicate this agent's work — avoid working with the same files or topics it is using. Work on non-overlapping tasks, or briefly tell the user what you launched and end your response.\noutput_file: ${data.outputFile}\nIf asked, you can check progress before completion by using ${FILE_READ_TOOL_NAME} or ${BASH_TOOL_NAME} tail on the output file.` : `Briefly tell the user what you launched and end your response. Do not generate any other text — agent results will arrive in a subsequent message.`;
+ const stopGuidance = `Do not stop this agent just because you have enough partial output. Stop it only if the user asks to cancel it, or if it is clearly runaway, harmful, duplicative, or no longer useful.`;
+ const instructions = data.canReadOutputFile ? `Do not duplicate this agent's work — avoid working with the same files or topics it is using. Work on non-overlapping tasks, or briefly tell the user what you launched and end your response.\n${stopGuidance}\noutput_file: ${data.outputFile}\nIf asked, you can check progress before completion by using ${FILE_READ_TOOL_NAME} or ${BASH_TOOL_NAME} tail on the output file.` : `Briefly tell the user what you launched and end your response. Do not generate any other text — agent results will arrive in a subsequent message.\n${stopGuidance}`;
const text = `${prefix}\n${instructions}`;
return {
tool_use_id: toolUseID,
diff --git a/src/tools/TaskStopTool/prompt.ts b/src/tools/TaskStopTool/prompt.ts
index cfa14e07..2926a234 100644
--- a/src/tools/TaskStopTool/prompt.ts
+++ b/src/tools/TaskStopTool/prompt.ts
@@ -4,5 +4,9 @@ export const DESCRIPTION = `
- Stops a running background task by its ID
- Takes a task_id parameter identifying the task to stop
- Returns a success or failure status
-- Use this tool when you need to terminate a long-running task
+- Use this tool only when the user explicitly asks to cancel/stop a task, or when
+ the task is clearly runaway, harmful, duplicative, or no longer useful.
+- Do not stop a background agent merely because you have already read enough of
+ its output. Prefer waiting for the automatic completion notification or
+ leaving it running while you summarize available progress.
`