+
+
+
v{{ version }} for OpenCode server {{ api_version }}
+
+# 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
+```
+
+
+
+
+[](https://pypi.org/project/opencode-python-sdk/)
+[](https://pypi.org/project/opencode-python-sdk/)
+[](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.
diff --git a/docs/project/changelog.md b/docs/project/changelog.md
new file mode 100644
index 0000000..786b75d
--- /dev/null
+++ b/docs/project/changelog.md
@@ -0,0 +1 @@
+--8<-- "CHANGELOG.md"
diff --git a/docs/project/contributing.md b/docs/project/contributing.md
new file mode 100644
index 0000000..ea38c9b
--- /dev/null
+++ b/docs/project/contributing.md
@@ -0,0 +1 @@
+--8<-- "CONTRIBUTING.md"
diff --git a/docs/project/how-its-built.md b/docs/project/how-its-built.md
new file mode 100644
index 0000000..6ee6a94
--- /dev/null
+++ b/docs/project/how-its-built.md
@@ -0,0 +1,44 @@
+# 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.
diff --git a/docs/project/security.md b/docs/project/security.md
new file mode 100644
index 0000000..a8ada70
--- /dev/null
+++ b/docs/project/security.md
@@ -0,0 +1 @@
+--8<-- "SECURITY.md"
diff --git a/docs/project/versioning.md b/docs/project/versioning.md
new file mode 100644
index 0000000..ec0113b
--- /dev/null
+++ b/docs/project/versioning.md
@@ -0,0 +1,20 @@
+# 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.
diff --git a/docs/reference/clients.md b/docs/reference/clients.md
new file mode 100644
index 0000000..547291b
--- /dev/null
+++ b/docs/reference/clients.md
@@ -0,0 +1,23 @@
+# Clients
+
+::: opencode.Opencode
+ options:
+ members: [with_options, close]
+ show_bases: false
+
+::: opencode.AsyncOpencode
+ options:
+ members: [with_options, close]
+ show_bases: false
+
+## Module constants
+
+::: opencode.API_VERSION
+
+::: opencode.NOT_GIVEN
+
+::: opencode.NotGiven
+
+::: opencode.BaseModel
+ options:
+ members: [to_dict, to_json]
diff --git a/docs/reference/exceptions.md b/docs/reference/exceptions.md
new file mode 100644
index 0000000..e186e2d
--- /dev/null
+++ b/docs/reference/exceptions.md
@@ -0,0 +1,205 @@
+# 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
diff --git a/docs/reference/index.md b/docs/reference/index.md
new file mode 100644
index 0000000..f54eeb5
--- /dev/null
+++ b/docs/reference/index.md
@@ -0,0 +1,15 @@
+# API reference
+
+Generated from the docstrings in `src/opencode`.
+
+| Page | Contents |
+| --- | --- |
+| [Clients](clients.md) | `Opencode`, `AsyncOpencode` |
+| [Server launcher](server.md) | `OpencodeServer`, `AsyncOpencodeServer` |
+| [Resources](resources.md) | Every `client.