diff --git a/lib/roast/cogs/agent/providers/claude/tool_result.rb b/lib/roast/cogs/agent/providers/claude/tool_result.rb index 979b5029..2119612f 100644 --- a/lib/roast/cogs/agent/providers/claude/tool_result.rb +++ b/lib/roast/cogs/agent/providers/claude/tool_result.rb @@ -327,7 +327,8 @@ def ok_line(*parts) summary.present? ? "#{prefix} #{summary}" : prefix end - # Renders " ERROR " with any wrapper stripped. + # Renders " ERROR " – the text inside the + # wrapper, or the whole content when it is unwrapped. # # Reads the instance's `content` and `tool_name` to produce a single-line # error summary. Error messages are intentionally NOT truncated so the full @@ -339,7 +340,7 @@ def ok_line(*parts) # #: () -> String def error_line - message = content.to_s.gsub(%r{}, "").strip + message = tag_text("tool_use_error") || content.to_s.strip "#{tool_name.to_s.upcase} ERROR #{message}".strip end diff --git a/test/roast/cogs/agent/providers/claude/tool_result_test.rb b/test/roast/cogs/agent/providers/claude/tool_result_test.rb index 1a0dc9f0..fb894070 100644 --- a/test/roast/cogs/agent/providers/claude/tool_result_test.rb +++ b/test/roast/cogs/agent/providers/claude/tool_result_test.rb @@ -103,6 +103,22 @@ class Claude::ToolResultTest < ActiveSupport::TestCase assert_equal "BASH ERROR File has not been read yet.", output end + test "error_line preserves special characters in the tool_use_error body" do + tool_use_message = Claude::Messages::ToolUseMessage.new( + type: :tool_use, + hash: { name: "bash", input: {} }, + ) + tool_result = Claude::ToolResult.new( + tool_use: tool_use_message, + content: "parse error: a < b && c > d, see x & exit 1", + is_error: true, + ) + + output = tool_result.send(:error_line) + + assert_equal "BASH ERROR parse error: a < b && c > d, see x & exit 1", output + end + test "error_line handles nil content gracefully" do tool_use_message = Claude::Messages::ToolUseMessage.new( type: :tool_use,