feat(core): publish tool.result on the bus for the activity panel
This commit is contained in:
@@ -29,7 +29,9 @@ from claude_agent_sdk import (
|
||||
AssistantMessage,
|
||||
ResultMessage,
|
||||
StreamEvent,
|
||||
ToolResultBlock,
|
||||
ToolUseBlock,
|
||||
UserMessage,
|
||||
fork_session_via_store,
|
||||
project_key_for_directory,
|
||||
)
|
||||
@@ -963,6 +965,20 @@ class Conversations:
|
||||
name=block.name,
|
||||
input=block.input,
|
||||
)
|
||||
elif isinstance(message, UserMessage):
|
||||
blocks = message.content if isinstance(message.content, list) else ()
|
||||
for block in blocks:
|
||||
if isinstance(block, ToolResultBlock):
|
||||
self._bus.publish(
|
||||
"tool.result",
|
||||
conversation_id=conversation_id,
|
||||
turn_id=turn_id,
|
||||
origin=origin,
|
||||
parent_tool_use_id=parent,
|
||||
tool_use_id=block.tool_use_id,
|
||||
is_error=bool(block.is_error),
|
||||
content=_result_preview(block.content),
|
||||
)
|
||||
elif isinstance(message, ResultMessage) and parent is None:
|
||||
self._bus.publish(
|
||||
"result",
|
||||
@@ -1055,6 +1071,24 @@ def _iso(value: datetime | None) -> str | None:
|
||||
return _aware(value).isoformat(timespec="seconds") if value is not None else None
|
||||
|
||||
|
||||
def _result_preview(
|
||||
content: str | list[dict[str, Any]] | None, limit: int = 400
|
||||
) -> str:
|
||||
"""Short text of a tool result for the panel: the transcript keeps the whole."""
|
||||
if content is None:
|
||||
return ""
|
||||
text = (
|
||||
content
|
||||
if isinstance(content, str)
|
||||
else "\n".join(
|
||||
str(part.get("text", ""))
|
||||
for part in content
|
||||
if isinstance(part, dict) and part.get("type") == "text"
|
||||
)
|
||||
)
|
||||
return text if len(text) <= limit else text[:limit] + "…"
|
||||
|
||||
|
||||
def _usage_dict(capture: TurnCapture) -> dict[str, Any] | None:
|
||||
usage = capture.usage
|
||||
if usage is None:
|
||||
|
||||
@@ -12,6 +12,9 @@ from claude_agent_sdk import (
|
||||
ResultMessage,
|
||||
StreamEvent,
|
||||
TextBlock,
|
||||
ToolResultBlock,
|
||||
ToolUseBlock,
|
||||
UserMessage,
|
||||
project_key_for_directory,
|
||||
)
|
||||
|
||||
@@ -655,3 +658,40 @@ def test_agent_kinds_follow_prompts(tmp_path: Path) -> None:
|
||||
kinds=("master", "deep"),
|
||||
prompts=Prompts(master=()),
|
||||
)
|
||||
|
||||
|
||||
async def test_observer_publishes_tool_results_for_the_panel(world: World) -> None:
|
||||
seen: list[dict[str, Any]] = []
|
||||
|
||||
async def collect() -> None:
|
||||
async for event in world.bus.stream(conversation_id="c1"):
|
||||
seen.append(event)
|
||||
|
||||
task = asyncio.create_task(collect())
|
||||
await asyncio.sleep(0)
|
||||
observe = world.conversations._observer("c1", "t1", "user")
|
||||
observe(
|
||||
AssistantMessage(
|
||||
content=[ToolUseBlock(id="toolu_1", name="Bash", input={"command": "ls"})],
|
||||
model="m",
|
||||
parent_tool_use_id="toolu_0",
|
||||
)
|
||||
)
|
||||
observe(
|
||||
UserMessage(
|
||||
content=[
|
||||
ToolResultBlock(tool_use_id="toolu_1", content="a\nb", is_error=False)
|
||||
],
|
||||
parent_tool_use_id="toolu_0",
|
||||
)
|
||||
)
|
||||
observe(UserMessage(content="plain text, no tool result"))
|
||||
await asyncio.sleep(0.05)
|
||||
task.cancel()
|
||||
assert [e["type"] for e in seen] == ["tool", "tool.result"]
|
||||
tool, result = seen
|
||||
assert tool["tool_use_id"] == "toolu_1" and tool["parent_tool_use_id"] == "toolu_0"
|
||||
assert result["tool_use_id"] == "toolu_1"
|
||||
assert result["parent_tool_use_id"] == "toolu_0"
|
||||
assert result["turn_id"] == "t1" and result["is_error"] is False
|
||||
assert result["content"] == "a\nb"
|
||||
|
||||
Reference in New Issue
Block a user