528d8225b7
- Added error handling callbacks (on_protocol_error, on_listener_error) and error history tracking (protocol_errors, listener_errors properties) for RPC failures. - Added max_event_history and max_stderr_chunks parameters to bound retained history in long-lived hosts, preventing unbounded memory growth. - Added assistant_text_with_thinking() and message_text_with_thinking() helper functions for backward compatibility with thinking block inclusion. - Improved error correlation for id-less parse/unknown-command failures to waiting requests and changed prompt scheduling failures to raise instead of timing out. - Refactored event dispatch to _dispatch_listeners() with exception handling and error recording for robust listener failure management.
172 lines
5.3 KiB
Markdown
172 lines
5.3 KiB
Markdown
# omp-rpc
|
|
|
|
Typed Python bindings for the `omp --mode rpc` protocol used by the coding agent.
|
|
|
|
This package wraps the newline-delimited JSON RPC transport exposed by the CLI and
|
|
provides:
|
|
|
|
- typed command methods for the stable RPC surface
|
|
- typed startup options for common `omp --mode rpc` flags such as thinking level,
|
|
tool selection, prompt appends, provider session IDs, and headless session toggles
|
|
- typed protocol models for state, bash results, compaction, and session stats
|
|
- a process-backed client that manages request correlation over stdio
|
|
- typed per-event listeners plus a typed catch-all notification hook
|
|
- helpers for collecting prompt runs and handling extension UI requests in manual or headless mode
|
|
|
|
## Basic Usage
|
|
|
|
```python
|
|
from omp_rpc import RpcClient
|
|
|
|
with RpcClient(provider="anthropic", model="claude-sonnet-4-5") as client:
|
|
state = client.get_state()
|
|
print(state.model.id if state.model else "no model")
|
|
|
|
turn = client.prompt_and_wait("Reply with just the word hello")
|
|
print(turn.require_assistant_text())
|
|
```
|
|
|
|
The wrapper also exposes the common RPC startup flags directly, so scripts do not
|
|
need to build `extra_args` by hand:
|
|
|
|
```python
|
|
from omp_rpc import RpcClient
|
|
|
|
with RpcClient(
|
|
model="openrouter/anthropic/claude-sonnet-4.6",
|
|
thinking="high",
|
|
no_session=True,
|
|
no_skills=True,
|
|
no_rules=True,
|
|
tools=("read", "edit", "write"),
|
|
append_system_prompt="Focus on reproducible benchmark behavior.",
|
|
) as client:
|
|
print(client.get_state().thinking_level)
|
|
```
|
|
|
|
For orchestration hosts, the wrapper also exposes typed event hooks and a simple
|
|
way to seed todos before the first prompt:
|
|
|
|
```python
|
|
from omp_rpc import MessageUpdateEvent, RpcClient
|
|
|
|
def on_message_update(event: MessageUpdateEvent) -> None:
|
|
assistant_event = event.assistant_message_event
|
|
if assistant_event.get("type") == "text_delta":
|
|
print(assistant_event["delta"], end="", flush=True)
|
|
|
|
with RpcClient(model="openrouter/anthropic/claude-sonnet-4.6", no_session=True) as client:
|
|
client.on_message_update(on_message_update)
|
|
client.set_todos(
|
|
[
|
|
"Map the read and edit tool surface.",
|
|
"Exercise the supported edit paths.",
|
|
"Write concrete findings and gaps.",
|
|
]
|
|
)
|
|
client.prompt_and_wait("Evaluate the current tool behavior.")
|
|
```
|
|
|
|
`set_todos()` accepts either a flat list of todo strings/items or explicit
|
|
phases, and `get_state().todo_phases` returns the typed current todo state.
|
|
|
|
By default the client runs:
|
|
|
|
```bash
|
|
omp --mode rpc
|
|
```
|
|
|
|
You can also point it at a custom command, which is useful inside this repo while
|
|
developing against the Bun entrypoint:
|
|
|
|
```python
|
|
from omp_rpc import RpcClient
|
|
|
|
with RpcClient(
|
|
command=[
|
|
"bun",
|
|
"packages/coding-agent/src/cli.ts",
|
|
"--mode",
|
|
"rpc",
|
|
"--provider",
|
|
"anthropic",
|
|
"--model",
|
|
"claude-sonnet-4-5",
|
|
],
|
|
) as client:
|
|
print(client.get_state().session_id)
|
|
```
|
|
|
|
## Extension UI Requests
|
|
|
|
Extensions in RPC mode can ask the host for input. Those requests are available as
|
|
typed `ExtensionUiRequest` instances:
|
|
|
|
```python
|
|
request = client.next_ui_request(timeout=5.0)
|
|
|
|
if request.method == "confirm":
|
|
client.send_ui_confirmation(request.id, True)
|
|
elif request.method in {"input", "editor"}:
|
|
client.send_ui_value(request.id, "approved")
|
|
```
|
|
|
|
For non-interactive scripts, you can install a default headless policy instead of
|
|
handling every request manually:
|
|
|
|
```python
|
|
with RpcClient(model="anthropic/claude-sonnet-4-5") as client:
|
|
client.install_headless_ui()
|
|
turn = client.prompt_and_wait("needs ui-safe automation")
|
|
print(turn.assistant_text)
|
|
```
|
|
|
|
That helper ignores passive UI notifications (`notify`, `setStatus`, `setWidget`,
|
|
`setTitle`, `set_editor_text`), answers `confirm` with `False`, and cancels
|
|
`select`/`input`/`editor` requests unless you provide explicit values.
|
|
|
|
## Error Handling and Retained History
|
|
|
|
The client now surfaces more of the transport edge cases that the wire protocol
|
|
allows:
|
|
|
|
- id-less `parse` and unknown-command failures are correlated back to the
|
|
waiting request when they can be matched unambiguously
|
|
- late `prompt` / `abort_and_prompt` scheduling failures cause
|
|
`prompt_and_wait()` and `wait_for_idle()` to raise instead of timing out
|
|
- unmatched background error responses are exposed through
|
|
`client.protocol_errors` and `client.on_protocol_error(...)`
|
|
- listener exceptions no longer kill the stdout reader thread; they are exposed
|
|
through `client.listener_errors` and `client.on_listener_error(...)`
|
|
|
|
For long-lived hosts, retained event and stderr history is bounded by default:
|
|
|
|
```python
|
|
from omp_rpc import RpcClient
|
|
|
|
with RpcClient(max_event_history=20_000, max_stderr_chunks=256) as client:
|
|
...
|
|
```
|
|
|
|
If a single prompt streams more events than `max_event_history` allows,
|
|
`prompt_and_wait()` raises a clear error so hosts can increase the limit instead
|
|
of silently losing earlier events.
|
|
|
|
## Text Helpers
|
|
|
|
`assistant_text()` and `message_text()` now return visible text blocks only.
|
|
If a host explicitly needs reasoning text too, use the `*_with_thinking`
|
|
helpers:
|
|
|
|
```python
|
|
from omp_rpc import assistant_text, assistant_text_with_thinking
|
|
|
|
visible = assistant_text(message)
|
|
full = assistant_text_with_thinking(message)
|
|
```
|
|
|
|
## Protocol Reference
|
|
|
|
The canonical wire protocol still lives in the repo at
|
|
[`docs/rpc.md`](../../docs/rpc.md).
|