Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Sensor/adr_sensor/parsers/copilot_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ def _process_event(self, event: Dict[str, Any], session_data: Dict[str, Any]):
if tool is not None:
success = bool(data.get("success"))
result_text = self._normalize_tool_result(data.get("result"))
error_text = self._normalize_tool_result(data.get("error"))
error_text = self._normalize_tool_error(data.get("error"))
tool["result"] = result_text
tool["status"] = "success" if success else "error"
if not success:
Expand Down Expand Up @@ -513,6 +513,14 @@ def _normalize_tool_result(self, result: Any) -> Optional[str]:

return truncate_middle(text, max_length=MAX_STRING_LENGTH, edge_chars=EDGE_CHARS)

def _normalize_tool_error(self, error: Any) -> Optional[str]:
"""Use the message of Copilot's {"message", "code"} error object."""
if isinstance(error, dict):
message = error.get("message")
if isinstance(message, str) and message:
return truncate_middle(message, max_length=MAX_STRING_LENGTH, edge_chars=EDGE_CHARS)
return self._normalize_tool_result(error)

def _truncate_large_arguments(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Truncate large string values within tool argument dictionaries."""
if not isinstance(arguments, dict):
Expand Down
27 changes: 27 additions & 0 deletions Sensor/tests/test_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,33 @@ def test_tool_failure_uses_error_field_when_result_is_absent(self, tmp_path):
assert tool.result is None
assert tool.error == "File not found"

def test_tool_failure_uses_message_of_error_object(self, tmp_path):
"""Copilot's schema records a failed tool's error as {"message", "code"}."""
session_dir = tmp_path / "error-object"
events = self._minimal_events("error-object") + [
{
"type": "tool.execution_start",
"timestamp": "2026-08-10T10:00:02.000Z",
"data": {"toolCallId": "call-1", "toolName": "bash", "arguments": {"command": "rm -rf build"}},
},
{
"type": "tool.execution_complete",
"timestamp": "2026-08-10T10:00:03.000Z",
"data": {
"toolCallId": "call-1",
"success": False,
"error": {"message": "The user rejected this tool call.", "code": "denied"},
},
},
]
self._write_events(session_dir, events)

entry = CopilotParser(base_path=tmp_path).parse_session_dir(session_dir)

tool = [tool for message in entry.chat_history for tool in message.tools][0]
assert tool.status == "error"
assert tool.error == "The user rejected this tool call."

def test_default_max_age_days(self):
assert CopilotParser().max_age_days == 14

Expand Down