feat: enhance tool result handling with tool_call_id for concurrent execution

This commit is contained in:
Xi Zhang
2026-04-19 23:15:20 +01:00
parent bd501cce34
commit 06822f236c
7 changed files with 99 additions and 8 deletions
+3 -2
View File
@@ -208,10 +208,11 @@ class SubAgentWidget(Vertical):
widget.set_success(content)
else:
widget.set_error(content)
# Move from running to completed
# Move from running to completed (dedup guards against repeat
# deliveries of the same tool result inflating the collapse summary).
if matched_key and matched_key in self._running_ids:
self._running_ids.remove(matched_key)
if matched_key:
if matched_key and matched_key not in self._completed_ids:
self._completed_ids.append(matched_key)
self._update_visibility()
+6 -1
View File
@@ -82,7 +82,11 @@ class StreamEventEmitter:
@staticmethod
def subagent_tool_result(
subagent: str, name: str, content: str, success: bool = True
subagent: str,
name: str,
content: str,
success: bool = True,
tool_call_id: str = "",
) -> StreamEvent:
"""Tool result from inside a sub-agent."""
return StreamEvent(
@@ -93,6 +97,7 @@ class StreamEventEmitter:
"name": name,
"content": content,
"success": success,
"id": tool_call_id,
},
)
+2 -1
View File
@@ -801,11 +801,12 @@ async def stream_agent_events(
info.id,
).data
name = getattr(msg, "name", "unknown")
tool_call_id = getattr(msg, "tool_call_id", "") or ""
raw_content, _is_img = _extract_tool_content(msg)
content = raw_content[: DisplayLimits.TOOL_RESULT_MAX]
success = is_success(content)
yield emitter.subagent_tool_result(
subagent, name, content, success
subagent, name, content, success, tool_call_id
).data
else:
for ev in _process_tool_result(msg, emitter, main_tracker):
+22 -4
View File
@@ -42,17 +42,34 @@ class SubAgentState:
return
self.tool_calls.append(tc_data)
def add_tool_result(self, name: str, content: str, success: bool = True):
result = {"name": name, "content": content, "success": success}
def add_tool_result(
self,
name: str,
content: str,
success: bool = True,
tool_call_id: str = "",
):
result = {
"name": name,
"content": content,
"success": success,
"tool_call_id": tool_call_id,
}
self.tool_results.append(result)
# Try to match result to the first unmatched tool call with same name
# Preferred: exact id match (correct under concurrent same-name tools).
if tool_call_id:
for tc in self.tool_calls:
if tc.get("id") == tool_call_id:
self._result_map[tool_call_id] = result
return
# Fallback: first unmatched tool call with same name.
for tc in self.tool_calls:
tc_id = tc.get("id", "")
tc_name = tc.get("name", "")
if tc_id and tc_id not in self._result_map and tc_name == name:
self._result_map[tc_id] = result
return
# Fallback: match first unmatched tool call
# Last resort: first unmatched tool call regardless of name.
for tc in self.tool_calls:
tc_id = tc.get("id", "")
if tc_id and tc_id not in self._result_map:
@@ -257,6 +274,7 @@ class StreamState:
event.get("name", "unknown"),
event.get("content", ""),
event.get("success", True),
event.get("id", ""),
)
elif event_type == "subagent_end":