# opencode-python-sdk > Typed Python client for the OpenCode 2.x server API. --- Source: https://shrijayan.github.io/opencode-python-sdk/
vunknown for OpenCode server 2.1.2 # Drive OpenCode from Python, fully typed.

All 142 operations of the OpenCode server API, sync and async, with pydantic models for every request, response and event. Generated from the same contract as the official TypeScript client.

```bash pip install opencode-python-sdk ```
[![PyPI](https://img.shields.io/pypi/v/opencode-python-sdk?color=d9730d&label=pypi)](https://pypi.org/project/opencode-python-sdk/) [![Python](https://img.shields.io/pypi/pyversions/opencode-python-sdk?color=0f6e6a)](https://pypi.org/project/opencode-python-sdk/) [![CI](https://github.com/shrijayan/opencode-python-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/shrijayan/opencode-python-sdk/actions/workflows/ci.yml)
[Get started](getting-started/index.md){ .md-button .md-button--primary } [API reference](reference/index.md){ .md-button }
=== "Sync" ```python from opencode import OpencodeServer with OpencodeServer(cwd="/path/to/repo") as server: # (1)! client = server.client() session = client.session.create(title="Review") client.session.prompt(session.id, text="Summarize the open TODOs") client.session.wait(session.id) for message in client.message.list(session.id): # (2)! print(message.type) ``` 1. Starts `opencode serve` on a free port with a random password, and stops it on exit. 2. Iterating a page fetches the following pages as needed. === "Async" ```python import asyncio from opencode import AsyncOpencode async def main() -> None: async with AsyncOpencode("http://127.0.0.1:4096") as client: session = await client.session.create(title="Review") await client.session.prompt(session.id, text="Hello") async for message in client.message.list(session.id): print(message.type) asyncio.run(main()) # (1)! ``` 1. Works with asyncio and trio.
01 ### Complete Every operation of the OpenCode 2.1 API, on `Opencode` and `AsyncOpencode`, with 96 typed event classes.
02 ### Generated from the source The contract is exported from OpenCode's own Effect `HttpApi`, so names and shapes match the TypeScript client.
03 ### Safe retries Only retries when a request cannot have changed server state. A prompt is never sent twice.
04 ### Forward compatible New fields, enum values, union variants and events from newer servers are kept, not rejected.
05 ### Typed errors Every error the server declares is its own exception class with a typed body.
06 ### Batteries included Launch `opencode serve`, paginate lists, and stream server-sent events with one call each.
## Where to next { .oc-section-title } - [Connect to a server](getting-started/connect.md) you already run, or [start one from Python](getting-started/server.md). - Read the [sessions and prompts guide](guides/sessions.md) for the core workflow. - Browse the [API reference](reference/index.md), or search it with ++slash++. - Using an LLM? Point it at [`llms.txt`](https://shrijayan.github.io/opencode-python-sdk/llms.txt) or [`llms-full.txt`](https://shrijayan.github.io/opencode-python-sdk/llms-full.txt). !!! note "Unofficial" This package is community-maintained and not affiliated with or endorsed by the OpenCode team. If the OpenCode maintainers want to take it over, the repository and the PyPI name will be transferred. --- Source: https://shrijayan.github.io/opencode-python-sdk/getting-started/ # Install ```bash pip install opencode-python-sdk ``` === "uv" ```bash uv add opencode-python-sdk ``` === "poetry" ```bash poetry add opencode-python-sdk ``` The distribution is called `opencode-python-sdk`; the package imports as `opencode`. It needs Python 3.10 or newer and depends only on `httpx`, `pydantic`, `anyio` and `typing-extensions`. ```python import opencode print(opencode.__version__) # this package print(opencode.API_VERSION) # the OpenCode server version it was generated from ``` To start a server from Python with [`OpencodeServer`](server.md), install the [OpenCode CLI](https://opencode.ai) as well, so that `opencode` is on `PATH`. ## Next - [Connect to a server](connect.md) that is already running. - [Run a server from Python](server.md) for scripts, tests and embedded use. - [Use the background service](service.md) to share one `opencode serve --service` between programs. --- Source: https://shrijayan.github.io/opencode-python-sdk/getting-started/connect/ # Connect to a server Start a server with the CLI, then point a client at it: ```bash OPENCODE_PASSWORD=secret opencode serve --port 4096 ``` ```python from opencode import Opencode client = Opencode("http://127.0.0.1:4096", password="secret") print(client.server.info().version) ``` ## Environment variables `base_url` and `password` fall back to environment variables, so the shortest form is `Opencode()`. | Argument | Variable | | --- | --- | | `base_url` | `OPENCODE_BASE_URL` | | `password` | `OPENCODE_PASSWORD`, then `OPENCODE_SERVER_PASSWORD` | `OPENCODE_PASSWORD` is the same variable the OpenCode CLI reads. Pass `password=""` to send no credentials. ## Closing the client The client owns an `httpx.Client` connection pool. Use it as a context manager, or call `close()`: ```python with Opencode() as client: client.agent.list() ``` ## Calling the API Methods mirror the official TypeScript client: `client..()`, in `snake_case`. Path parameters are positional; everything else is keyword-only. ```python client.session.get("ses_...") client.session.prompt("ses_...", text="Fix the failing test", resume=True) client.session.revert.stage("ses_...", message_id="msg_...") client.mcp.list() ``` Every method is listed under [Resources](../reference/resources.md). ### Optional and null arguments The server distinguishes a field that is absent from a field set to `null`. Optional arguments default to `NOT_GIVEN` and are left out of the request; passing `None` sends `null`. ```python client.session.update("ses_...", title="New title") # (1)! client.session.list(parent_id=None) # (2)! ``` 1. Sends `{"title": "New title"}`. 2. Sends `parentID=null`, which returns only root sessions. --- Source: https://shrijayan.github.io/opencode-python-sdk/getting-started/server/ # Run a server from Python `OpencodeServer` runs `opencode serve` as a child process, on a free local port with a random password, for the lifetime of a `with` block. ```python from opencode import OpencodeServer server = OpencodeServer( cwd="/path/to/repo", # (1)! isolated=True, # (2)! config={"model": "anthropic/claude-sonnet-5-5"}, # (3)! ) with server: client = server.client() print(client.server.info().version) ``` 1. The server's working directory, which becomes its default project. 2. Ignore project `opencode.json`, `AGENTS.md` and Claude Code files on disk, for reproducible runs. 3. Passed to the server as `OPENCODE_CONFIG_CONTENT`. The server runs in `--stdio` mode, so it exits when the Python process dies, even if the `with` block never finishes. Its password reaches the child through `OPENCODE_PASSWORD`, which the server strips from the environment its tools inherit. ## Async `AsyncOpencodeServer` takes the same arguments: ```python from opencode import AsyncOpencodeServer async with AsyncOpencodeServer(cwd="/path/to/repo") as server: async with server.client() as client: await client.session.create(title="Async") ``` ## When it fails to start If `opencode` is missing or the server exits early, `ServerStartError` is raised. Its `output` attribute holds the last lines the server printed. ```python from opencode import OpencodeServer, ServerStartError try: with OpencodeServer() as server: ... except ServerStartError as error: print(error.output) ``` See the [`OpencodeServer` reference](../reference/server.md) for every argument. --- Source: https://shrijayan.github.io/opencode-python-sdk/getting-started/service/ # Use the background service OpenCode can run one shared background server per user, `opencode serve --service`. It registers itself in `$XDG_STATE_HOME/opencode/service.json` (default `~/.local/state/opencode/service.json`) with its URL, PID, version and password. `Opencode.from_service()` connects to it, starting it first if it is not running: ```python from opencode import Opencode with Opencode.from_service(version="2.1.2") as client: # (1)! print(client.server.info().version) ``` 1. Optional. A service running another version is stopped and replaced. `version` can also be a predicate, such as `lambda v: v.startswith("2.")`. Pass `start=False` to only connect to a running service; it raises `ServiceError` when there is none. ## Without a client ```python from opencode import service endpoint = service.discover() # an Endpoint, or None; never starts anything endpoint = service.ensure() # starts the service when needed service.headers(endpoint) # {"authorization": "Basic ..."} for other HTTP clients service.stop(pty="handoff") # hand persistent terminals to the next service ``` Several processes may call `ensure()` at once: each starts at most two attempts, the service's own lock picks a winner, and every caller gets the same service. It gives up after 120 seconds with a `ServiceError` that includes the end of the failed process's stderr. ## Async `await AsyncOpencode.from_service()`, `service.async_discover()`, `service.async_ensure()` and `service.async_stop()` take the same arguments. These helpers follow the TypeScript client's `Service` from `@opencode-ai/client`. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/ # Guide index Every resource group on the client, and where it is documented. Every group has a reference entry; the common ones also have a guide. CI fails if a group on the client is missing from this table (see `tests/test_docs_coverage.py`). | Group | What it covers | Guide | Reference | | --- | --- | --- | --- | | `client.agent` | List and inspect agents | - | [`AgentResource`](../reference/resources.md#opencode.resources.agent.AgentResource) | | `client.command` | List slash commands | - | [`CommandResource`](../reference/resources.md#opencode.resources.command.CommandResource) | | `client.config` | Read and update configuration | - | [`ConfigResource`](../reference/resources.md#opencode.resources.config.ConfigResource) | | `client.credential` | Manage provider credentials | - | [`CredentialResource`](../reference/resources.md#opencode.resources.credential.CredentialResource) | | `client.debug` | Inspect loaded locations | - | [`DebugResource`](../reference/resources.md#opencode.resources.debug.DebugResource) | | `client.event` | Subscribe to server events | [Guide](events.md) | [`EventResource`](../reference/resources.md#opencode.resources.event.EventResource) | | `client.experimental` | Experimental endpoints, such as persistent terminals | - | [`ExperimentalResource`](../reference/resources.md#opencode.resources.experimental.ExperimentalResource) | | `client.file` | Read, list, find and write files | - | [`FileResource`](../reference/resources.md#opencode.resources.file.FileResource) | | `client.form` | List pending forms | - | [`FormResource`](../reference/resources.md#opencode.resources.form.FormResource) | | `client.generate` | One-off text generation | - | [`GenerateResource`](../reference/resources.md#opencode.resources.generate.GenerateResource) | | `client.integration` | Connect and manage integrations | - | [`IntegrationResource`](../reference/resources.md#opencode.resources.integration.IntegrationResource) | | `client.location` | Project locations the server knows | - | [`LocationResource`](../reference/resources.md#opencode.resources.location.LocationResource) | | `client.mcp` | MCP servers and their resources | - | [`McpResource`](../reference/resources.md#opencode.resources.mcp.McpResource) | | `client.message` | Read session messages | [Guide](sessions.md) | [`MessageResource`](../reference/resources.md#opencode.resources.message.MessageResource) | | `client.migration` | Data migrations | - | [`MigrationResource`](../reference/resources.md#opencode.resources.migration.MigrationResource) | | `client.model` | List models | - | [`ModelResource`](../reference/resources.md#opencode.resources.model.ModelResource) | | `client.permission` | Answer and manage permission requests | - | [`PermissionResource`](../reference/resources.md#opencode.resources.permission.PermissionResource) | | `client.plugin` | List plugins | - | [`PluginResource`](../reference/resources.md#opencode.resources.plugin.PluginResource) | | `client.project` | Projects | - | [`ProjectResource`](../reference/resources.md#opencode.resources.project.ProjectResource) | | `client.provider` | Model providers | - | [`ProviderResource`](../reference/resources.md#opencode.resources.provider.ProviderResource) | | `client.pty` | Terminals | [Interactive terminals](terminals.md) | [`PtyResource`](../reference/resources.md#opencode.resources.pty.PtyResource) | | `client.reference` | References | - | [`ReferenceResource`](../reference/resources.md#opencode.resources.reference.ReferenceResource) | | `client.rpc` | Plugin RPC | [Plugin RPC](plugin-rpc.md) | [`RpcResource`](../reference/resources.md#opencode.resources.rpc.RpcResource) | | `client.server` | Server info and health | - | [`ServerResource`](../reference/resources.md#opencode.resources.server.ServerResource) | | `client.session` | Sessions, prompts and their sub-resources; `run` for one-call prompts | [Guide](sessions.md), [headless](headless.md) | [`SessionResource`](../reference/resources.md#opencode.resources.session.SessionResource) | | `client.shell` | Shells | - | [`ShellResource`](../reference/resources.md#opencode.resources.shell.ShellResource) | | `client.skill` | Skills | - | [`SkillResource`](../reference/resources.md#opencode.resources.skill.SkillResource) | | `client.vcs` | Version control status and branches | - | [`VcsResource`](../reference/resources.md#opencode.resources.vcs.VcsResource) | | `client.voice` | Voice input | - | [`VoiceResource`](../reference/resources.md#opencode.resources.voice.VoiceResource) | | `client.websearch` | Web search | - | [`WebsearchResource`](../reference/resources.md#opencode.resources.websearch.WebsearchResource) | | `client.worktree` | Git worktrees | - | [`WorktreeResource`](../reference/resources.md#opencode.resources.worktree.WorktreeResource) | --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/async/ # Async `AsyncOpencode` has the same resources and methods as `Opencode`. Methods return awaitables, list methods return an `AsyncPaginator`, and streams return an `AsyncStream`. It runs on [AnyIO](https://anyio.readthedocs.io), so it works under both asyncio and trio. === "asyncio" ```python import asyncio from opencode import AsyncOpencode async def main() -> None: async with AsyncOpencode("http://127.0.0.1:4096") as client: session = await client.session.create(title="Async") await client.session.prompt(session.id, text="Hello") async for message in client.message.list(session.id): print(message.type) asyncio.run(main()) ``` === "trio" ```python import trio from opencode import AsyncOpencode async def main() -> None: async with AsyncOpencode("http://127.0.0.1:4096") as client: info = await client.server.info() print(info.version) trio.run(main) ``` ## Concurrency One client can serve many concurrent tasks; it shares one connection pool. ```python import asyncio async def titles(client: AsyncOpencode, ids: list[str]) -> list[str]: sessions = await asyncio.gather(*(client.session.get(i) for i in ids)) return [s.title for s in sessions] ``` ## Pages and streams ```python page = await client.session.list(limit=20) # (1)! async for session in client.session.list(): # (2)! ... async with await client.event.subscribe() as events: # (3)! async for event in events: print(event.type) ``` 1. `await` the paginator for the first page. 2. `async for` over it for every item across pages. 3. `subscribe()` returns an `AsyncStream` once awaited. ## aiohttp transport For high-concurrency asyncio code you can swap httpx's networking for [aiohttp](https://docs.aiohttp.org). Install the extra and pass `DefaultAioHttpClient` as `http_client`: ```bash pip install 'opencode-python-sdk[aiohttp]' ``` ```python from opencode import AsyncOpencode, DefaultAioHttpClient async def main() -> None: async with DefaultAioHttpClient() as http, AsyncOpencode("http://127.0.0.1:4096", http_client=http) as client: info = await client.server.info() ``` Retries, timeouts and event streams work the same. aiohttp runs on asyncio only, not trio. The client does not close an `http_client` you pass in, so close it yourself as above. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/directory/ # The project directory An OpenCode server can serve several projects. Most endpoints take a `location` that says which one a request is about. Set a default once on the client: ```python client = Opencode("http://127.0.0.1:4096", directory="/path/to/repo") client.agent.list() # (1)! client.agent.list(location={"directory": "/other"}) # (2)! ``` 1. Sent with `location` = `/path/to/repo`. 2. A per-call `location` overrides the default. Without a default, the server uses its own working directory. With `OpencodeServer`, that is `cwd`; you can also pass `directory=` to `server.client()`. ```python with OpencodeServer(cwd="/repos/api") as server: api = server.client() web = server.client(directory="/repos/web") ``` --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/errors/ # Errors Every exception derives from `opencode.OpencodeError`. Errors the server declares are raised as their own class, with a typed body on `.error`: ```python import opencode try: client.session.get("ses_missing") except opencode.SessionNotFoundError as error: # (1)! print(error.error.session_id, error.status_code) except opencode.NotFoundError: # (2)! ... except opencode.APIConnectionError: # (3)! ... ``` 1. A declared error: `error.error` is a typed `SessionNotFoundErrorBody`. 2. Any other 404. 3. The server could not be reached, or the request timed out. ## Hierarchy ```text OpencodeError ├── ServerStartError OpencodeServer could not start └── APIError ├── APIConnectionError no response │ └── APITimeoutError ├── APIResponseValidationError unexpected response body └── APIStatusError any non-2xx response ├── BadRequestError 400 ├── AuthenticationError 401 ├── PermissionDeniedError 403 ├── NotFoundError 404 ├── ConflictError 409 ├── ContentTooLargeError 413 ├── UnprocessableEntityError 422 ├── RateLimitError 429 └── InternalServerError 5xx ├── NotImplementedServerError 501 └── ServiceUnavailableError 503 ``` Declared errors such as `SessionNotFoundError` or `SessionBusyError` subclass the status class that matches their HTTP status, so catching `NotFoundError` also catches `SessionNotFoundError`. Each declared error is documented on the method that can raise it, under **Raises**. The full list is in the [exceptions reference](../reference/exceptions.md). ## What an `APIStatusError` carries | Attribute | Value | | --- | --- | | `status_code` | The HTTP status | | `response` | The `httpx.Response` | | `request` | The `httpx.Request` | | `body` | The decoded JSON body, or text | | `error_tag` | The server's `_tag`, which may be one this client does not know yet | | `request_id` | The `x-request-id` header, when the server sends one | --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/events/ # Events and streaming `client.event.subscribe()` opens a server-sent event stream of everything happening on the server. ```python with client.event.subscribe() as events: # (1)! for event in events: print(event.type) ``` 1. The context manager closes the HTTP connection when you stop reading. Events are typed (`SessionCreatedEvent`, `SessionTextDeltaEvent` and so on; see [Events](../reference/types/events.md)). Narrow them with `match`: ```python from opencode.types import SessionTextDeltaEvent, SessionIdleEvent with client.event.subscribe() as events: for event in events: match event: case SessionTextDeltaEvent(): print(event.to_dict()) case SessionIdleEvent(): break case _: pass ``` Event types this client version does not know arrive as `UnknownEvent`, with all their fields kept. ## Timeouts Streams only apply the connect timeout by default, because they stay open between events. Pass `timeout=` to bound a stream. ## Delivery The event stream is volatile by contract. A slow consumer overflows and fails the stream, and events sent while you are disconnected are lost. Keep the loop body fast, and after reconnecting, read current state (for example with `client.message.list`) rather than assuming you saw every event. ## Async ```python async with await client.event.subscribe() as events: async for event in events: print(event.type) ``` ## Shared event stream `client.events` shares one connection among every subscriber, like `client.event` in the TypeScript client. The connection opens with the first subscriber and closes when the last one leaves. Each subscriber has its own queue of up to 4096 events; one that falls further behind fails with `EventOverflowError` without affecting the others. The latest `server.connected` event is replayed to subscribers that join later. ```python from opencode.types import SessionIdleEvent with client.events.of_type(SessionIdleEvent).for_session(session.id) as idle: client.session.prompt(session.id, text="Hello") event = idle.get(timeout=300) # typed as SessionIdleEvent ``` - Filters chain: `of_type(*classes)` narrows the yielded type, `for_session(id)` keeps one session's events, and `where(predicate)` takes any test. Entering the `with` block joins the stream, so nothing published after that is missed. - `subscribe(on_activity=callback)` calls `callback` for every chunk received, keepalive comments included, as a liveness signal. - `close()` (or leaving the `with` block) ends the subscription, also from another thread. In async code, cancel a wait with `anyio.fail_after`, `asyncio.timeout` or a trio cancel scope; the subscription stays usable. - Reconnection is opt-in: `EventHub(client, reconnect=True)` (or `AsyncEventHub`, or a `Reconnect(...)` policy) reopens the connection with exponential backoff after connection errors, 5xx and 429. Subscribers see `server.connected` once. **The stream is volatile** (see [Delivery](#delivery)): events published while disconnected are lost, so re-read state through the API after a reconnect when it matters. The async hub works the same way under asyncio and trio: ```python async with client.events.for_session(session.id) as events: async for event in events: print(event.type) ``` `client.event.subscribe()` still opens a dedicated connection. Typed plugin RPC events are covered in the [plugin RPC guide](plugin-rpc.md). --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/headless/ # Running agents headless The common embedded use of OpenCode is: start the server, wait until the agents you configured have loaded, run one prompt per unit of work, and read the result. Two helpers cover that on both clients. ## Wait until the server is ready Agents load and MCP servers connect in the background after `opencode serve` starts listening, so the first requests can arrive before your agent exists. `wait_until_ready` polls until everything you name is there: ```python client.wait_until_ready(agents=["reviewer"], mcp_servers=["github"], timeout=60) ``` It treats connection errors and `503` answers as "still booting". After `timeout` seconds it raises `TimeoutError` naming what is still missing. ## Run a prompt to completion ```python from opencode.types import ModelRef result = client.session.run( "Review the staged changes", agent="reviewer", model=ModelRef(provider_id="anthropic", id="claude-sonnet-5-5"), timeout=600, ) print(result.outcome) # "succeeded", "failed" or "interrupted" print(result.text) # text of the last assistant message print(result.cost, result.tokens.input, result.tokens.output) ``` `run` creates a session with the agent and model, sends the prompt, blocks on `session.wait` until the session is idle, then returns a [`opencode.lib.RunResult`](../reference/testing.md#opencode.lib.RunResult): - `messages`: the messages this run added, oldest first, starting with the prompt - `text`, `outcome`, `error`: the final answer and how the run ended - `cost`, `tokens`: usage of this run; `session.cost` and `session.tokens` are the session totals Set `model` together with `agent` when creating a session: otherwise the session can run on a different model than the agent's. Pass `session_id=` to continue a session. `agent` and `model` then switch the session before the prompt, and `messages` holds only the new messages. ### Interrupts If waiting ends early, through `KeyboardInterrupt`, cancelling the task (async), the `timeout`, or a connection error, `run` calls `session.interrupt` before re-raising, so the agent does not keep running on the server. ### Files `files` takes `PromptInputFileAttachment`s. Images and other inline content can be `data:` URLs: ```python import base64 from opencode.types import PromptInputFileAttachment png = base64.b64encode(open("screenshot.png", "rb").read()).decode() client.session.run( "What is wrong in this screenshot?", files=[PromptInputFileAttachment(uri=f"data:image/png;base64,{png}", name="screenshot.png")], ) ``` ## Async The same helpers exist on `AsyncOpencode`: ```python async with AsyncOpencode(url, password=password) as client: await client.wait_until_ready(agents=["build"]) result = await client.session.run("Summarize README.md", agent="build") ``` --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/httpx/ # Advanced httpx config The client is built on [httpx](https://www.python-httpx.org). Pass your own `httpx.Client` (or `httpx.AsyncClient`) for proxies, custom CA bundles, transports or event hooks. You keep ownership and must close it. ```python import httpx from opencode import Opencode client = Opencode( "https://opencode.internal.example.com", http_client=httpx.Client(proxy="http://proxy:8080", verify="/etc/ssl/ca.pem"), default_headers={"X-Team": "platform"}, ) ``` ## Per-request extras Every method takes `extra_headers`, `extra_query` and `extra_body`, merged into that request. Use them to reach server features this client version does not model yet. ```python client.session.create( title="t", extra_headers={"X-Trace": "1"}, extra_body={"future": True}, ) ``` ## Derived clients `with_options()` returns a client with changed settings that shares the same connection pool: ```python fast = client.with_options(timeout=5.0, max_retries=0) ``` --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/logging/ # Logging The client logs to the `opencode` logger with the standard `logging` module. It is silent unless you configure it. ```python import logging logging.basicConfig() logging.getLogger("opencode").setLevel(logging.DEBUG) ``` | Level | What is logged | | --- | --- | | `DEBUG` | Every request and response: method, URL, attempt, status | | `INFO` | Retries, with the delay and the reason | Credentials are never logged. The password only travels in the `Authorization` header and never appears in `repr()`, log lines or exception messages. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/models/ # Models and unions Responses are [pydantic](https://docs.pydantic.dev) v2 models, defined in `opencode.types`. Attributes are `snake_case`; the wire names stay available as aliases, so a model validates server JSON and serializes back to it unchanged. ```python from opencode.types import ModelRef session = client.session.create(model=ModelRef(id="claude-sonnet-5-5", provider_id="anthropic")) session.project_id # (1)! session.to_dict() # (2)! session.to_json(indent=2) ``` 1. Attribute access in `snake_case`. 2. `{"id": "...", "projectID": "...", ...}`: wire names, unset fields left out. Requests accept either model instances or plain dicts with wire names. ## Discriminated unions Unions are discriminated on their `type` field, so `isinstance` and `match` narrow them, and type checkers follow: ```python from opencode.types import SessionMessageAssistant, SessionMessageUser for message in client.message.list(session.id): match message: case SessionMessageUser(text=text): print("user:", text) case SessionMessageAssistant(): print("assistant replied") case _: pass # (1)! ``` 1. Always keep a fallback arm: it also catches variants added by newer servers. ## Forward compatibility A client generated from an older contract keeps working against a newer server: - **New fields** are kept on the model and available as attributes and through `model_extra`. - **New enum values** are accepted as plain strings. - **New union variants** are parsed into an `Unknown...` fallback model with all their fields, rather than failing. - **New event types** arrive as `UnknownEvent`. Validation errors on a response are raised as `APIResponseValidationError`, and report field locations and reasons without values. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/pagination/ # Pagination List endpoints with cursors return a page. Iterating a page fetches the following pages as needed. ```python for session in client.session.list(limit=50): # (1)! print(session.title) ``` 1. `limit` is the page size, not a cap on the total. To work page by page: ```python page = client.session.list(limit=50) page.data # this page only page.has_next_page() next_page = page.next_page() for page in client.session.list(limit=50).iter_pages(): print(len(page.data)) ``` ## Async Async list methods return an `AsyncPaginator`. `await` it for the first page, or `async for` over it for every item: ```python first = await client.session.list(limit=50) async for session in client.session.list(limit=50): print(session.title) ``` Endpoints without cursors return a plain list or a response model. The [resources reference](../reference/resources.md) shows each method's return type. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/plugin-rpc/ # Plugin RPC Declare a plugin RPC once as a class, then call it with typed inputs and outputs: ```python from pydantic import BaseModel from opencode import RpcCallError, RpcDefinition, rpc_event, rpc_method class AddInput(BaseModel): amount: int class Total(BaseModel): total: int class Counter(RpcDefinition, id="counter"): add = rpc_method(AddInput, Total) reset = rpc_method(None, Total) changed = rpc_event(Total) counter = client.rpc(Counter) try: print(counter.call(Counter.add, AddInput(amount=2)).total) except RpcCallError as error: print(error.type, error.message, error.data) with counter.subscribe(Counter.changed) as changes: # only rpc.counter.changed, with validated data for change in changes: print(change.data.total) ``` Any type pydantic validates works for inputs, outputs and event data. `rpc_method(..., errors={"not_found": Model})` validates the `data` of declared errors. Failures raise `RpcCallError`; crashes and outputs that do not match the declared type raise `RpcCallInternalError`. `counter.events()` yields every raw event of the RPC. The untyped `client.rpc.call(rpc_id, method, input=...)` is still available. ## Async The async client returns `AsyncRpcClient`, with the same methods: ```python counter = client.rpc(Counter) total = await counter.call(Counter.add, AddInput(amount=2)) async with counter.subscribe(Counter.changed) as changes: async for change in changes: print(change.data.total) ``` RPC events travel on the [shared event stream](events.md#shared-event-stream), so they share its delivery rules. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/retries/ # Retries and timeouts ## Retries The client retries twice by default, with exponential backoff and jitter, and honors `Retry-After` up to 60 seconds. Which requests are retried depends on whether they may already have run: | Method | Retried on | | --- | --- | | `GET`, `PUT`, `DELETE` | Connection errors, 408, 429, 500, 502, 503, 504 | | `POST`, `PATCH` | Only when the request cannot have run: connection could not be opened, 429, 503 | So a prompt is never sent twice because a response was slow. ```python client = Opencode(max_retries=5) client.with_options(max_retries=0).session.prompt(session_id, text="...") ``` ## Timeouts The default is 10 minutes overall with a 10 second connect timeout. Set it per client, per derived client, or per call. A float is seconds; an `httpx.Timeout` gives full control. ```python import httpx client = Opencode(timeout=30.0) client.with_options(timeout=None).session.wait(session_id) # (1)! client.session.get("ses_...", timeout=5.0) # (2)! client = Opencode(timeout=httpx.Timeout(60.0, connect=2.0)) ``` 1. `with_options` returns a client that shares the connection pool. `None` waits forever. 2. Per call. Event streams apply only the connect timeout unless you pass one, because they stay open between events. A timed-out request raises `APITimeoutError`. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/sessions/ # Sessions and prompts A session is one conversation with an agent. The usual loop is: create a session, send a prompt, wait for the agent to go idle, then read the messages. ```python from opencode import Opencode with Opencode() as client: session = client.session.create(title="Fix tests", agent="build") # (1)! client.session.prompt(session.id, text="Fix the failing test in tests/test_api.py") client.session.wait(session.id) # (2)! for message in client.message.list(session.id): print(message.type) ``` 1. `agent` picks one of the server's agents; `client.agent.list()` shows them. 2. Blocks until the agent loop is idle. Long runs may exceed the default timeout; see below. ## Waiting for long runs `wait()` uses the client's timeout (10 minutes by default). To wait as long as it takes: ```python client.with_options(timeout=None).session.wait(session.id) ``` To react to progress instead of blocking, subscribe to [events](events.md). ## Prompt options ```python from opencode.types import PromptInputFileAttachment client.session.prompt( session.id, text="Review this file", files=[PromptInputFileAttachment(...)], # (1)! resume=True, # (2)! ) ``` 1. Files, agents and skills can be attached to a prompt. 2. Resume the agent loop if it was interrupted. ## Managing sessions | Task | Call | | --- | --- | | List sessions | `client.session.list()` | | Rename | `client.session.update(session_id, title=...)` | | Fork at a message | `client.session.fork(session_id, ...)` | | Switch model | `client.session.switch_model(session_id, ...)` | | Stop the agent | `client.session.interrupt(session_id)` | | Compact history | `client.session.compact(session_id)` | | Diff of changes | `client.session.diff(session_id)` | | Delete | `client.session.remove(session_id)` | | Revert changes | `client.session.revert.stage(session_id, message_id=...)` | The full list, with arguments, is in the [`SessionResource` reference](../reference/resources.md#opencode.resources.session.SessionResource). --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/terminals/ # Interactive terminals `client.pty.attach()` opens an interactive connection to a server-side PTY over a WebSocket. It needs the `pty` extra: ```bash pip install "opencode-python-sdk[pty]" ``` ```python pty = client.pty.create(command="/bin/sh").data with client.pty.attach(pty.id) as term: # (1)! term.resize(120, 40) term.send("echo hi\n") for chunk in term: # (2)! print(chunk.decode(errors="replace"), end="") ``` 1. Closing the connection leaves the PTY running; remove it with `client.pty.remove(pty.id)`. 2. Output arrives as `bytes` until the shell exits. `recv(timeout=...)` reads one chunk and returns `None` once the terminal has ended. `term.cursor` is the output position to pass as `cursor=` when you reattach; `cursor=-1` skips the replay of earlier output. ## Persistent terminals Persistent terminals belong to a session and survive server restarts *(experimental)*: ```python info = client.experimental.persistent_pty.create(session.id, command="/bin/sh", args=[], title="sh", env={}) with client.experimental.persistent_pty.attach(info.id, role="controller", on_event=print) as term: term.send("ls\n") print(term.recv(timeout=5)) ``` `role="observer"` watches without sending input, `takeover=True` takes control from another controller, and `attachment_id` identifies this client across reconnects. `on_event` receives the JSON events (`attached`, `replay_complete`, `resized`, `controller_changed`, ...). ## Async The async client returns `AsyncPtyConnection`, which needs asyncio: ```python async with await client.pty.attach(pty.id) as term: await term.send("echo hi\n") async for chunk in term: ... ``` ## Errors A refused handshake (bad ticket, unknown PTY) or an abnormal close raises `PtyConnectionError`, with `status` for the HTTP status and `code` / `reason` for the close frame (`4404` means the terminal was not found, has exited or is unavailable). The same error explains how to install the extra when `websockets` is missing. [`examples/terminal.py`](https://github.com/shrijayan/opencode-python-sdk/blob/main/examples/terminal.py) bridges your local terminal to a remote shell. --- Source: https://shrijayan.github.io/opencode-python-sdk/guides/testing/ # Testing with the fake server `opencode.testing.FakeServer` lets you test code that uses the SDK without starting `opencode serve` or calling a model. It is an `httpx` transport (an `httpx.MockTransport`) that keeps sessions, messages and events in memory and answers with payloads built from the generated models. ```python from opencode import Opencode from opencode.testing import FakeServer def test_review() -> None: fake = FakeServer(reply=lambda prompt: "LGTM" if "diff" in prompt else "?") client = Opencode.fake(fake) # same as Opencode(transport=fake) result = client.session.run("Review this diff", agent="build") assert result.text == "LGTM" assert fake.prompts == ["Review this diff"] ``` `AsyncOpencode.fake(fake)` works the same way. ## What it implements Server info, agents, MCP servers, the event stream, and sessions: create, get, list, remove, switch agent or model, prompt (with `resume=False` queueing the prompt without running it), wait, interrupt and messages, with cursor pagination. Each prompt gets an assistant reply, an idle marker, cost and token usage. Other routes answer `404` with a message naming the route. ## Simulating a slow boot ```python fake = FakeServer(agents=["build"]) fake.add_agent("reviewer", after_polls=3) # listed from the 4th GET /api/agent fake.set_mcp_status("github", "connected", after_polls=2) Opencode.fake(fake).wait_until_ready(agents=["reviewer"], mcp_servers=["github"]) ``` ## Injecting failures ```python fake.fail( "POST", r"/api/session/.+/prompt", status=409, body={"_tag": "SessionBusyError", "sessionID": "ses_1", "message": "busy"}, ) fake.fail("GET", "/api/info", exception=httpx.ConnectError("refused"), times=2) ``` `path` is a regular expression matched against the whole path. `times=None` fails every matching request. ## Inspecting state `fake.requests` records every `httpx.Request`; `fake.sessions`, `fake.messages`, `fake.inbox` and `fake.events` hold the server state and can be changed directly. --- Source: https://shrijayan.github.io/opencode-python-sdk/project/changelog/ # Changelog Releases are published automatically on every merge to `main`. Release notes for each version are on the [GitHub releases page](https://github.com/shrijayan/opencode-python-sdk/releases). ## Unreleased - `client.events`: a shared event stream with one connection for all subscribers, bounded per-subscriber queues, `server.connected` replay, `of_type` / `for_session` / `where` filters, `on_activity` keepalive hook, cancellation, and opt-in reconnection with backoff (`EventHub(client, reconnect=True)`). - `client.rpc(Definition)`: typed plugin RPC calls and event subscriptions declared with `RpcDefinition`, `rpc_method` and `rpc_event`; RPC failures raise `RpcCallError` / `RpcCallInternalError`. - `Stream.on_activity` and `Stream.interrupt()`. - `client.copy(...)`, an alias of `client.with_options(...)`; documented that copies share the connection pool and do not close it. - `OPENCODE_LOG=debug|info` sends the `opencode` logger to stderr at that level. - `DefaultAioHttpClient`: an optional aiohttp transport for `AsyncOpencode`, installed with the `aiohttp` extra (`pip install 'opencode-python-sdk[aiohttp]'`) and passed as `http_client=`. - `.with_raw_response` and `.with_streaming_response` on every resource (sync and async): return an `APIResponse` / `AsyncAPIResponse` with `headers`, `status_code`, `http_response`, `request`, `text`, `json()`, `iter_bytes()` / `iter_lines()` and `parse()` for the usual typed value. - `client.get` / `post` / `put` / `patch` / `delete` for endpoints the client has no method for. ## 0.1.0 First release, generated from OpenCode 2.1.2: sync and async clients for all 142 operations, typed models and events, typed errors, pagination, event streams, retries, and the `OpencodeServer` launcher. --- Source: https://shrijayan.github.io/opencode-python-sdk/project/contributing/ # Contributing Thanks for helping. This project is a generated client with a small hand-written runtime, so most changes fall into one of two places. ## Setup ```bash uv sync uv run pytest -m "not live" ``` Live tests start a real `opencode serve` and need the [OpenCode CLI](https://opencode.ai) on `PATH`. They make no model calls and use a temporary data directory: ```bash uv run pytest -m live ``` ## Checks CI runs all of these; run them before opening a pull request. ```bash uv run ruff check . uv run ruff format --check . uv run pyright uv run mypy uv run python -m codegen --check uv run pytest ``` ## Documentation The site in `docs/` is built with MkDocs Material and mkdocstrings. The API reference is generated from docstrings at build time by `docs/hooks/types_reference.py`. ```bash uv sync --group docs uv run mkdocs serve uv run mkdocs build --strict ``` ### Docs must keep up with the code A change to the public API is not done until the docs cover it. CI enforces this: | Check | Where | Fails when | | --- | --- | --- | | `tests/test_docs_coverage.py` | `pytest` job | A resource, method, `opencode.__all__` name or `opencode.types` model is not in the reference; an exported class or one of its public methods has no docstring; a resource group is missing from `docs/guides/index.md`; a guide mentions a method or type that does not exist | | ruff `D1` rules | Lint job | A public function in a public module has no docstring | | `mkdocs build --strict` | Docs workflow | A link or cross-reference is broken | Each failure names what is missing and the file to change. In practice: - **New endpoint or resource group** (from a spec sync): resource and type pages are generated from the code, so they appear on their own. Add a row for a new group to `docs/guides/index.md`. - **New hand-written class or function exported from `opencode`**: give it a docstring and add a `::: opencode.Name` directive to the right page in `docs/reference/`. - **Renamed or removed API**: update every guide that mentions it. ## Layout | Path | What it is | Edit by hand? | | --- | --- | --- | | `codegen/extract/` | Exports the OpenCode contract from `@opencode/protocol` | Yes | | `spec/contract.json` | The exported contract | No, run the extractor | | `codegen/*.py` | The Python code generator | Yes | | `src/opencode/types/`, `src/opencode/resources/`, `src/opencode/_errors.py`, `api.md` | Generated code | No | | `src/opencode/_*.py` (other) | Transport, retries, streaming, pagination, launcher | Yes | | `tests/` | Tests | Yes | ## Updating to a new OpenCode version The `Sync OpenCode spec` workflow does this nightly and opens a pull request. To do it locally: ```bash cd codegen/extract bun add --exact --ignore-scripts @opencode/protocol@ effect@ bun run extract.ts cd ../.. uv run python -m codegen ``` Then review the diff of `api.md` and update the README version table if the OpenCode minor version changed. ## Changing generated code Change the generator in `codegen/`, run `uv run python -m codegen`, and commit both the generator change and the regenerated files. Never edit generated files directly; CI will reject the change. ## Releases Releases are automatic. Every merge to `main` that changes `src/`, `spec/` or `pyproject.toml` runs CI, picks the next version from the merged commit subjects, publishes to PyPI and creates a GitHub release with generated notes. Docs, tests and CI changes alone do not release. Use [Conventional Commits](https://www.conventionalcommits.org/) subjects; with rebase merging, every commit lands on `main` as written: | Subject | Bump | | --- | --- | | `fix: handle empty cursor` | patch | | `feat: add session.run helper` | minor | | `feat!: rename Opencode.server` or a `BREAKING CHANGE:` footer | major (minor while below 1.0) | | anything else (`chore:`, `refactor:`, ...) | patch | The version comes from git tags (`hatch-vcs`); there is no version number to edit. To release without package changes, run the Release workflow manually with **force**. --- Source: https://shrijayan.github.io/opencode-python-sdk/project/how-its-built/ # How it's built Most of this package is generated. The models, resources, error classes and `api.md` come from OpenCode's own contract; the transport, retries, streaming, pagination and server launcher are written by hand. ```text @opencode/protocol (npm) ──bun codegen/extract──▶ spec/contract.json ──python -m codegen──▶ src/opencode ``` ## 1. Extract the contract `codegen/extract/extract.ts` loads `ClientApi` from the published `@opencode/protocol` package, the same Effect `HttpApi` definition the official TypeScript client is built from. It exports every endpoint with its inputs, outputs and declared errors, and every schema as JSON Schema, into `spec/contract.json`. Because the contract comes from the server's source of truth rather than a hand-written OpenAPI file, method names and request shapes match the TypeScript client exactly. ## 2. Generate Python `python -m codegen` reads the contract and writes: | Output | What it contains | | --- | --- | | `src/opencode/types/` | pydantic models for every schema, discriminated unions with `Unknown...` fallbacks, open enums | | `src/opencode/resources/` | A sync and an async class per API group, with typed arguments and docstrings | | `src/opencode/_errors.py` | An exception class for every declared server error | | `api.md` | The method table | ## 3. Hand-written runtime | Module | Responsibility | | --- | --- | | `_base_client.py` | Request building, auth, retries with backoff, timeouts | | `_pagination.py` | Cursor pages and the async paginator | | `_streaming.py` | Server-sent event decoding | | `_server.py` | `OpencodeServer` | | `_models.py`, `_types.py` | Model base class, `NOT_GIVEN`, open enums | ## Keeping it current Generated code is committed. CI regenerates it and fails if anything changed, so the committed code always matches `spec/contract.json`. A nightly workflow checks for new `@opencode/protocol` releases and opens a pull request with the regenerated client. See [Contributing](contributing.md) for running it locally. --- Source: https://shrijayan.github.io/opencode-python-sdk/project/security/ # Security policy ## Reporting a vulnerability Please report vulnerabilities privately through [GitHub security advisories](https://github.com/shrijayan/opencode-python-sdk/security/advisories/new). Do not open a public issue. You should receive a reply within a few days. For vulnerabilities in the OpenCode server itself, report to the [OpenCode project](https://github.com/anomalyco/opencode/security). ## Handling of credentials - The server password is only sent in the `Authorization` header. It never appears in `repr()`, logs or exception messages. - Response validation errors report field locations and reasons, not values. - `OpencodeServer` passes the password to the child process through `OPENCODE_PASSWORD`, which the server removes from the environment its tools inherit. --- Source: https://shrijayan.github.io/opencode-python-sdk/project/versioning/ # Versioning and releases | `opencode-python-sdk` | OpenCode server | | --- | --- | | 0.1.x | 2.1.x | `opencode.API_VERSION` is the server version the client was generated from. The package follows [semantic versioning](https://semver.org). Until 1.0, minor versions may contain breaking changes; each is noted in the [changelog](changelog.md). ## Compatibility with newer servers A client keeps working when the server is upgraded within a major version: new fields, enum values, union variants and event types are kept rather than rejected. See [forward compatibility](../guides/models.md#forward-compatibility). ## Releases Releases are published to [PyPI](https://pypi.org/project/opencode-python-sdk/) from GitHub Actions with trusted publishing and build attestations, so no API token is stored in the repository. --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/ # API reference Generated from the docstrings in `src/opencode`. | Page | Contents | | --- | --- | | [Clients](clients.md) | `Opencode`, `AsyncOpencode` | | [Server launcher](server.md) | `OpencodeServer`, `AsyncOpencodeServer` | | [Background service](service.md) | `opencode.service`, `Endpoint`, `ServiceError` | | [Resources](resources.md) | Every `client.` and its methods | | [Pagination and streams](pagination.md) | `SyncCursorPage`, `AsyncPaginator`, `Stream`, `AsyncStream`, `APIResponse` | | [Shared events and RPC](events.md) | `EventHub`, `Subscription`, `RpcDefinition`, `RpcClient` | | [Exceptions](exceptions.md) | Status errors and every declared server error | | [Types](types/index.md) | The models in `opencode.types`, grouped by domain | The method table with HTTP routes is also in [api.md](https://github.com/shrijayan/opencode-python-sdk/blob/main/api.md). --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/clients/ # Clients ::: opencode.Opencode options: members: [with_options, copy, close, wait_until_ready, fake, get, post, put, patch, delete] show_bases: false ::: opencode.AsyncOpencode options: members: [with_options, copy, close, wait_until_ready, fake, get, post, put, patch, delete] show_bases: false ::: opencode.DefaultAioHttpClient options: members: false show_bases: false ## Module constants ::: opencode.API_VERSION ::: opencode.NOT_GIVEN ::: opencode.NotGiven ::: opencode.BaseModel options: members: [to_dict, to_json] --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/events/ # Shared events and plugin RPC The shared event stream behind `client.events`, and the typed plugin RPC helpers behind `client.rpc(Definition)`. See the [events guide](../guides/events.md) and the [plugin RPC guide](../guides/plugin-rpc.md). ## Event hubs ::: opencode.EventHub ::: opencode.AsyncEventHub ::: opencode.Subscription ::: opencode.AsyncSubscription ::: opencode.Reconnect ::: opencode.EventOverflowError ::: opencode.SUBSCRIBER_CAPACITY ::: opencode.session_id_of ## Plugin RPC ::: opencode._rpc.TypedRpcResource ::: opencode.RpcDefinition ::: opencode.rpc_method ::: opencode.rpc_event ::: opencode.RpcMethod ::: opencode.RpcEventDefinition ::: opencode.RpcClient ::: opencode.AsyncRpcClient ::: opencode.RpcEvents ::: opencode.AsyncRpcEvents ::: opencode.RpcEventPayload ::: opencode.RpcCallError ::: opencode.RpcCallInternalError --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/exceptions/ # Exceptions See the [errors guide](../guides/errors.md) for the hierarchy and how to catch them. ## Base and status errors ::: opencode.OpencodeError options: heading_level: 3 ::: opencode.APIError options: heading_level: 3 ::: opencode.APIConnectionError options: heading_level: 3 ::: opencode.APITimeoutError options: heading_level: 3 ::: opencode.APIResponseValidationError options: heading_level: 3 ::: opencode.APIStatusError options: heading_level: 3 ::: opencode.BadRequestError options: heading_level: 3 ::: opencode.AuthenticationError options: heading_level: 3 ::: opencode.PermissionDeniedError options: heading_level: 3 ::: opencode.NotFoundError options: heading_level: 3 ::: opencode.ConflictError options: heading_level: 3 ::: opencode.ContentTooLargeError options: heading_level: 3 ::: opencode.UnprocessableEntityError options: heading_level: 3 ::: opencode.RateLimitError options: heading_level: 3 ::: opencode.InternalServerError options: heading_level: 3 ::: opencode.NotImplementedServerError options: heading_level: 3 ::: opencode.ServiceUnavailableError options: heading_level: 3 ## Declared server errors Raised when the server returns an error it declares in its contract. Each has a typed body on `.error`. ::: opencode.AgentNotFoundError options: heading_level: 3 ::: opencode.CommandExecutionError options: heading_level: 3 ::: opencode.CommandNotFoundError options: heading_level: 3 ::: opencode.ForbiddenError options: heading_level: 3 ::: opencode.FormAlreadySettledError options: heading_level: 3 ::: opencode.FormInvalidAnswerError options: heading_level: 3 ::: opencode.FormNotFoundError options: heading_level: 3 ::: opencode.InstructionEntryValueTooLargeError options: heading_level: 3 ::: opencode.IntegrationAttemptNotFoundError options: heading_level: 3 ::: opencode.IntegrationMethodNotFoundError options: heading_level: 3 ::: opencode.IntegrationNotFoundError options: heading_level: 3 ::: opencode.InvalidCursorError options: heading_level: 3 ::: opencode.InvalidRequestError options: heading_level: 3 ::: opencode.LocationNotFoundError options: heading_level: 3 ::: opencode.McpServerNotFoundError options: heading_level: 3 ::: opencode.MessageNotFoundError options: heading_level: 3 ::: opencode.PermissionNotFoundError options: heading_level: 3 ::: opencode.PermissionSavedNotFoundError options: heading_level: 3 ::: opencode.ProjectNotFoundError options: heading_level: 3 ::: opencode.ProviderNotFoundError options: heading_level: 3 ::: opencode.PtyNotFoundError options: heading_level: 3 ::: opencode.RemoteFileNotFoundError options: heading_level: 3 ::: opencode.RpcError options: heading_level: 3 ::: opencode.RpcInternalError options: heading_level: 3 ::: opencode.SessionBusyError options: heading_level: 3 ::: opencode.SessionNotFoundError options: heading_level: 3 ::: opencode.ShellNotFoundError options: heading_level: 3 ::: opencode.SkillNotFoundError options: heading_level: 3 ::: opencode.UnauthorizedError options: heading_level: 3 ::: opencode.UnknownError options: heading_level: 3 ::: opencode.VcsInitNotSupportedError options: heading_level: 3 ::: opencode.WorktreeError options: heading_level: 3 --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/pagination/ # Pagination and streams ::: opencode.SyncCursorPage ::: opencode.AsyncCursorPage ::: opencode.AsyncPaginator ::: opencode.Stream ::: opencode.AsyncStream ## Raw responses Returned by `with_raw_response` and `with_streaming_response` on every resource. See [Accessing raw response data](https://github.com/shrijayan/opencode-python-sdk#accessing-raw-response-data). ::: opencode.APIResponse ::: opencode.AsyncAPIResponse --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/server/ # Server launcher ::: opencode.OpencodeServer ::: opencode.AsyncOpencodeServer ::: opencode.ServerStartError --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/service/ # Background service The `opencode.service` module, and the `from_service` constructors on [`Opencode`](clients.md) and [`AsyncOpencode`](clients.md). ::: opencode.service options: show_root_heading: false members: [discover, ensure, stop, headers, default_file, async_discover, async_ensure, async_stop] ::: opencode.Endpoint ::: opencode.service.BasicAuth ::: opencode.ServiceError --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/terminals/ # Terminals Connections returned by `client.pty.attach()` and `client.experimental.persistent_pty.attach()`. See the [terminals guide](../guides/terminals.md). ::: opencode.PtyConnection ::: opencode.AsyncPtyConnection ::: opencode.PtyConnectionError --- Source: https://shrijayan.github.io/opencode-python-sdk/reference/testing/ # Helpers and testing ## `session.run` result ::: opencode.lib.RunResult ## Fake server ::: opencode.testing.FakeServer options: members: [add_agent, set_mcp_status, fail, prompts] ::: opencode.testing.DEFAULT_MODEL