Skip to content

Commit 607921d

Browse files
authored
feat: expose parent_tool_use_id to support subagent tracking #138 (#166)
1 parent fd98d12 commit 607921d

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

src/claude_code_sdk/_internal/message_parser.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def parse_message(data: dict[str, Any]) -> Message:
4646
match message_type:
4747
case "user":
4848
try:
49+
parent_tool_use_id = data.get("parent_tool_use_id")
4950
if isinstance(data["message"]["content"], list):
5051
user_content_blocks: list[ContentBlock] = []
5152
for block in data["message"]["content"]:
@@ -70,8 +71,14 @@ def parse_message(data: dict[str, Any]) -> Message:
7071
is_error=block.get("is_error"),
7172
)
7273
)
73-
return UserMessage(content=user_content_blocks)
74-
return UserMessage(content=data["message"]["content"])
74+
return UserMessage(
75+
content=user_content_blocks,
76+
parent_tool_use_id=parent_tool_use_id,
77+
)
78+
return UserMessage(
79+
content=data["message"]["content"],
80+
parent_tool_use_id=parent_tool_use_id,
81+
)
7582
except KeyError as e:
7683
raise MessageParseError(
7784
f"Missing required field in user message: {e}", data
@@ -109,7 +116,9 @@ def parse_message(data: dict[str, Any]) -> Message:
109116
)
110117

111118
return AssistantMessage(
112-
content=content_blocks, model=data["message"]["model"]
119+
content=content_blocks,
120+
model=data["message"]["model"],
121+
parent_tool_use_id=data.get("parent_tool_use_id"),
113122
)
114123
except KeyError as e:
115124
raise MessageParseError(

src/claude_code_sdk/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ class UserMessage:
230230
"""User message."""
231231

232232
content: str | list[ContentBlock]
233+
parent_tool_use_id: str | None = None
233234

234235

235236
@dataclass
@@ -238,6 +239,7 @@ class AssistantMessage:
238239

239240
content: list[ContentBlock]
240241
model: str
242+
parent_tool_use_id: str | None = None
241243

242244

243245
@dataclass

tests/test_message_parser.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ def test_parse_user_message_with_mixed_content(self):
130130
assert isinstance(message.content[2], ToolResultBlock)
131131
assert isinstance(message.content[3], TextBlock)
132132

133+
def test_parse_user_message_inside_subagent(self):
134+
"""Test parsing a valid user message."""
135+
data = {
136+
"type": "user",
137+
"message": {"content": [{"type": "text", "text": "Hello"}]},
138+
"parent_tool_use_id": "toolu_01Xrwd5Y13sEHtzScxR77So8",
139+
}
140+
message = parse_message(data)
141+
assert isinstance(message, UserMessage)
142+
assert message.parent_tool_use_id == "toolu_01Xrwd5Y13sEHtzScxR77So8"
143+
133144
def test_parse_valid_assistant_message(self):
134145
"""Test parsing a valid assistant message."""
135146
data = {
@@ -185,6 +196,28 @@ def test_parse_valid_system_message(self):
185196
assert isinstance(message, SystemMessage)
186197
assert message.subtype == "start"
187198

199+
def test_parse_assistant_message_inside_subagent(self):
200+
"""Test parsing a valid assistant message."""
201+
data = {
202+
"type": "assistant",
203+
"message": {
204+
"content": [
205+
{"type": "text", "text": "Hello"},
206+
{
207+
"type": "tool_use",
208+
"id": "tool_123",
209+
"name": "Read",
210+
"input": {"file_path": "/test.txt"},
211+
},
212+
],
213+
"model": "claude-opus-4-1-20250805",
214+
},
215+
"parent_tool_use_id": "toolu_01Xrwd5Y13sEHtzScxR77So8",
216+
}
217+
message = parse_message(data)
218+
assert isinstance(message, AssistantMessage)
219+
assert message.parent_tool_use_id == "toolu_01Xrwd5Y13sEHtzScxR77So8"
220+
188221
def test_parse_valid_result_message(self):
189222
"""Test parsing a valid result message."""
190223
data = {

0 commit comments

Comments
 (0)