Skip to content

Commit c0b950c

Browse files
v0.2.3: Better error details and markdown table stripping
- Error events now always show a message (fallback to 'tool call failed') - Markdown tables stripped from assistant_response (no more pipe soup) - Code blocks, list markers, numbered lists stripped - Multiple spaces collapsed - Error messages also stripped of markdown before display 107 tests. Zero dependencies. Co-authored-by: Ona <no-reply@ona.com>
1 parent 6934636 commit c0b950c

5 files changed

Lines changed: 51 additions & 8 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "agent-strace"
7-
version = "0.2.2"
7+
version = "0.2.3"
88
description = "strace for AI agents. Capture and replay every tool call, LLM request, and decision point."
99
readme = "README.md"
1010
license = "AGPL-3.0-only"

src/agent_trace/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""agent-trace: strace for AI agents."""
22

3-
__version__ = "0.2.2"
3+
__version__ = "0.2.3"

src/agent_trace/hooks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,10 @@ def handle_post_tool(input_data: dict, failed: bool = False) -> None:
289289

290290
if failed:
291291
event_type = EventType.ERROR
292+
error_msg = str(tool_output)[:500] if tool_output else "tool call failed"
292293
event_data = {
293294
"tool_name": tool_name,
294-
"error": str(tool_output)[:500],
295+
"error": error_msg,
295296
}
296297
else:
297298
event_type = EventType.TOOL_RESULT

src/agent_trace/replay.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ def _format_duration(ms: float | None) -> str:
8585
def _strip_markdown(text: str) -> str:
8686
"""Remove markdown formatting for terminal display."""
8787
import re
88+
# Code blocks (fenced)
89+
text = re.sub(r'```[\s\S]*?```', '', text)
8890
# Bold and italic
8991
text = re.sub(r'\*\*(.+?)\*\*', r'\1', text)
9092
text = re.sub(r'\*(.+?)\*', r'\1', text)
@@ -96,9 +98,16 @@ def _strip_markdown(text: str) -> str:
9698
text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE)
9799
# Links [text](url) -> text
98100
text = re.sub(r'\[(.+?)\]\(.+?\)', r'\1', text)
99-
# Collapse multiple newlines
101+
# Markdown tables: strip separator rows and pipe formatting
102+
text = re.sub(r'^\|?[-:| ]+\|[-:| ]+\|?$', '', text, flags=re.MULTILINE)
103+
text = re.sub(r'^\|(.+)\|$', lambda m: m.group(1).replace('|', ', ').strip(), text, flags=re.MULTILINE)
104+
# List markers
105+
text = re.sub(r'^[\s]*[-*+]\s+', '', text, flags=re.MULTILINE)
106+
text = re.sub(r'^[\s]*\d+\.\s+', '', text, flags=re.MULTILINE)
107+
# Collapse multiple spaces and newlines
100108
text = re.sub(r'\n{2,}', ' ', text)
101109
text = text.replace('\n', ' ')
110+
text = re.sub(r' {2,}', ' ', text)
102111
return text.strip()
103112

104113

@@ -234,13 +243,14 @@ def format_event(event: TraceEvent, base_ts: float | None = None) -> str:
234243
parts.append(f"{color}{C.BOLD}error{C.RESET}")
235244
if name:
236245
parts.append(f"{C.RED}{name}{C.RESET}")
246+
if code:
247+
parts.append(f"{C.DIM}(code: {code}){C.RESET}")
237248
if msg:
238-
msg_preview = str(msg)[:120]
239-
if len(str(msg)) > 120:
249+
msg = _strip_markdown(str(msg))
250+
msg_preview = msg[:150]
251+
if len(msg) > 150:
240252
msg_preview += "..."
241253
parts.append(f"\n{C.GRAY}{'':>14} {C.RED}{msg_preview}{C.RESET}")
242-
if code:
243-
parts.append(f"{C.DIM}(code: {code}){C.RESET}")
244254

245255
elif event.event_type == EventType.SESSION_START:
246256
cmd = event.data.get("command", [])

tests/test_replay.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,38 @@ def test_multiline_collapsed(self):
3737
def test_plain_text_unchanged(self):
3838
self.assertEqual(_strip_markdown("hello world"), "hello world")
3939

40+
def test_table_stripped(self):
41+
table = "| File | Tests |\n|---|---|\n| test_a.py | 7 |\n| test_b.py | 25 |"
42+
result = _strip_markdown(table)
43+
self.assertNotIn("|---|", result)
44+
self.assertIn("test_a.py", result)
45+
self.assertIn("7", result)
46+
47+
def test_table_separator_removed(self):
48+
result = _strip_markdown("|---|---|")
49+
self.assertEqual(result, "")
50+
51+
def test_code_block_removed(self):
52+
result = _strip_markdown("before\n```python\nprint('hi')\n```\nafter")
53+
self.assertNotIn("print", result)
54+
self.assertIn("before", result)
55+
self.assertIn("after", result)
56+
57+
def test_list_markers_stripped(self):
58+
result = _strip_markdown("- item one\n- item two\n* item three")
59+
self.assertIn("item one", result)
60+
self.assertNotIn("- ", result)
61+
self.assertNotIn("* ", result)
62+
63+
def test_numbered_list_stripped(self):
64+
result = _strip_markdown("1. first\n2. second")
65+
self.assertIn("first", result)
66+
self.assertNotIn("1.", result)
67+
68+
def test_multiple_spaces_collapsed(self):
69+
result = _strip_markdown("hello world")
70+
self.assertEqual(result, "hello world")
71+
4072

4173
class TestToolCallDetail(unittest.TestCase):
4274
def test_bash_command(self):

0 commit comments

Comments
 (0)