import { c as _c } from "react-compiler-runtime"; import React from 'react'; import { z } from 'zod/v4'; import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js'; import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage.js'; import { MessageResponse } from '../../components/MessageResponse.js'; import { Box, Text } from '../../ink.js'; import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'; import type { TaskType } from '../../Task.js'; import type { Tool } from '../../Tool.js'; import { buildTool, type ToolDef } from '../../Tool.js'; import type { LocalAgentTaskState } from '../../tasks/LocalAgentTask/LocalAgentTask.js'; import type { LocalShellTaskState } from '../../tasks/LocalShellTask/guards.js'; import type { RemoteAgentTaskState } from '../../tasks/RemoteAgentTask/RemoteAgentTask.js'; import type { TaskState } from '../../tasks/types.js'; import { AbortError } from '../../utils/errors.js'; import { lazySchema } from '../../utils/lazySchema.js'; import { extractTextContent } from '../../utils/messages.js'; import { semanticBoolean } from '../../utils/semanticBoolean.js'; import { sleep } from '../../utils/sleep.js'; import { jsonParse } from '../../utils/slowOperations.js'; import { countCharInString } from '../../utils/stringUtils.js'; import { getTaskOutput } from '../../utils/task/diskOutput.js'; import { updateTaskState } from '../../utils/task/framework.js'; import { formatTaskOutput } from '../../utils/task/outputFormatting.js'; import type { ThemeName } from '../../utils/theme.js'; import { AgentPromptDisplay, AgentResponseDisplay } from '../AgentTool/UI.js'; import BashToolResultMessage from '../BashTool/BashToolResultMessage.js'; import { TASK_OUTPUT_TOOL_NAME } from './constants.js'; const inputSchema = lazySchema(() => z.strictObject({ task_id: z.string().describe('The task ID to get output from'), block: semanticBoolean(z.boolean().default(true)).describe('Whether to wait for completion'), timeout: z.number().min(0).max(600000).default(30000).describe('Max wait time in ms') })); type InputSchema = ReturnType; type TaskOutputToolInput = z.infer; // Unified output type covering all task types type TaskOutput = { task_id: string; task_type: TaskType; status: string; description: string; output: string; exitCode?: number | null; error?: string; // For agents prompt?: string; result?: string; }; type TaskOutputToolOutput = { retrieval_status: 'success' | 'timeout' | 'not_ready'; task: TaskOutput | null; }; // Re-export Progress from centralized types to break import cycles export type { TaskOutputProgress as Progress } from '../../types/tools.js'; // Get output for any task type async function getTaskOutputData(task: TaskState): Promise { let output: string; if (task.type === 'local_bash') { const bashTask = task as LocalShellTaskState; const taskOutputObj = bashTask.shellCommand?.taskOutput; if (taskOutputObj) { const stdout = await taskOutputObj.getStdout(); const stderr = taskOutputObj.getStderr(); output = [stdout, stderr].filter(Boolean).join('\n'); } else { output = await getTaskOutput(task.id); } } else { output = await getTaskOutput(task.id); } const baseOutput: TaskOutput = { task_id: task.id, task_type: task.type, status: task.status, description: task.description, output }; // Add type-specific fields if (task.type === 'local_bash') { const bashTask = task as LocalShellTaskState; return { ...baseOutput, exitCode: bashTask.result?.code ?? null }; } if (task.type === 'local_agent') { const agentTask = task as LocalAgentTaskState; // Prefer the clean final answer from the in-memory result over the raw // JSONL transcript on disk. The disk output is a symlink to the full // session transcript (every message, tool use, etc.), not just the // subagent's answer. The in-memory result contains only the final // assistant text content blocks. const cleanResult = agentTask.result ? extractTextContent(agentTask.result.content, '\n') : undefined; return { ...baseOutput, prompt: agentTask.prompt, result: cleanResult || output, output: cleanResult || output, error: agentTask.error }; } if (task.type === 'remote_agent') { const remoteTask = task as RemoteAgentTaskState; return { ...baseOutput, prompt: remoteTask.command }; } return baseOutput; } // Wait for task to complete async function waitForTaskCompletion(taskId: string, getAppState: () => { tasks?: Record; }, timeoutMs: number, abortController?: AbortController): Promise { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { // Check abort signal if (abortController?.signal.aborted) { throw new AbortError(); } const state = getAppState(); const task = state.tasks?.[taskId] as TaskState | undefined; if (!task) { return null; } if (task.status !== 'running' && task.status !== 'pending') { return task; } // Wait before polling again await sleep(100); } // Timeout - return current state const finalState = getAppState(); return finalState.tasks?.[taskId] as TaskState ?? null; } export const TaskOutputTool: Tool = buildTool({ name: TASK_OUTPUT_TOOL_NAME, searchHint: 'read output/logs from a background task', maxResultSizeChars: 100_000, shouldDefer: true, // Backwards-compatible aliases for renamed tools aliases: ['AgentOutputTool', 'BashOutputTool'], userFacingName() { return 'Task Output'; }, get inputSchema(): InputSchema { return inputSchema(); }, async description() { return '[Deprecated] — prefer Read on the task output file path'; }, isConcurrencySafe(_input) { return this.isReadOnly?.(_input) ?? false; }, isEnabled() { return "external" !== 'ant'; }, isReadOnly(_input) { return true; }, toAutoClassifierInput(input) { return input.task_id; }, async prompt() { return `DEPRECATED: Prefer using the Read tool on the task's output file path instead. Background tasks return their output file path in the tool result, and you receive a with the same path when the task completes — Read that file directly. - Retrieves output from a running or completed task (background shell, agent, or remote session) - Takes a task_id parameter identifying the task - Returns the task output along with status information - Use block=true (default) to wait for task completion - Use block=false for non-blocking check of current status - Task IDs can be found using the /tasks command - Works with all task types: background shells, async agents, and remote sessions`; }, async validateInput({ task_id }, { getAppState }) { if (!task_id) { return { result: false, message: 'Task ID is required', errorCode: 1 }; } const appState = getAppState(); const task = appState.tasks?.[task_id] as TaskState | undefined; if (!task) { return { result: false, message: `No task found with ID: ${task_id}`, errorCode: 2 }; } return { result: true }; }, async call(input: TaskOutputToolInput, toolUseContext, _canUseTool, _parentMessage, onProgress) { const { task_id, block, timeout } = input; const appState = toolUseContext.getAppState(); const task = appState.tasks?.[task_id] as TaskState | undefined; if (!task) { throw new Error(`No task found with ID: ${task_id}`); } if (!block) { // Non-blocking: return current state if (task.status !== 'running' && task.status !== 'pending') { // Mark as notified updateTaskState(task_id, toolUseContext.setAppState, t => ({ ...t, notified: true })); return { data: { retrieval_status: 'success' as const, task: await getTaskOutputData(task) } }; } return { data: { retrieval_status: 'not_ready' as const, task: await getTaskOutputData(task) } }; } // Blocking: wait for completion if (onProgress) { onProgress({ toolUseID: `task-output-waiting-${Date.now()}`, data: { type: 'waiting_for_task', taskDescription: task.description, taskType: task.type } }); } const completedTask = await waitForTaskCompletion(task_id, toolUseContext.getAppState, timeout, toolUseContext.abortController); if (!completedTask) { return { data: { retrieval_status: 'timeout' as const, task: null } }; } if (completedTask.status === 'running' || completedTask.status === 'pending') { return { data: { retrieval_status: 'timeout' as const, task: await getTaskOutputData(completedTask) } }; } // Mark as notified updateTaskState(task_id, toolUseContext.setAppState, t => ({ ...t, notified: true })); return { data: { retrieval_status: 'success' as const, task: await getTaskOutputData(completedTask) } }; }, mapToolResultToToolResultBlockParam(data, toolUseID) { const parts: string[] = []; parts.push(`${data.retrieval_status}`); if (data.task) { parts.push(`${data.task.task_id}`); parts.push(`${data.task.task_type}`); parts.push(`${data.task.status}`); if (data.task.exitCode !== undefined && data.task.exitCode !== null) { parts.push(`${data.task.exitCode}`); } if (data.task.output?.trim()) { const { content } = formatTaskOutput(data.task.output, data.task.task_id); parts.push(`\n${content.trimEnd()}\n`); } if (data.task.error) { parts.push(`${data.task.error}`); } } return { tool_use_id: toolUseID, type: 'tool_result' as const, content: parts.join('\n\n') }; }, renderToolUseMessage(input) { const { block = true } = input; if (!block) { return 'non-blocking'; } return ''; }, renderToolUseTag(input) { if (!input.task_id) { return null; } return {input.task_id}; }, renderToolUseProgressMessage(progressMessages) { const lastProgress = progressMessages[progressMessages.length - 1]; const progressData = lastProgress?.data as { taskDescription?: string; taskType?: string; } | undefined; return {progressData?.taskDescription &&   {progressData.taskDescription}}      Waiting for task{' '} (esc to give additional instructions) ; }, renderToolResultMessage(content, _, { verbose, theme }) { return ; }, renderToolUseRejectedMessage() { return ; }, renderToolUseErrorMessage(result, { verbose }) { return ; } } satisfies ToolDef); function TaskOutputResultDisplay(t0) { const $ = _c(54); const { content, verbose: t1, theme } = t0; const verbose = t1 === undefined ? false : t1; const expandShortcut = useShortcutDisplay("app:toggleTranscript", "Global", "ctrl+o"); let t2; if ($[0] !== content) { t2 = typeof content === "string" ? jsonParse(content) : content; $[0] = content; $[1] = t2; } else { t2 = $[1]; } const result = t2; if (!result.task) { let t3; if ($[2] === Symbol.for("react.memo_cache_sentinel")) { t3 = No task output available; $[2] = t3; } else { t3 = $[2]; } return t3; } const { task } = result; if (task.task_type === "local_bash") { let t3; if ($[3] !== task.error || $[4] !== task.output) { t3 = { stdout: task.output, stderr: "", isImage: false, dangerouslyDisableSandbox: true, returnCodeInterpretation: task.error }; $[3] = task.error; $[4] = task.output; $[5] = t3; } else { t3 = $[5]; } const bashOut = t3; let t4; if ($[6] !== bashOut || $[7] !== verbose) { t4 = ; $[6] = bashOut; $[7] = verbose; $[8] = t4; } else { t4 = $[8]; } return t4; } if (task.task_type === "local_agent") { const lineCount = task.result ? countCharInString(task.result, "\n") + 1 : 0; if (result.retrieval_status === "success") { if (verbose) { let t3; if ($[9] !== lineCount || $[10] !== task.description) { t3 = {task.description} ({lineCount} lines); $[9] = lineCount; $[10] = task.description; $[11] = t3; } else { t3 = $[11]; } let t4; if ($[12] !== task.prompt || $[13] !== theme) { t4 = task.prompt && ; $[12] = task.prompt; $[13] = theme; $[14] = t4; } else { t4 = $[14]; } let t5; if ($[15] !== task.result || $[16] !== theme) { t5 = task.result && ; $[15] = task.result; $[16] = theme; $[17] = t5; } else { t5 = $[17]; } let t6; if ($[18] !== task.error) { t6 = task.error && Error:{task.error}; $[18] = task.error; $[19] = t6; } else { t6 = $[19]; } let t7; if ($[20] !== t4 || $[21] !== t5 || $[22] !== t6) { t7 = {t4}{t5}{t6}; $[20] = t4; $[21] = t5; $[22] = t6; $[23] = t7; } else { t7 = $[23]; } let t8; if ($[24] !== t3 || $[25] !== t7) { t8 = {t3}{t7}; $[24] = t3; $[25] = t7; $[26] = t8; } else { t8 = $[26]; } return t8; } let t3; if ($[27] !== expandShortcut) { t3 = Read output ({expandShortcut} to expand); $[27] = expandShortcut; $[28] = t3; } else { t3 = $[28]; } return t3; } if (result.retrieval_status === "timeout" || task.status === "running") { let t3; if ($[29] === Symbol.for("react.memo_cache_sentinel")) { t3 = Task is still running…; $[29] = t3; } else { t3 = $[29]; } return t3; } if (result.retrieval_status === "not_ready") { let t3; if ($[30] === Symbol.for("react.memo_cache_sentinel")) { t3 = Task is still running…; $[30] = t3; } else { t3 = $[30]; } return t3; } let t3; if ($[31] === Symbol.for("react.memo_cache_sentinel")) { t3 = Task not ready; $[31] = t3; } else { t3 = $[31]; } return t3; } if (task.task_type === "remote_agent") { let t3; if ($[32] !== task.description || $[33] !== task.status) { t3 =   {task.description} [{task.status}]; $[32] = task.description; $[33] = task.status; $[34] = t3; } else { t3 = $[34]; } let t4; if ($[35] !== task.output || $[36] !== verbose) { t4 = task.output && verbose && {task.output}; $[35] = task.output; $[36] = verbose; $[37] = t4; } else { t4 = $[37]; } let t5; if ($[38] !== expandShortcut || $[39] !== task.output || $[40] !== verbose) { t5 = !verbose && task.output && {" "}({expandShortcut} to expand); $[38] = expandShortcut; $[39] = task.output; $[40] = verbose; $[41] = t5; } else { t5 = $[41]; } let t6; if ($[42] !== t3 || $[43] !== t4 || $[44] !== t5) { t6 = {t3}{t4}{t5}; $[42] = t3; $[43] = t4; $[44] = t5; $[45] = t6; } else { t6 = $[45]; } return t6; } let t3; if ($[46] !== task.description || $[47] !== task.status) { t3 =   {task.description} [{task.status}]; $[46] = task.description; $[47] = task.status; $[48] = t3; } else { t3 = $[48]; } let t4; if ($[49] !== task.output) { t4 = task.output && {task.output.slice(0, 500)}; $[49] = task.output; $[50] = t4; } else { t4 = $[50]; } let t5; if ($[51] !== t3 || $[52] !== t4) { t5 = {t3}{t4}; $[51] = t3; $[52] = t4; $[53] = t5; } else { t5 = $[53]; } return t5; } export default TaskOutputTool;