mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-11-09 19:02:05 +00:00
fix: list a subagent's background Bash commands (#1540)
Claude Code sends no `tool_use_result` with a subagent's tool results. The adapter recognizes a background Bash command only from the `backgroundTaskId` in that structured result, so it never recognized one that a subagent started. The command's terminal was reported exited (code 0) as soon as the result came, while the command kept running, and the command was missing from the subagent's `state_update.background`. The real SDK stream (CLI 2.1.318) for a subagent running `sleep 30` in the background: ``` task_started task_id=beure6fer task_type=local_bash tool_use_id=toolu_018M… owned_by_subagent=true is_backgrounded=true user parent_tool_use_id=toolu_01HY… tool_result for toolu_018M…, no tool_use_result ``` The local transcripts agree: none of the 35 subagent background Bash results across CLI 2.1.293 to 2.1.318 has a `toolUseResult`. When a Bash result has no structured task id, the adapter now uses the live background `local_bash` task whose `task_started` named that Bash call. The existing subagent test now sends the stream the CLI sends. It fails without the fix.
This commit is contained in:
@@ -8502,6 +8502,18 @@ export class ClaudeAcpAgent {
|
||||
content,
|
||||
toolUseResult,
|
||||
session.toolUseCache,
|
||||
(toolUseId) => {
|
||||
for (const [taskId, task] of session.liveBackgroundTasks) {
|
||||
if (
|
||||
task.parentToolUseId === toolUseId &&
|
||||
task.taskType === "local_bash" &&
|
||||
task.backgrounded
|
||||
) {
|
||||
return taskId;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
if (backgroundBashTask) await asyncTasks.taskBackgrounded(backgroundBashTask);
|
||||
// The command's terminal runs until its task ends, unless the
|
||||
|
||||
@@ -863,18 +863,29 @@ function recordPublished(task: AsyncTask, fields: Record<string, unknown>): void
|
||||
* Recovers a background Bash task from the structured `backgroundTaskId` of
|
||||
* its tool result. The text of a tool result never gives a task id: a
|
||||
* foreground command can print any text.
|
||||
*
|
||||
* Claude Code sends no `tool_use_result` with a subagent's tool results. For
|
||||
* those, `startedTaskOf` gives the background `local_bash` task whose
|
||||
* `task_started` named the Bash call, which comes before the result.
|
||||
*/
|
||||
export function backgroundBashTaskFromToolResult(
|
||||
content: unknown,
|
||||
toolUseResult: unknown,
|
||||
toolUses: Record<string, { name: string; input: unknown }>,
|
||||
startedTaskOf?: (toolUseId: string) => string | undefined,
|
||||
): (AsyncTaskStarted & { taskId: string; toolCallId: string }) | undefined {
|
||||
if (!Array.isArray(content)) return undefined;
|
||||
const toolResults = content.flatMap(toolResultBlock);
|
||||
const bashResults = toolResults.filter((result) => toolUses[result.toolUseId]?.name === "Bash");
|
||||
if (bashResults.length === 0) return undefined;
|
||||
|
||||
const backgroundResults = structuredBackgroundResults(toolUseResult, toolUses);
|
||||
let backgroundResults = structuredBackgroundResults(toolUseResult, toolUses);
|
||||
if (backgroundResults.length === 0 && startedTaskOf) {
|
||||
backgroundResults = bashResults.flatMap((result) => {
|
||||
const taskId = result.isError ? undefined : startedTaskOf(result.toolUseId);
|
||||
return taskId ? [{ taskId, toolUseId: result.toolUseId }] : [];
|
||||
});
|
||||
}
|
||||
const distinctTaskIds = new Set(backgroundResults.map((result) => result.taskId));
|
||||
if (distinctTaskIds.size !== 1) return undefined;
|
||||
const taskId = [...distinctTaskIds][0];
|
||||
|
||||
@@ -4510,10 +4510,13 @@ describe("ACP v2 requests that subagents make", () => {
|
||||
description: "make test",
|
||||
tool_use_id: "toolu_bg",
|
||||
parent_task_id: "agent-1",
|
||||
owned_by_subagent: true,
|
||||
is_backgrounded: true,
|
||||
uuid: randomUUID(),
|
||||
session_id: options.sessionId,
|
||||
};
|
||||
// As seen live (CLI 2.1.318): a subagent's tool result has no
|
||||
// `tool_use_result`, so only the `task_started` above names the task.
|
||||
yield childMessage(options, {
|
||||
type: "user",
|
||||
message: {
|
||||
@@ -4526,13 +4529,6 @@ describe("ACP v2 requests that subagents make", () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
tool_use_result: {
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
interrupted: false,
|
||||
isImage: false,
|
||||
backgroundTaskId: "bg1",
|
||||
},
|
||||
});
|
||||
yield taskEnd(options, "agent-1", "toolu_agent-1");
|
||||
yield level(options, [bash]);
|
||||
|
||||
Reference in New Issue
Block a user