fix(python-sdk): preserve reused session ancestry

This commit is contained in:
Yichen Jiang
2026-07-24 12:21:10 +08:00
parent b9f8eca10c
commit 11eedca16e
5 changed files with 81 additions and 15 deletions
+11 -8
View File
@@ -445,7 +445,7 @@ class HarnessClient:
self._notification_subscribers.pop(subscription_id, None)
def _record_session_relationship_locked(self, notification: Notification) -> None:
if notification.method not in {"subagent.started", "subagent.finished"}:
if notification.method != "subagent.started":
return
parent_id = notification.payload.get("parentSessionId")
child_id = notification.payload.get("childSessionId")
@@ -461,15 +461,18 @@ class HarnessClient:
def _notification_belongs_to_session_tree(self, session_id: str) -> NotificationFilter:
def belongs(notification: Notification) -> bool:
payload = notification.payload
related_ids = (
payload.get("sessionId"),
payload.get("parentSessionId"),
payload.get("childSessionId"),
)
return any(
if notification.method in {"subagent.started", "subagent.finished"}:
parent_id = payload.get("parentSessionId")
if (
isinstance(parent_id, str)
and self._session_is_descendant_of(parent_id, session_id)
):
return True
return payload.get("childSessionId") == session_id
related_id = payload.get("sessionId")
return (
isinstance(related_id, str)
and self._session_is_descendant_of(related_id, session_id)
for related_id in related_ids
)
return belongs
+60 -1
View File
@@ -9,7 +9,7 @@ from pathlib import Path
import pytest
from deepseek_harness import DeepSeekHarness, HarnessClient, HarnessConfig
from deepseek_harness import DeepSeekHarness, HarnessClient, HarnessConfig, Notification
def test_high_level_sdk_runs_turn_and_collects_final_response(tmp_path: Path) -> None:
@@ -442,6 +442,65 @@ def test_session_subscription_keeps_descendant_relationships_across_subscription
assert client._notifications.qsize() == 0
def test_session_subscription_preserves_reused_child_ancestry_after_late_finish() -> None:
client = HarnessClient()
old_seen: list[Notification] = []
new_seen: list[Notification] = []
with (
client.subscribe_session_notifications("old-parent") as old_subscription,
client.subscribe_session_notifications("new-parent") as new_subscription,
):
client._handle_message({
"jsonrpc": "2.0",
"method": "subagent.started",
"params": {"parentSessionId": "old-parent", "childSessionId": "reused-child"},
})
old_subscription.drain(old_seen.append)
new_subscription.drain(new_seen.append)
assert [notification.method for notification in old_seen] == ["subagent.started"]
assert new_seen == []
client._handle_message({
"jsonrpc": "2.0",
"method": "subagent.started",
"params": {"parentSessionId": "new-parent", "childSessionId": "reused-child"},
})
old_subscription.drain(old_seen.append)
new_subscription.drain(new_seen.append)
assert [notification.method for notification in new_seen] == ["subagent.started"]
client._handle_message({
"jsonrpc": "2.0",
"method": "subagent.finished",
"params": {"parentSessionId": "old-parent", "childSessionId": "reused-child"},
})
old_subscription.drain(old_seen.append)
new_subscription.drain(new_seen.append)
assert [notification.method for notification in old_seen] == [
"subagent.started",
"subagent.finished",
]
assert [notification.method for notification in new_seen] == ["subagent.started"]
client._handle_message({
"jsonrpc": "2.0",
"method": "session.event",
"params": {"sessionId": "reused-child", "event": {"type": "assistant/message"}},
})
old_subscription.drain(old_seen.append)
new_subscription.drain(new_seen.append)
assert [notification.method for notification in old_seen] == [
"subagent.started",
"subagent.finished",
]
assert [notification.method for notification in new_seen] == [
"subagent.started",
"session.event",
]
assert client._notifications.qsize() == 0
def test_client_contains_notification_filter_failure_to_its_subscription(tmp_path: Path) -> None:
script = tmp_path / "fake_bridge.py"
script.write_text(