Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

This is a pre-1.0 breaking Runtime cutover.

- Added Browser interaction policy, policy generation, canonical mutation
origins, origin digest, and Browser contract evidence to `RunResponse` and
the public Core client contract fixture.
- Added the async, single-use `RuntimeWorker` with direct Python handler execution.
- Added credential-free Runtime discovery, mTLS, Session attachment generations,
WebSocket/long-poll recovery, lease renewal, resume, cancellation and drain.
Expand Down
6 changes: 3 additions & 3 deletions contracts/core-client.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
{"client_method":"get_agent","http_method":"GET","path":"/api/v1/agents/{slug}","auth":"none"},
{"client_method":"get_agent_card","http_method":"GET","path":"/api/v1/agents/{slug}/agent-card.json","auth":"none"},
{"client_method":"get_agent_card","http_method":"GET","path":"/api/v1/agents/{slug}/agent-card.extended.json","auth":"none"},
{"client_method":"run_agent","http_method":"POST","path":"/api/v1/run","auth":"access_token","required_scope":"agents:run","required_headers":["Idempotency-Key"],"success_statuses":[200,201,202],"response_headers":["Location","Idempotency-Replayed"],"response_fields":["agent_connection_mode","runtime_transport","runtime_transport_reason","runtime_transport_changed_at","dispatch_state","attempt_count","max_attempts","started_at","replayed"]},
{"client_method":"start_agent_run","http_method":"POST","path":"/api/v1/runs","auth":"access_token","required_scope":"agents:run","required_headers":["Idempotency-Key"],"success_statuses":[200,201,202],"response_headers":["Location","Idempotency-Replayed"],"response_fields":["agent_connection_mode","runtime_transport","runtime_transport_reason","runtime_transport_changed_at","dispatch_state","attempt_count","max_attempts","started_at","replayed"]},
{"client_method":"get_run","http_method":"GET","path":"/api/v1/runs/{id}","auth":"access_token","required_scope":"runs:read","response_fields":["agent_connection_mode","runtime_transport","runtime_transport_reason","runtime_transport_changed_at","dispatch_state","attempt_count","max_attempts","started_at","replayed"]},
{"client_method":"run_agent","http_method":"POST","path":"/api/v1/run","auth":"access_token","required_scope":"agents:run","required_headers":["Idempotency-Key"],"success_statuses":[200,201,202],"response_headers":["Location","Idempotency-Replayed"],"response_fields":["agent_connection_mode","runtime_transport","runtime_transport_reason","runtime_transport_changed_at","browser_interaction_policy","browser_interaction_policy_generation","browser_mutation_origins","browser_mutation_origins_sha256","browser_contract_id","dispatch_state","attempt_count","max_attempts","started_at","replayed"]},
{"client_method":"start_agent_run","http_method":"POST","path":"/api/v1/runs","auth":"access_token","required_scope":"agents:run","required_headers":["Idempotency-Key"],"success_statuses":[200,201,202],"response_headers":["Location","Idempotency-Replayed"],"response_fields":["agent_connection_mode","runtime_transport","runtime_transport_reason","runtime_transport_changed_at","browser_interaction_policy","browser_interaction_policy_generation","browser_mutation_origins","browser_mutation_origins_sha256","browser_contract_id","dispatch_state","attempt_count","max_attempts","started_at","replayed"]},
{"client_method":"get_run","http_method":"GET","path":"/api/v1/runs/{id}","auth":"access_token","required_scope":"runs:read","response_fields":["agent_connection_mode","runtime_transport","runtime_transport_reason","runtime_transport_changed_at","browser_interaction_policy","browser_interaction_policy_generation","browser_mutation_origins","browser_mutation_origins_sha256","browser_contract_id","dispatch_state","attempt_count","max_attempts","started_at","replayed"]},
{"client_method":"list_run_events","http_method":"GET","path":"/api/v1/runs/{id}/events","auth":"access_token","required_scope":"runs:read","response_fields":["items","meta.requested_after_sequence","meta.effective_after_sequence","meta.retained_through_sequence","meta.earliest_available_sequence","meta.latest_available_sequence","meta.retention_gap","meta.terminal","meta.stream_complete"]},
{"client_method":"list_run_children","http_method":"GET","path":"/api/v1/runs/{id}/children","auth":"access_token","required_scope":"runs:read","response_fields":["parent_run_id","items[].child_run_id","items[].caller_agent_id","items[].target_agent_id","items[].status","items[].children"]},
{"client_method":"list_run_artifacts","http_method":"GET","path":"/api/v1/runs/{id}/artifacts","auth":"access_token","required_scope":"runs:read"},
Expand Down
5 changes: 5 additions & 0 deletions src/openlinker/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ class RunResponse(Model):
runtime_transport: str | None = None
runtime_transport_reason: str | None = None
runtime_transport_changed_at: str | None = None
browser_interaction_policy: str | None = None
browser_interaction_policy_generation: int | None = None
browser_mutation_origins: list[str] = jfield(default_factory=list)
browser_mutation_origins_sha256: str | None = None
browser_contract_id: str | None = None
dispatch_state: str = ""
attempt_count: int = 0
max_attempts: int = 0
Expand Down
9 changes: 9 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ async def handler(request: httpx.Request) -> httpx.Response:
"runtime_transport": "long_poll",
"runtime_transport_reason": "websocket_unavailable",
"runtime_transport_changed_at": "2026-07-18T00:00:00Z",
"browser_interaction_policy": "full",
"browser_interaction_policy_generation": 7,
"browser_mutation_origins": ["https://example.com"],
"browser_mutation_origins_sha256": "a" * 64,
"browser_contract_id": "openlinker.browser.v2",
"dispatch_state": "terminal",
"attempt_count": 1,
"max_attempts": 3,
Expand Down Expand Up @@ -99,6 +104,10 @@ async def handler(request: httpx.Request) -> httpx.Response:
assert resp.agent_connection_mode == "runtime"
assert resp.runtime_transport == "long_poll"
assert resp.runtime_transport_reason == "websocket_unavailable"
assert resp.browser_interaction_policy == "full"
assert resp.browser_interaction_policy_generation == 7
assert resp.browser_mutation_origins == ["https://example.com"]
assert resp.browser_contract_id == "openlinker.browser.v2"
assert resp.dispatch_state == "terminal"
assert resp.attempt_count == 1
assert seen["path"] == "/api/v1/run"
Expand Down