fix(providers): pretty-print tool args/results in dashboard events#161
Merged
fix(providers): pretty-print tool args/results in dashboard events#161
Conversation
Closes #93 The web dashboard, JSONL event log, and console verbose mode all show tool-call activity by consuming `agent_tool_start` / `agent_tool_complete` events. Both providers were emitting raw `str()` output of structured SDK objects, producing two readability problems: 1. Copilot's tool results showed the full Python repr of the SDK `Result` dataclass: Result(content='line one\nline two', contents=None, detailed_content='...', kind=None) with literal \n / \r\n escapes and doubled \\ on Windows paths. 2. Tool arguments rendered as Python dict repr (`{'k': 'v'}` with single quotes, doubled backslashes) instead of JSON. Add `providers/_event_format.py` with two helpers: - `format_tool_arguments(args, max_length=500)` — JSON-encodes dict-like arguments, falls back to `str()` for unusual shapes, truncates with a trailing `…`. - `extract_tool_result_text(result, max_length=500)` — pulls `.content` (then `.detailed_content`) from structured Copilot Result objects, passes plain strings through unchanged (Claude provider), truncates with `…`. Apply to: - `copilot.py` `_forward_event` — primary fix for dashboard / JSONL. - `copilot.py` `_log_event_verbose` — same fix for the `-v`/`-vv` console output (tool args + tool results). - `claude.py` agentic loop — fixes the same `str(dict(...))` arguments anti-pattern for provider parity. After this change a Windows tool result like `Result(content='Errors: ...\\nMSB4276 ...')` renders as plain multi-line text in the dashboard activity panel. Tests: - New `tests/test_providers/test_event_format.py` covers JSON encoding, Result-object unwrapping, plain-string passthrough, truncation. - Updated existing Claude truncation tests for the new `<= 500 + 1-char ellipsis` contract. - 2384 tests pass (1 unrelated live Copilot integration test excluded). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r formatters Addresses review feedback on PR #161: 1. **Copilot `_forward_event` integration tests** (provider parity gap): New tests/test_providers/test_copilot_event_callback.py mirrors the Claude tests, verifying the agent_tool_start / agent_tool_complete payloads are JSON-formatted (arguments) and unwrapped from the structured Result object (result), with truncation + ellipsis. Also covers the .args/.output attribute aliases. This prevents a regression where a future change to copilot.py reverts to `str(result)[:500]` without any test failing. 2. **Boundary tests for both helpers** in test_event_format.py: - At max_length: returned unchanged, no ellipsis. - One char below max_length: returned unchanged. - One char above max_length: truncated with ellipsis (total = max_length + 1). Catches off-by-one errors in the truncation predicate. 3. **Non-string content tests** for extract_tool_result_text: Documents the actual contract — when result.content is a non-string non-None value, the helper falls back to str(result) (NOT to detailed_content), because the `text_attr is None` branch isn't taken. Locks the behavior so a future refactor is forced to think about it. 4. **Docstring clarifications** in _event_format.py: - Both helpers now explicitly state the truncated result is max_length + 1 chars (the previous wording "truncated to max_length characters with a trailing ellipsis" was ambiguous). - extract_tool_result_text docstring now describes the actual decision order (plain string → content → detailed_content → str(result)) instead of the misleading "falls back to str(result) for plain-string results (Claude provider)" wording, which made it sound like Claude's strings went through the str() fallback. No production code changes — only tests and documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merged
jrob5756
added a commit
that referenced
this pull request
May 6, 2026
- conductor resume flag parity with run (#158) - reasoning effort displayed in dashboard (#160) - iteration_limit_reached/resolved events for dashboard (#162) - registry latest now means default branch HEAD, not newest tag (#157) - forbid extra fields on Agent/Parallel/ForEach/Workflow schemas (#159) - pretty-print tool args/results in dashboard events (#161) - capture uv stdout+stderr on Windows install failure (#156) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #93
Problem
The web dashboard, JSONL event log, and console verbose mode all show tool-call activity by consuming
agent_tool_start/agent_tool_completeevents. Both providers were emitting rawstr()output of structured SDK objects, which produced two readability problems:Copilot tool results showed the full Python repr of the SDK
Resultdataclass:with literal
\\n/\\r\\nescapes and doubled\\\\on Windows paths.Tool arguments (both providers) rendered as Python dict repr (
{'k': 'v'}with single quotes, doubled backslashes) instead of JSON.Fix
New
src/conductor/providers/_event_format.pymodule exposes two helpers used by both providers:format_tool_arguments(args, max_length=500)— JSON-encodes dict-like arguments, falls back tostr()for unusual shapes, truncates with a trailing….extract_tool_result_text(result, max_length=500)— pulls.content(then.detailed_content) from structured CopilotResultobjects, passes plain strings through unchanged (Claude provider), truncates with….Applied at three sites:
copilot.py_forward_event(lines ~1317–1345)copilot.py_log_event_verbose(lines ~1219–1252)-v/-vvconsole outputclaude.pystr(dict(...))arguments fix for provider parityBefore / after
A Windows tool result that previously rendered as:
now renders as:
Tool arguments now serialise as JSON:
Truncation contract
Helpers truncate to
max_lengthand append a single-character…ellipsis when truncation occurs (solen(out) <= max_length + 1). Two existing Claude tests (test_tool_arguments_truncated,test_tool_result_truncated) updated to reflect this and to additionally assert the ellipsis is present.Tests
tests/test_providers/test_event_format.pycovers: empty inputs, JSON encoding, Windows-path single-escaping, structuredResultunwrapping (preferringcontentoverdetailed_content), plain-string passthrough, fallback tostr()for unknown shapes, and truncation.tests/test_providers/test_claude_event_callback.pytruncation tests updated.make lintclean.Provider parity
Per the AGENTS.md Provider Parity rule, the
agent_tool_start/agent_tool_completeevent payloads are now consistent across both providers (JSON arguments, plain-text result content), so the dashboard, JSONL log, and console subscriber all render the same way regardless of provider.