From 3bb15380f77c2bf4b0485daf4e7d8e274a67c683 Mon Sep 17 00:00:00 2001 From: MacMini2 Date: Sat, 18 Jul 2026 23:35:10 +0800 Subject: [PATCH] feat: expose run transport evidence --- src/openlinker/types.py | 24 ++++++++++++++++++++++++ tests/test_client.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/openlinker/types.py b/src/openlinker/types.py index dda0f48..bcfe943 100644 --- a/src/openlinker/types.py +++ b/src/openlinker/types.py @@ -187,13 +187,36 @@ class TaskCallbackSubscription(Model): @dataclass class RunResponse(Model): run_id: str = "" + agent_id: str | None = None + agent_slug: str | None = None + agent_name: str | None = None + agent_connection_mode: str | None = None status: str = "" + input: Any = None output: Any = None error_code: str | None = None error_message: str | None = None cost_cents: int = 0 duration_ms: int = 0 + started_at: str = "" + finished_at: str | None = None source: str | None = None + runtime_contract_id: str = "" + runtime_transport: str | None = None + runtime_transport_reason: str | None = None + runtime_transport_changed_at: str | None = None + dispatch_state: str = "" + attempt_count: int = 0 + max_attempts: int = 0 + next_attempt_at: str | None = None + latest_attempt_id: str | None = None + active_attempt_id: str | None = None + cancel_state: str | None = None + cancel_requested_at: str | None = None + cancel_acknowledged_at: str | None = None + cancel_reason: str | None = None + dead_lettered_at: str | None = None + replay_of_run_id: str | None = None parent_run_id: str | None = None caller_agent_id: str | None = None billing_mode: str | None = None @@ -202,6 +225,7 @@ class RunResponse(Model): requirement_evidence: Any = None evidence_summary: Any = None next_action: Any = None + replayed: bool = False @dataclass diff --git a/tests/test_client.py b/tests/test_client.py index 84bee13..cf30354 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -51,7 +51,31 @@ async def test_run_agent_encodes_request_body(): async def handler(request: httpx.Request) -> httpx.Response: seen["path"] = request.url.path seen["body"] = json.loads(request.content) - return httpx.Response(200, json={"run_id": "run-1", "status": "success", "duration_ms": 12}) + return httpx.Response( + 200, + json={ + "run_id": "run-1", + "agent_id": "agent-1", + "agent_slug": "runtime-agent", + "agent_name": "Runtime Agent", + "agent_connection_mode": "runtime", + "status": "success", + "cost_cents": 0, + "duration_ms": 12, + "started_at": "2026-07-18T00:00:00Z", + "finished_at": "2026-07-18T00:00:01Z", + "source": "api", + "runtime_contract_id": "openlinker.runtime.v2", + "runtime_transport": "long_poll", + "runtime_transport_reason": "websocket_unavailable", + "runtime_transport_changed_at": "2026-07-18T00:00:00Z", + "dispatch_state": "terminal", + "attempt_count": 1, + "max_attempts": 3, + "latest_attempt_id": "attempt-1", + "replayed": False, + }, + ) async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as hc: sdk = openlinker_client.Client("https://api.example.test", http_client=hc) @@ -69,8 +93,15 @@ async def handler(request: httpx.Request) -> httpx.Response: ) assert resp.run_id == "run-1" + assert resp.agent_connection_mode == "runtime" + assert resp.runtime_transport == "long_poll" + assert resp.runtime_transport_reason == "websocket_unavailable" + assert resp.dispatch_state == "terminal" + assert resp.attempt_count == 1 assert seen["path"] == "/api/v1/run" assert seen["body"]["agent_id"] == "agent-1" + assert "agent_connection_mode" not in seen["body"] + assert "runtime_transport" not in seen["body"] assert seen["body"]["task_callback"]["secret"] == "caller-secret"