fix: surface copilot acp progress and stale detection

This commit is contained in:
David Zhang 2026-04-24 17:54:46 +07:00
parent 18f3fc8a6f
commit 0524a40790
4 changed files with 196 additions and 13 deletions

View file

@ -141,6 +141,61 @@ class CopilotACPClientSafetyTests(unittest.TestCase):
self.assertIn("error", response)
self.assertFalse(outside.exists())
def test_session_update_streams_live_text_and_reasoning_callbacks(self) -> None:
streamed: list[str] = []
reasoned: list[str] = []
self.client = CopilotACPClient(
acp_cwd="/tmp",
stream_delta_callback=streamed.append,
reasoning_callback=reasoned.append,
)
text_parts: list[str] = []
reasoning_parts: list[str] = []
process = _FakeProcess()
handled = self.client._handle_server_message(
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"update": {
"sessionUpdate": "agent_message_chunk",
"content": {"text": "hello"},
}
},
},
process=process,
cwd="/tmp",
text_parts=text_parts,
reasoning_parts=reasoning_parts,
)
self.assertTrue(handled)
handled = self.client._handle_server_message(
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"update": {
"sessionUpdate": "agent_thought_chunk",
"content": {"text": "thinking"},
}
},
},
process=process,
cwd="/tmp",
text_parts=text_parts,
reasoning_parts=reasoning_parts,
)
self.assertTrue(handled)
self.assertEqual(text_parts, ["hello"])
self.assertEqual(reasoning_parts, ["thinking"])
self.assertEqual(streamed, ["hello"])
self.assertEqual(reasoned, ["thinking"])
if __name__ == "__main__":
unittest.main()

View file

@ -1,5 +1,6 @@
import sys
import threading
import time
import types
from types import SimpleNamespace
@ -154,6 +155,46 @@ def test_concurrent_requests_do_not_break_each_other_when_one_client_closes(monk
assert len(factory.calls) == 2
def test_copilot_acp_provider_activity_prevents_false_stale_timeout(monkeypatch):
class FakeCopilotRequestClient(FakeRequestClient):
def __init__(self, kwargs):
self._kwargs = dict(kwargs)
super().__init__(self._responder)
def _responder(self, **kwargs):
activity_cb = self._kwargs.get("activity_callback")
stream_cb = self._kwargs.get("stream_delta_callback")
reasoning_cb = self._kwargs.get("reasoning_callback")
for i in range(8):
if activity_cb is not None:
activity_cb(f"copilot-acp heartbeat {i}")
time.sleep(0.05)
if reasoning_cb is not None:
reasoning_cb("thinking")
if stream_cb is not None:
stream_cb("done")
return {"ok": True}
def _fake_acp_factory(**kwargs):
return FakeCopilotRequestClient(kwargs)
monkeypatch.setattr("agent.copilot_acp_client.CopilotACPClient", _fake_acp_factory)
agent = _build_agent()
agent.provider = "copilot-acp"
agent.base_url = "acp://copilot"
agent.model = "gpt-5.4"
agent._client_kwargs = {"api_key": "copilot-acp", "base_url": agent.base_url}
agent.client = FakeSharedClient(lambda **kwargs: {"shared": True})
agent.stream_delta_callback = lambda _delta: None
agent.reasoning_callback = lambda _delta: None
agent.status_callback = None
agent._compute_non_stream_stale_timeout = lambda _messages: 0.08
result = agent._interruptible_api_call({"model": agent.model, "messages": []})
assert result == {"ok": True}
def test_streaming_call_recreates_closed_shared_client_before_request(monkeypatch):
chunks = iter([