Skip to content

Commit 9a2e334

Browse files
declan-scaleclaude
andcommitted
feat(task-messages): add optional is_error to ToolResponseContent
Additive optional field so a tool failure carries a structured status through to derived tool spans. Without it, harnesses that track tool errors (e.g. golden agent's ToolCompleted.is_error) lose error status when normalized onto the unified harness surface — a tracing-fidelity regression. Defaults to None when the harness reports no status. Threaded through the domain entity and the schema->entity converter; the entity->schema path validates by attribute so it carries through. Regenerating the SDK picks this up as ToolResponseContent.is_error, unblocking the harness wiring in scale-agentex-python (AGX1-371). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7c2ccfd commit 9a2e334

3 files changed

Lines changed: 97 additions & 52 deletions

File tree

agentex/openapi.yaml

Lines changed: 88 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,19 +1213,25 @@ paths:
12131213
: {\n \"anyOf\": [\n {},\n {\n \
12141214
\ \"type\": \"null\"\n }\n ],\n \"default\"\
12151215
: null,\n \"description\": \"The result of the tool.\",\n \
1216-
\ \"title\": \"Content\"\n }\n },\n \"title\":\
1217-
\ \"ToolResponseContentEntityOptional\",\n \"type\": \"object\"\n\
1218-
\ }\n },\n \"description\": \"Filter model for TaskMessage - all\
1219-
\ fields optional for flexible filtering.\\n\\nThe `exclude` field determines\
1220-
\ whether this filter is inclusionary or exclusionary.\\nWhen multiple\
1221-
\ filters are provided:\\n- Inclusionary filters (exclude=False) are OR'd\
1222-
\ together\\n- Exclusionary filters (exclude=True) are OR'd together and\
1223-
\ negated with $nor\\n- The two groups are AND'd: (include1 OR include2)\
1224-
\ AND NOT (exclude1 OR exclude2)\",\n \"properties\": {\n \"content\"\
1225-
: {\n \"anyOf\": [\n {\n \"$ref\": \"#/$defs/ToolRequestContentEntityOptional\"\
1226-
\n },\n {\n \"$ref\": \"#/$defs/DataContentEntityOptional\"\
1227-
\n },\n {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\
1228-
\n },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
1216+
\ \"title\": \"Content\"\n },\n \"is_error\": {\n\
1217+
\ \"anyOf\": [\n {\n \"type\": \"boolean\"\
1218+
\n },\n {\n \"type\": \"null\"\n \
1219+
\ }\n ],\n \"default\": null,\n \"\
1220+
description\": \"Whether the tool call resulted in an error. `None` when\
1221+
\ the harness does not report a status.\",\n \"title\": \"Is\
1222+
\ Error\"\n }\n },\n \"title\": \"ToolResponseContentEntityOptional\"\
1223+
,\n \"type\": \"object\"\n }\n },\n \"description\": \"Filter\
1224+
\ model for TaskMessage - all fields optional for flexible filtering.\\\
1225+
n\\nThe `exclude` field determines whether this filter is inclusionary\
1226+
\ or exclusionary.\\nWhen multiple filters are provided:\\n- Inclusionary\
1227+
\ filters (exclude=False) are OR'd together\\n- Exclusionary filters (exclude=True)\
1228+
\ are OR'd together and negated with $nor\\n- The two groups are AND'd:\
1229+
\ (include1 OR include2) AND NOT (exclude1 OR exclude2)\",\n \"properties\"\
1230+
: {\n \"content\": {\n \"anyOf\": [\n {\n \"$ref\"\
1231+
: \"#/$defs/ToolRequestContentEntityOptional\"\n },\n {\n\
1232+
\ \"$ref\": \"#/$defs/DataContentEntityOptional\"\n },\n\
1233+
\ {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\n\
1234+
\ },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
12291235
\n },\n {\n \"$ref\": \"#/$defs/ReasoningContentEntityOptional\"\
12301236
\n },\n {\n \"type\": \"null\"\n }\n \
12311237
\ ],\n \"default\": null,\n \"description\": \"Filter by message\
@@ -1416,19 +1422,24 @@ paths:
14161422
\ {},\n {\n \"type\": \"null\"\n \
14171423
\ }\n ],\n \"default\": null,\n \"description\"\
14181424
: \"The result of the tool.\",\n \"title\": \"Content\"\n \
1419-
\ }\n },\n \"title\": \"ToolResponseContentEntityOptional\"\
1420-
,\n \"type\": \"object\"\n }\n },\n \"description\": \"Filter\
1421-
\ model for TaskMessage - all fields optional for flexible filtering.\\\
1422-
n\\nThe `exclude` field determines whether this filter is inclusionary or\
1423-
\ exclusionary.\\nWhen multiple filters are provided:\\n- Inclusionary filters\
1424-
\ (exclude=False) are OR'd together\\n- Exclusionary filters (exclude=True)\
1425-
\ are OR'd together and negated with $nor\\n- The two groups are AND'd:\
1426-
\ (include1 OR include2) AND NOT (exclude1 OR exclude2)\",\n \"properties\"\
1427-
: {\n \"content\": {\n \"anyOf\": [\n {\n \"$ref\"\
1428-
: \"#/$defs/ToolRequestContentEntityOptional\"\n },\n {\n\
1429-
\ \"$ref\": \"#/$defs/DataContentEntityOptional\"\n },\n\
1430-
\ {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\n\
1431-
\ },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
1425+
\ },\n \"is_error\": {\n \"anyOf\": [\n {\n\
1426+
\ \"type\": \"boolean\"\n },\n {\n \
1427+
\ \"type\": \"null\"\n }\n ],\n \
1428+
\ \"default\": null,\n \"description\": \"Whether the tool call\
1429+
\ resulted in an error. `None` when the harness does not report a status.\"\
1430+
,\n \"title\": \"Is Error\"\n }\n },\n \"title\"\
1431+
: \"ToolResponseContentEntityOptional\",\n \"type\": \"object\"\n \
1432+
\ }\n },\n \"description\": \"Filter model for TaskMessage - all fields\
1433+
\ optional for flexible filtering.\\n\\nThe `exclude` field determines whether\
1434+
\ this filter is inclusionary or exclusionary.\\nWhen multiple filters are\
1435+
\ provided:\\n- Inclusionary filters (exclude=False) are OR'd together\\\
1436+
n- Exclusionary filters (exclude=True) are OR'd together and negated with\
1437+
\ $nor\\n- The two groups are AND'd: (include1 OR include2) AND NOT (exclude1\
1438+
\ OR exclude2)\",\n \"properties\": {\n \"content\": {\n \"anyOf\"\
1439+
: [\n {\n \"$ref\": \"#/$defs/ToolRequestContentEntityOptional\"\
1440+
\n },\n {\n \"$ref\": \"#/$defs/DataContentEntityOptional\"\
1441+
\n },\n {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\
1442+
\n },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
14321443
\n },\n {\n \"$ref\": \"#/$defs/ReasoningContentEntityOptional\"\
14331444
\n },\n {\n \"type\": \"null\"\n }\n \
14341445
\ ],\n \"default\": null,\n \"description\": \"Filter by message\
@@ -1769,19 +1780,25 @@ paths:
17691780
: {\n \"anyOf\": [\n {},\n {\n \
17701781
\ \"type\": \"null\"\n }\n ],\n \"default\"\
17711782
: null,\n \"description\": \"The result of the tool.\",\n \
1772-
\ \"title\": \"Content\"\n }\n },\n \"title\":\
1773-
\ \"ToolResponseContentEntityOptional\",\n \"type\": \"object\"\n\
1774-
\ }\n },\n \"description\": \"Filter model for TaskMessage - all\
1775-
\ fields optional for flexible filtering.\\n\\nThe `exclude` field determines\
1776-
\ whether this filter is inclusionary or exclusionary.\\nWhen multiple\
1777-
\ filters are provided:\\n- Inclusionary filters (exclude=False) are OR'd\
1778-
\ together\\n- Exclusionary filters (exclude=True) are OR'd together and\
1779-
\ negated with $nor\\n- The two groups are AND'd: (include1 OR include2)\
1780-
\ AND NOT (exclude1 OR exclude2)\",\n \"properties\": {\n \"content\"\
1781-
: {\n \"anyOf\": [\n {\n \"$ref\": \"#/$defs/ToolRequestContentEntityOptional\"\
1782-
\n },\n {\n \"$ref\": \"#/$defs/DataContentEntityOptional\"\
1783-
\n },\n {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\
1784-
\n },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
1783+
\ \"title\": \"Content\"\n },\n \"is_error\": {\n\
1784+
\ \"anyOf\": [\n {\n \"type\": \"boolean\"\
1785+
\n },\n {\n \"type\": \"null\"\n \
1786+
\ }\n ],\n \"default\": null,\n \"\
1787+
description\": \"Whether the tool call resulted in an error. `None` when\
1788+
\ the harness does not report a status.\",\n \"title\": \"Is\
1789+
\ Error\"\n }\n },\n \"title\": \"ToolResponseContentEntityOptional\"\
1790+
,\n \"type\": \"object\"\n }\n },\n \"description\": \"Filter\
1791+
\ model for TaskMessage - all fields optional for flexible filtering.\\\
1792+
n\\nThe `exclude` field determines whether this filter is inclusionary\
1793+
\ or exclusionary.\\nWhen multiple filters are provided:\\n- Inclusionary\
1794+
\ filters (exclude=False) are OR'd together\\n- Exclusionary filters (exclude=True)\
1795+
\ are OR'd together and negated with $nor\\n- The two groups are AND'd:\
1796+
\ (include1 OR include2) AND NOT (exclude1 OR exclude2)\",\n \"properties\"\
1797+
: {\n \"content\": {\n \"anyOf\": [\n {\n \"$ref\"\
1798+
: \"#/$defs/ToolRequestContentEntityOptional\"\n },\n {\n\
1799+
\ \"$ref\": \"#/$defs/DataContentEntityOptional\"\n },\n\
1800+
\ {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\n\
1801+
\ },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
17851802
\n },\n {\n \"$ref\": \"#/$defs/ReasoningContentEntityOptional\"\
17861803
\n },\n {\n \"type\": \"null\"\n }\n \
17871804
\ ],\n \"default\": null,\n \"description\": \"Filter by message\
@@ -1972,19 +1989,24 @@ paths:
19721989
\ {},\n {\n \"type\": \"null\"\n \
19731990
\ }\n ],\n \"default\": null,\n \"description\"\
19741991
: \"The result of the tool.\",\n \"title\": \"Content\"\n \
1975-
\ }\n },\n \"title\": \"ToolResponseContentEntityOptional\"\
1976-
,\n \"type\": \"object\"\n }\n },\n \"description\": \"Filter\
1977-
\ model for TaskMessage - all fields optional for flexible filtering.\\\
1978-
n\\nThe `exclude` field determines whether this filter is inclusionary or\
1979-
\ exclusionary.\\nWhen multiple filters are provided:\\n- Inclusionary filters\
1980-
\ (exclude=False) are OR'd together\\n- Exclusionary filters (exclude=True)\
1981-
\ are OR'd together and negated with $nor\\n- The two groups are AND'd:\
1982-
\ (include1 OR include2) AND NOT (exclude1 OR exclude2)\",\n \"properties\"\
1983-
: {\n \"content\": {\n \"anyOf\": [\n {\n \"$ref\"\
1984-
: \"#/$defs/ToolRequestContentEntityOptional\"\n },\n {\n\
1985-
\ \"$ref\": \"#/$defs/DataContentEntityOptional\"\n },\n\
1986-
\ {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\n\
1987-
\ },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
1992+
\ },\n \"is_error\": {\n \"anyOf\": [\n {\n\
1993+
\ \"type\": \"boolean\"\n },\n {\n \
1994+
\ \"type\": \"null\"\n }\n ],\n \
1995+
\ \"default\": null,\n \"description\": \"Whether the tool call\
1996+
\ resulted in an error. `None` when the harness does not report a status.\"\
1997+
,\n \"title\": \"Is Error\"\n }\n },\n \"title\"\
1998+
: \"ToolResponseContentEntityOptional\",\n \"type\": \"object\"\n \
1999+
\ }\n },\n \"description\": \"Filter model for TaskMessage - all fields\
2000+
\ optional for flexible filtering.\\n\\nThe `exclude` field determines whether\
2001+
\ this filter is inclusionary or exclusionary.\\nWhen multiple filters are\
2002+
\ provided:\\n- Inclusionary filters (exclude=False) are OR'd together\\\
2003+
n- Exclusionary filters (exclude=True) are OR'd together and negated with\
2004+
\ $nor\\n- The two groups are AND'd: (include1 OR include2) AND NOT (exclude1\
2005+
\ OR exclude2)\",\n \"properties\": {\n \"content\": {\n \"anyOf\"\
2006+
: [\n {\n \"$ref\": \"#/$defs/ToolRequestContentEntityOptional\"\
2007+
\n },\n {\n \"$ref\": \"#/$defs/DataContentEntityOptional\"\
2008+
\n },\n {\n \"$ref\": \"#/$defs/TextContentEntityOptional\"\
2009+
\n },\n {\n \"$ref\": \"#/$defs/ToolResponseContentEntityOptional\"\
19882010
\n },\n {\n \"$ref\": \"#/$defs/ReasoningContentEntityOptional\"\
19892011
\n },\n {\n \"type\": \"null\"\n }\n \
19902012
\ ],\n \"default\": null,\n \"description\": \"Filter by message\
@@ -6445,6 +6467,13 @@ components:
64456467
content:
64466468
title: Content
64476469
description: The result of the tool.
6470+
is_error:
6471+
anyOf:
6472+
- type: boolean
6473+
- type: 'null'
6474+
title: Is Error
6475+
description: Whether the tool call resulted in an error. `None` when the
6476+
harness does not report a status.
64486477
type: object
64496478
required:
64506479
- author
@@ -6480,6 +6509,13 @@ components:
64806509
content:
64816510
title: Content
64826511
description: The result of the tool.
6512+
is_error:
6513+
anyOf:
6514+
- type: boolean
6515+
- type: 'null'
6516+
title: Is Error
6517+
description: Whether the tool call resulted in an error. `None` when the
6518+
harness does not report a status.
64836519
type: object
64846520
required:
64856521
- author

agentex/src/api/schemas/task_messages.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ class ToolResponseContent(BaseTaskMessageContent):
108108
..., description="The name of the tool that is being responded to."
109109
)
110110
content: Any = Field(..., description="The result of the tool.")
111+
is_error: bool | None = Field(
112+
default=None,
113+
description="Whether the tool call resulted in an error. `None` when the harness does not report a status.",
114+
)
111115

112116

113117
class DataContent(BaseTaskMessageContent):

agentex/src/domain/entities/task_messages.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ class ToolResponseContentEntity(BaseTaskMessageContentEntity):
125125
..., description="The name of the tool that is being responded to."
126126
)
127127
content: Any = Field(..., description="The result of the tool.")
128+
is_error: bool | None = Field(
129+
default=None,
130+
description="Whether the tool call resulted in an error. `None` when the harness does not report a status.",
131+
)
128132

129133

130134
OptionalToolResponseContentEntity = make_optional(ToolResponseContentEntity)
@@ -207,6 +211,7 @@ def convert_task_message_content_to_entity(
207211
tool_call_id=content.tool_call_id,
208212
name=content.name,
209213
content=content.content,
214+
is_error=content.is_error,
210215
)
211216

212217
assert_never(content)

0 commit comments

Comments
 (0)