Skip to content

Commit 6ac9cd0

Browse files
committed
Document the shared event stream and plugin RPC in the docs site
1 parent 61ca063 commit 6ac9cd0

6 files changed

Lines changed: 146 additions & 1 deletion

File tree

‎docs/guides/events.md‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,42 @@ async with await client.event.subscribe() as events:
4747
async for event in events:
4848
print(event.type)
4949
```
50+
51+
## Shared event stream
52+
53+
`client.events` shares one connection among every subscriber, like `client.event` in the TypeScript client. The
54+
connection opens with the first subscriber and closes when the last one leaves. Each subscriber has its own queue of
55+
up to 4096 events; one that falls further behind fails with `EventOverflowError` without affecting the others. The
56+
latest `server.connected` event is replayed to subscribers that join later.
57+
58+
```python
59+
from opencode.types import SessionIdleEvent
60+
61+
with client.events.of_type(SessionIdleEvent).for_session(session.id) as idle:
62+
client.session.prompt(session.id, text="Hello")
63+
event = idle.get(timeout=300) # typed as SessionIdleEvent
64+
```
65+
66+
- Filters chain: `of_type(*classes)` narrows the yielded type, `for_session(id)` keeps one session's events, and
67+
`where(predicate)` takes any test. Entering the `with` block joins the stream, so nothing published after that is
68+
missed.
69+
- `subscribe(on_activity=callback)` calls `callback` for every chunk received, keepalive comments included, as a
70+
liveness signal.
71+
- `close()` (or leaving the `with` block) ends the subscription, also from another thread. In async code, cancel a wait
72+
with `anyio.fail_after`, `asyncio.timeout` or a trio cancel scope; the subscription stays usable.
73+
- Reconnection is opt-in: `EventHub(client, reconnect=True)` (or `AsyncEventHub`, or a `Reconnect(...)` policy) reopens
74+
the connection with exponential backoff after connection errors, 5xx and 429. Subscribers see `server.connected`
75+
once. **The stream is volatile** (see [Delivery](#delivery)): events published while disconnected are lost, so re-read state through the API
76+
after a reconnect when it matters.
77+
78+
The async hub works the same way under asyncio and trio:
79+
80+
```python
81+
async with client.events.for_session(session.id) as events:
82+
async for event in events:
83+
print(event.type)
84+
```
85+
86+
`client.event.subscribe()` still opens a dedicated connection.
87+
88+
Typed plugin RPC events are covered in the [plugin RPC guide](plugin-rpc.md).

‎docs/guides/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ entry; the common ones also have a guide. CI fails if a group on the client is m
2828
| `client.provider` | Model providers | - | [`ProviderResource`](../reference/resources.md#opencode.resources.provider.ProviderResource) |
2929
| `client.pty` | Terminals | [Interactive terminals](terminals.md) | [`PtyResource`](../reference/resources.md#opencode.resources.pty.PtyResource) |
3030
| `client.reference` | References | - | [`ReferenceResource`](../reference/resources.md#opencode.resources.reference.ReferenceResource) |
31-
| `client.rpc` | Plugin RPC | - | [`RpcResource`](../reference/resources.md#opencode.resources.rpc.RpcResource) |
31+
| `client.rpc` | Plugin RPC | [Plugin RPC](plugin-rpc.md) | [`RpcResource`](../reference/resources.md#opencode.resources.rpc.RpcResource) |
3232
| `client.server` | Server info and health | - | [`ServerResource`](../reference/resources.md#opencode.resources.server.ServerResource) |
3333
| `client.session` | Sessions, prompts and their sub-resources | [Guide](sessions.md) | [`SessionResource`](../reference/resources.md#opencode.resources.session.SessionResource) |
3434
| `client.shell` | Shells | - | [`ShellResource`](../reference/resources.md#opencode.resources.shell.ShellResource) |

‎docs/guides/plugin-rpc.md‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Plugin RPC
2+
3+
Declare a plugin RPC once as a class, then call it with typed inputs and outputs:
4+
5+
```python
6+
from pydantic import BaseModel
7+
from opencode import RpcCallError, RpcDefinition, rpc_event, rpc_method
8+
9+
10+
class AddInput(BaseModel):
11+
amount: int
12+
13+
14+
class Total(BaseModel):
15+
total: int
16+
17+
18+
class Counter(RpcDefinition, id="counter"):
19+
add = rpc_method(AddInput, Total)
20+
reset = rpc_method(None, Total)
21+
changed = rpc_event(Total)
22+
23+
24+
counter = client.rpc(Counter)
25+
try:
26+
print(counter.call(Counter.add, AddInput(amount=2)).total)
27+
except RpcCallError as error:
28+
print(error.type, error.message, error.data)
29+
30+
with counter.subscribe(Counter.changed) as changes: # only rpc.counter.changed, with validated data
31+
for change in changes:
32+
print(change.data.total)
33+
```
34+
35+
Any type pydantic validates works for inputs, outputs and event data. `rpc_method(..., errors={"not_found": Model})`
36+
validates the `data` of declared errors. Failures raise `RpcCallError`; crashes and outputs that do not match the
37+
declared type raise `RpcCallInternalError`. `counter.events()` yields every raw event of the RPC. The untyped
38+
`client.rpc.call(rpc_id, method, input=...)` is still available.
39+
40+
41+
## Async
42+
43+
The async client returns `AsyncRpcClient`, with the same methods:
44+
45+
```python
46+
counter = client.rpc(Counter)
47+
total = await counter.call(Counter.add, AddInput(amount=2))
48+
async with counter.subscribe(Counter.changed) as changes:
49+
async for change in changes:
50+
print(change.data.total)
51+
```
52+
53+
RPC events travel on the [shared event stream](events.md#shared-event-stream), so they share its delivery rules.

‎docs/reference/events.md‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Shared events and plugin RPC
2+
3+
The shared event stream behind `client.events`, and the typed plugin RPC helpers behind `client.rpc(Definition)`.
4+
See the [events guide](../guides/events.md) and the [plugin RPC guide](../guides/plugin-rpc.md).
5+
6+
## Event hubs
7+
8+
::: opencode.EventHub
9+
10+
::: opencode.AsyncEventHub
11+
12+
::: opencode.Subscription
13+
14+
::: opencode.AsyncSubscription
15+
16+
::: opencode.Reconnect
17+
18+
::: opencode.EventOverflowError
19+
20+
::: opencode.SUBSCRIBER_CAPACITY
21+
22+
::: opencode.session_id_of
23+
24+
## Plugin RPC
25+
26+
::: opencode._rpc.TypedRpcResource
27+
28+
::: opencode.RpcDefinition
29+
30+
::: opencode.rpc_method
31+
32+
::: opencode.rpc_event
33+
34+
::: opencode.RpcMethod
35+
36+
::: opencode.RpcEventDefinition
37+
38+
::: opencode.RpcClient
39+
40+
::: opencode.AsyncRpcClient
41+
42+
::: opencode.RpcEvents
43+
44+
::: opencode.AsyncRpcEvents
45+
46+
::: opencode.RpcEventPayload
47+
48+
::: opencode.RpcCallError
49+
50+
::: opencode.RpcCallInternalError

‎docs/reference/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Generated from the docstrings in `src/opencode`.
88
| [Server launcher](server.md) | `OpencodeServer`, `AsyncOpencodeServer` |
99
| [Resources](resources.md) | Every `client.<group>` and its methods |
1010
| [Pagination and streams](pagination.md) | `SyncCursorPage`, `AsyncPaginator`, `Stream`, `AsyncStream` |
11+
| [Shared events and RPC](events.md) | `EventHub`, `Subscription`, `RpcDefinition`, `RpcClient` |
1112
| [Exceptions](exceptions.md) | Status errors and every declared server error |
1213
| [Types](types/index.md) | The models in `opencode.types`, grouped by domain |
1314

‎mkdocs.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ nav:
139139
- Models and unions: guides/models.md
140140
- Pagination: guides/pagination.md
141141
- Events and streaming: guides/events.md
142+
- Plugin RPC: guides/plugin-rpc.md
142143
- Interactive terminals: guides/terminals.md
143144
- Errors: guides/errors.md
144145
- Retries and timeouts: guides/retries.md
@@ -151,6 +152,7 @@ nav:
151152
- Server launcher: reference/server.md
152153
- Resources: reference/resources.md
153154
- Pagination and streams: reference/pagination.md
155+
- Shared events and RPC: reference/events.md
154156
- Terminals: reference/terminals.md
155157
- Exceptions: reference/exceptions.md
156158
- Types: reference/types/index.md

0 commit comments

Comments
 (0)