diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index a696b6a..154a697 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-alpha.36"
+ ".": "0.1.0-alpha.37"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 3f719fa..79ec5d8 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 26
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-62d8fccba4eb8dc3a80434e0849eab3352e49fb96a718bb7b6d17ed8e582b716.yml
-openapi_spec_hash: 4ff9376cf9634e91731e63fe482ea532
-config_hash: 1ae82c93499b9f0b9ba828b8919f9cb3
+configured_endpoints: 43
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-97b61518d8666ea7cb310af04248e00bcf8dc9753ba3c7e84471df72b3232004.yml
+openapi_spec_hash: a3500531973ad999c350b87c21aa3ab8
+config_hash: 026ef000d34bf2f930e7b41e77d2d3ff
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 16b4106..5fb3b0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,342 +1,41 @@
# Changelog
-## 0.1.0-alpha.36 (2025-08-27)
+## 0.1.0-alpha.37 (2026-01-01)
-Full Changelog: [v0.1.0-alpha.35...v0.1.0-alpha.36](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.35...v0.1.0-alpha.36)
+Full Changelog: [v0.1.0-alpha.36...v0.1.0-alpha.37](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.36...v0.1.0-alpha.37)
### Features
-* **client:** support file upload requests ([c2e1522](https://github.com/sst/opencode-sdk-python/commit/c2e1522ffba596910098e1e58eef7b4d00548e18))
+* **api:** manual updates ([979c43d](https://github.com/sst/opencode-sdk-python/commit/979c43dbc7f72a03ec5c16c02b800cccca95a24e))
+* improve future compat with pydantic v3 ([5d58a79](https://github.com/sst/opencode-sdk-python/commit/5d58a795ce9d8635b311c8eae4d5c719ecf5558a))
### Bug Fixes
-* avoid newer type syntax ([42779eb](https://github.com/sst/opencode-sdk-python/commit/42779eb3d7035b677ef213d6508658dbd56b50bf))
+* **client:** close streams without requiring full consumption ([81dc914](https://github.com/sst/opencode-sdk-python/commit/81dc9145e044aff113827b1bd7bc54f9aeb1db8e))
+* **compat:** compat with `pydantic<2.8.0` when using additional fields ([662246b](https://github.com/sst/opencode-sdk-python/commit/662246b39c07bcac1346f3033f36c4188a80196e))
### Chores
-* **internal:** change ci workflow machines ([14c918e](https://github.com/sst/opencode-sdk-python/commit/14c918ee18edc797d2d8dd1f1d462ffb004a7e89))
-* **internal:** codegen related update ([477ff58](https://github.com/sst/opencode-sdk-python/commit/477ff58aa920fd0c02378ff33c041d061923dee4))
-* **internal:** fix ruff target version ([359b956](https://github.com/sst/opencode-sdk-python/commit/359b95615445c2f675aa3520cb233c19b50dfe31))
-* **internal:** update comment in script ([9ac7cbb](https://github.com/sst/opencode-sdk-python/commit/9ac7cbb6dba85d18d18bd37f95dffc0eea3d2605))
-* **internal:** update pyright exclude list ([5d96f63](https://github.com/sst/opencode-sdk-python/commit/5d96f63f7a9aaebb2e85fcd11b380bbeca3b7310))
-* update @stainless-api/prism-cli to v5.15.0 ([88487ee](https://github.com/sst/opencode-sdk-python/commit/88487ee6dbc8ac60e9d26573c8f08ae3f1389e83))
-* update github action ([fe98742](https://github.com/sst/opencode-sdk-python/commit/fe98742cd53b1e8783f600879164dd16fea610d3))
-
-## 0.1.0-alpha.35 (2025-07-29)
-
-Full Changelog: [v0.1.0-alpha.34...v0.1.0-alpha.35](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.34...v0.1.0-alpha.35)
-
-### Features
-
-* **api:** api update ([06ebf15](https://github.com/sst/opencode-sdk-python/commit/06ebf15e7ed0f782dbf51352a71fc5edb948a93c))
-
-## 0.1.0-alpha.34 (2025-07-28)
-
-Full Changelog: [v0.1.0-alpha.33...v0.1.0-alpha.34](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.33...v0.1.0-alpha.34)
-
-### Features
-
-* **api:** api update ([0bc9251](https://github.com/sst/opencode-sdk-python/commit/0bc92517109d280c22e22639ee4ffa58d63d837b))
-* **api:** api update ([14ada9d](https://github.com/sst/opencode-sdk-python/commit/14ada9d7d1e93d85f357f417633b691b116c8ad5))
-
-## 0.1.0-alpha.33 (2025-07-25)
-
-Full Changelog: [v0.1.0-alpha.32...v0.1.0-alpha.33](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.32...v0.1.0-alpha.33)
-
-### Features
-
-* **api:** api update ([9002768](https://github.com/sst/opencode-sdk-python/commit/9002768edd617a44d4d686dd9e88f41fe6a56f2f))
-
-
-### Chores
-
-* **project:** add settings file for vscode ([7fff9af](https://github.com/sst/opencode-sdk-python/commit/7fff9af8fd66865dc933dce74f0385250377af87))
-
-## 0.1.0-alpha.32 (2025-07-24)
-
-Full Changelog: [v0.1.0-alpha.31...v0.1.0-alpha.32](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.31...v0.1.0-alpha.32)
-
-### Features
-
-* **api:** api update ([988b38c](https://github.com/sst/opencode-sdk-python/commit/988b38ce1d4b7694083abe26f2198463d4555012))
-
-## 0.1.0-alpha.31 (2025-07-24)
-
-Full Changelog: [v0.1.0-alpha.30...v0.1.0-alpha.31](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.30...v0.1.0-alpha.31)
-
-### Features
-
-* **api:** api update ([35553a6](https://github.com/sst/opencode-sdk-python/commit/35553a6e3b3472562cdc38f0399fcd37af1b52e9))
-
-## 0.1.0-alpha.30 (2025-07-23)
-
-Full Changelog: [v0.1.0-alpha.29...v0.1.0-alpha.30](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.29...v0.1.0-alpha.30)
-
-### Bug Fixes
-
-* **parsing:** parse extra field types ([6817656](https://github.com/sst/opencode-sdk-python/commit/6817656ba347e8074960af1526763c134d75cf7d))
-
-## 0.1.0-alpha.29 (2025-07-22)
-
-Full Changelog: [v0.1.0-alpha.28...v0.1.0-alpha.29](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.28...v0.1.0-alpha.29)
-
-### Features
-
-* **api:** api update ([08130f0](https://github.com/sst/opencode-sdk-python/commit/08130f0c068f4008ffda297c68a68a44dec34d95))
-
-## 0.1.0-alpha.28 (2025-07-22)
-
-Full Changelog: [v0.1.0-alpha.27...v0.1.0-alpha.28](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.27...v0.1.0-alpha.28)
-
-### Features
-
-* **api:** api update ([e8022cd](https://github.com/sst/opencode-sdk-python/commit/e8022cd6d313c1c710dc2721f7e962285d48b02e))
-
-## 0.1.0-alpha.27 (2025-07-22)
-
-Full Changelog: [v0.1.0-alpha.26...v0.1.0-alpha.27](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.26...v0.1.0-alpha.27)
-
-### Features
-
-* **api:** api update ([50c887c](https://github.com/sst/opencode-sdk-python/commit/50c887c4202f587317afecb8998181c6de78b7b4))
-
-
-### Bug Fixes
-
-* **parsing:** ignore empty metadata ([8ee35ae](https://github.com/sst/opencode-sdk-python/commit/8ee35ae762cb0ade81b08cc41a9f496afe9fd484))
-
-## 0.1.0-alpha.26 (2025-07-21)
-
-Full Changelog: [v0.1.0-alpha.25...v0.1.0-alpha.26](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.25...v0.1.0-alpha.26)
-
-### Features
-
-* **api:** api update ([827dc0c](https://github.com/sst/opencode-sdk-python/commit/827dc0c780afd217f981cdd31d371fe96327aeec))
-
-## 0.1.0-alpha.25 (2025-07-21)
-
-Full Changelog: [v0.1.0-alpha.24...v0.1.0-alpha.25](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.24...v0.1.0-alpha.25)
-
-### Features
-
-* **api:** api update ([a85f832](https://github.com/sst/opencode-sdk-python/commit/a85f832a942075091b9ca3f7e6399ba00239f354))
-
-## 0.1.0-alpha.24 (2025-07-21)
-
-Full Changelog: [v0.1.0-alpha.23...v0.1.0-alpha.24](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.23...v0.1.0-alpha.24)
-
-### Features
-
-* **api:** api update ([bd6dd48](https://github.com/sst/opencode-sdk-python/commit/bd6dd48f11b23c77802e0a36af94c1a92c0326c7))
-
-## 0.1.0-alpha.23 (2025-07-18)
-
-Full Changelog: [v0.1.0-alpha.22...v0.1.0-alpha.23](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.22...v0.1.0-alpha.23)
-
-### Features
-
-* **api:** api update ([d8c9fc9](https://github.com/sst/opencode-sdk-python/commit/d8c9fc984c48b7dadce8840c5c1e010a508d58b4))
-
-## 0.1.0-alpha.22 (2025-07-17)
-
-Full Changelog: [v0.1.0-alpha.21...v0.1.0-alpha.22](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.21...v0.1.0-alpha.22)
-
-### Features
-
-* **api:** api update ([582070a](https://github.com/sst/opencode-sdk-python/commit/582070ae69b0ae1088271038b0fcb818c30c74cf))
-
-## 0.1.0-alpha.21 (2025-07-17)
-
-Full Changelog: [v0.1.0-alpha.20...v0.1.0-alpha.21](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.20...v0.1.0-alpha.21)
-
-### Features
-
-* **api:** api update ([71211e8](https://github.com/sst/opencode-sdk-python/commit/71211e888ecd5e848ac4de5ed058e4756025f694))
-
-## 0.1.0-alpha.20 (2025-07-17)
-
-Full Changelog: [v0.1.0-alpha.19...v0.1.0-alpha.20](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.19...v0.1.0-alpha.20)
-
-### Features
-
-* **api:** api update ([f48c0d6](https://github.com/sst/opencode-sdk-python/commit/f48c0d6bb1943df3e3758d19b83c70fd1c15e2c2))
-
-## 0.1.0-alpha.19 (2025-07-16)
-
-Full Changelog: [v0.1.0-alpha.18...v0.1.0-alpha.19](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.18...v0.1.0-alpha.19)
-
-### Features
-
-* **api:** api update ([07b8550](https://github.com/sst/opencode-sdk-python/commit/07b8550e658373298ac5d64eed102f21d03a29fa))
-
-## 0.1.0-alpha.18 (2025-07-16)
-
-Full Changelog: [v0.1.0-alpha.17...v0.1.0-alpha.18](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.17...v0.1.0-alpha.18)
-
-### Features
-
-* **api:** api update ([02c3399](https://github.com/sst/opencode-sdk-python/commit/02c3399fb52fa96d50e6dd9c74f3106d1107308e))
-
-## 0.1.0-alpha.17 (2025-07-16)
-
-Full Changelog: [v0.1.0-alpha.16...v0.1.0-alpha.17](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.16...v0.1.0-alpha.17)
-
-### Features
-
-* **api:** api update ([e76b574](https://github.com/sst/opencode-sdk-python/commit/e76b57439c37c0d3514e1497a4d1a78279844bdc))
-
-## 0.1.0-alpha.16 (2025-07-15)
-
-Full Changelog: [v0.1.0-alpha.15...v0.1.0-alpha.16](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.15...v0.1.0-alpha.16)
-
-### Features
-
-* **api:** api update ([670fa88](https://github.com/sst/opencode-sdk-python/commit/670fa889512f9000e6fee8c9f5c2b49434224592))
-
-## 0.1.0-alpha.15 (2025-07-15)
-
-Full Changelog: [v0.1.0-alpha.14...v0.1.0-alpha.15](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.14...v0.1.0-alpha.15)
-
-### Features
-
-* **api:** api update ([88bbf66](https://github.com/sst/opencode-sdk-python/commit/88bbf66c1f6ec7266fccb7f8e3265bb074afd5e6))
-
-## 0.1.0-alpha.14 (2025-07-15)
-
-Full Changelog: [v0.1.0-alpha.13...v0.1.0-alpha.14](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.13...v0.1.0-alpha.14)
-
-### Features
-
-* **api:** api update ([80d8572](https://github.com/sst/opencode-sdk-python/commit/80d85724c6b17b867ac3d19b0741bb88bb604798))
-
-## 0.1.0-alpha.13 (2025-07-15)
-
-Full Changelog: [v0.1.0-alpha.12...v0.1.0-alpha.13](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.12...v0.1.0-alpha.13)
-
-### Features
-
-* **api:** api update ([a51d627](https://github.com/sst/opencode-sdk-python/commit/a51d627f3a39324ca769a688b63c95dc8f5eba35))
-
-## 0.1.0-alpha.12 (2025-07-12)
-
-Full Changelog: [v0.1.0-alpha.11...v0.1.0-alpha.12](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.11...v0.1.0-alpha.12)
-
-### Bug Fixes
-
-* **client:** don't send Content-Type header on GET requests ([d52fbac](https://github.com/sst/opencode-sdk-python/commit/d52fbac0f4e2ae7f3338272eb7075f1401912fe4))
-
-
-### Chores
-
-* **readme:** fix version rendering on pypi ([d7ae516](https://github.com/sst/opencode-sdk-python/commit/d7ae5162cc2346314e69fd7609050d0e97eecf6c))
-
-## 0.1.0-alpha.11 (2025-07-09)
-
-Full Changelog: [v0.1.0-alpha.10...v0.1.0-alpha.11](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.10...v0.1.0-alpha.11)
-
-### Bug Fixes
-
-* **parsing:** correctly handle nested discriminated unions ([ff5c4a1](https://github.com/sst/opencode-sdk-python/commit/ff5c4a14337714858bd0c193a453fc28f011b263))
-
-
-### Chores
-
-* **internal:** bump pinned h11 dep ([6faa22e](https://github.com/sst/opencode-sdk-python/commit/6faa22e132534a89f10a872ead9ce78fd4ab553c))
-* **package:** mark python 3.13 as supported ([5f2edbe](https://github.com/sst/opencode-sdk-python/commit/5f2edbe52d0450a205d69d57e75ee571cabe4b10))
-
-## 0.1.0-alpha.10 (2025-07-06)
-
-Full Changelog: [v0.1.0-alpha.9...v0.1.0-alpha.10](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.9...v0.1.0-alpha.10)
-
-### Features
-
-* **api:** manual updates ([fdab2a9](https://github.com/sst/opencode-sdk-python/commit/fdab2a9ee5b71d90b1c18c00f67e40247efae0e4))
-
-## 0.1.0-alpha.9 (2025-07-05)
-
-Full Changelog: [v0.1.0-alpha.8...v0.1.0-alpha.9](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.8...v0.1.0-alpha.9)
-
-### Features
-
-* **api:** manual updates ([27a53d3](https://github.com/sst/opencode-sdk-python/commit/27a53d3f43455c8420c1501f3995c140f0bf777d))
-
-## 0.1.0-alpha.8 (2025-07-03)
-
-Full Changelog: [v0.1.0-alpha.7...v0.1.0-alpha.8](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.7...v0.1.0-alpha.8)
-
-### Features
-
-* **api:** api update ([6f7ea7f](https://github.com/sst/opencode-sdk-python/commit/6f7ea7f1f813c31e513fbe33d8653fe3e07f7831))
-
-## 0.1.0-alpha.7 (2025-07-02)
-
-Full Changelog: [v0.1.0-alpha.6...v0.1.0-alpha.7](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.6...v0.1.0-alpha.7)
-
-### Features
-
-* **api:** update via SDK Studio ([84628c0](https://github.com/sst/opencode-sdk-python/commit/84628c0bd3cd508832f04db0fd8a6cd5367dddf3))
-
-
-### Chores
-
-* **ci:** change upload type ([f3019c9](https://github.com/sst/opencode-sdk-python/commit/f3019c94cb548e436b2d7d884969a90db4649f80))
-
-## 0.1.0-alpha.6 (2025-06-30)
-
-Full Changelog: [v0.1.0-alpha.5...v0.1.0-alpha.6](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.5...v0.1.0-alpha.6)
-
-### Features
-
-* **api:** update via SDK Studio ([e1cb382](https://github.com/sst/opencode-sdk-python/commit/e1cb382c5391eb135a31ad98c7301c061191c563))
-* **api:** update via SDK Studio ([0985851](https://github.com/sst/opencode-sdk-python/commit/09858518e9312ca72238efd596cc0313927c26e3))
-
-## 0.1.0-alpha.5 (2025-06-30)
-
-Full Changelog: [v0.1.0-alpha.4...v0.1.0-alpha.5](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.4...v0.1.0-alpha.5)
-
-### Bug Fixes
-
-* **ci:** correct conditional ([6a748da](https://github.com/sst/opencode-sdk-python/commit/6a748dadf9df2b27b9c1123dc3ef989213f75090))
-
-
-### Chores
-
-* **ci:** only run for pushes and fork pull requests ([493f7d2](https://github.com/sst/opencode-sdk-python/commit/493f7d2131e0e17fc2128dad40b327e708f64366))
-
-## 0.1.0-alpha.4 (2025-06-27)
-
-Full Changelog: [v0.1.0-alpha.3...v0.1.0-alpha.4](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.3...v0.1.0-alpha.4)
-
-### Features
-
-* **api:** update via SDK Studio ([6a793f7](https://github.com/sst/opencode-sdk-python/commit/6a793f7fd33a34f19656a3e723b61a32b0068a88))
-
-## 0.1.0-alpha.3 (2025-06-27)
-
-Full Changelog: [v0.1.0-alpha.2...v0.1.0-alpha.3](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.2...v0.1.0-alpha.3)
-
-### Features
-
-* **api:** update via SDK Studio ([9ab5a57](https://github.com/sst/opencode-sdk-python/commit/9ab5a570a78b28aa0dfbad5e6302f930f2011fed))
-* **api:** update via SDK Studio ([3e426e4](https://github.com/sst/opencode-sdk-python/commit/3e426e4328bd876b3bc5123e20b9a1b69dd1756d))
-
-## 0.1.0-alpha.2 (2025-06-27)
-
-Full Changelog: [v0.1.0-alpha.1...v0.1.0-alpha.2](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.1...v0.1.0-alpha.2)
-
-### Features
-
-* **api:** update via SDK Studio ([a6cf7c5](https://github.com/sst/opencode-sdk-python/commit/a6cf7c5b2a411503294088428ca7918226eca161))
-
-## 0.1.0-alpha.1 (2025-06-27)
-
-Full Changelog: [v0.0.1-alpha.0...v0.1.0-alpha.1](https://github.com/sst/opencode-sdk-python/compare/v0.0.1-alpha.0...v0.1.0-alpha.1)
-
-### Features
-
-* **api:** update via SDK Studio ([6e58c71](https://github.com/sst/opencode-sdk-python/commit/6e58c71f2372aa3d44c0d30e0309011ef22a9e04))
-* **api:** update via SDK Studio ([06a27a0](https://github.com/sst/opencode-sdk-python/commit/06a27a02713a8d7bb141e1db844c0b7466818a1d))
-* **api:** update via SDK Studio ([e77f059](https://github.com/sst/opencode-sdk-python/commit/e77f05977e808723ca9df84c481a42f601ca4fd1))
-* **api:** update via SDK Studio ([ff05a4a](https://github.com/sst/opencode-sdk-python/commit/ff05a4adf063d98b3434af29069ea513243071e0))
+* bump `httpx-aiohttp` version to 0.1.9 ([92cb13d](https://github.com/sst/opencode-sdk-python/commit/92cb13de6acb1716afc4e51d0d865058d7eabbe0))
+* do not install brew dependencies in ./scripts/bootstrap by default ([74f0fa2](https://github.com/sst/opencode-sdk-python/commit/74f0fa24042592fdaa27e85a58ae7fc299a8e6e3))
+* **internal:** codegen related update ([7a2d45e](https://github.com/sst/opencode-sdk-python/commit/7a2d45e3eaa42261b53f8218c4122594be3faa76))
+* **internal:** codegen related update ([e9891cb](https://github.com/sst/opencode-sdk-python/commit/e9891cbe8377b5a2e3c6c8143a84bfcadef0ab9c))
+* **internal:** codegen related update ([662dd28](https://github.com/sst/opencode-sdk-python/commit/662dd28de7ccb627c3971bc220def866a1ffedb9))
+* **internal:** codegen related update ([59a2645](https://github.com/sst/opencode-sdk-python/commit/59a2645ea4afcfa1e444ddffa8d0cf37ec1e17ee))
+* **internal:** codegen related update ([5ac8002](https://github.com/sst/opencode-sdk-python/commit/5ac8002ba29ecf23fa422d16a1f65b071c9787ed))
+* **internal:** codegen related update ([c2181c0](https://github.com/sst/opencode-sdk-python/commit/c2181c02870aa2fa62c10a911314904999c296f1))
+* **internal:** codegen related update ([a59b154](https://github.com/sst/opencode-sdk-python/commit/a59b154a2d44291e60f0b34f2a39a1849657b5a7))
+* **internal:** codegen related update ([ab5c493](https://github.com/sst/opencode-sdk-python/commit/ab5c4936b874a1e66f6719b03044aead20db01a1))
+* **internal:** codegen related update ([875138a](https://github.com/sst/opencode-sdk-python/commit/875138a43a23834c7a1428e2a4d5c371af3e0299))
+* **internal:** codegen related update ([0ccd3fe](https://github.com/sst/opencode-sdk-python/commit/0ccd3fe9df64a7bff4a66426d3610c03321aee37))
+* **internal:** codegen related update ([568efca](https://github.com/sst/opencode-sdk-python/commit/568efcac0c2684cceaf8f24673f36b94b00be0e3))
+* **internal:** codegen related update ([04093bb](https://github.com/sst/opencode-sdk-python/commit/04093bbe319065b7869da83fbaa77e9a29038e9c))
+* **internal:** codegen related update ([33f3521](https://github.com/sst/opencode-sdk-python/commit/33f3521d9b17fe39b2f13f24f6edd29e3ff26ef9))
+* **internal:** detect missing future annotations with ruff ([932d414](https://github.com/sst/opencode-sdk-python/commit/932d414bd019219a28c392790c5f031313a6b936))
+* **internal:** move mypy configurations to `pyproject.toml` file ([273dffa](https://github.com/sst/opencode-sdk-python/commit/273dffa4ac0653ed7bdb5455af4a5f640c48f91c))
+* **internal:** update pydantic dependency ([6ae1aed](https://github.com/sst/opencode-sdk-python/commit/6ae1aed3aab37a20707d677b943bdd8c5ee3d4bf))
+* sync repo ([9b3134a](https://github.com/sst/opencode-sdk-python/commit/9b3134a27e8d3db940bf76a6bd4b78321047208a))
+* **tests:** simplify `get_platform` test ([0a47e60](https://github.com/sst/opencode-sdk-python/commit/0a47e601d1cbcf879694ce130d9f2e1729a728bb))
+* **types:** change optional parameter type from NotGiven to Omit ([ed48f59](https://github.com/sst/opencode-sdk-python/commit/ed48f59b5316e425d3ae13e9c493848b37ffc03e))
diff --git a/LICENSE b/LICENSE
index 821edeb..47e2776 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2025 opencode
+Copyright 2026 opencode
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
diff --git a/README.md b/README.md
index 0e9425a..6dc0e2a 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[)](https://pypi.org/project/opencode-ai/)
-The Opencode Python library provides convenient access to the Opencode REST API from any Python 3.8+
+The Opencode Python library provides convenient access to the Opencode REST API from any Python 3.9+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
@@ -116,6 +116,31 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ
Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.
+## Nested params
+
+Nested parameters are dictionaries, typed using `TypedDict`, for example:
+
+```python
+from opencode_ai import Opencode
+
+client = Opencode()
+
+response = client.session.prompt(
+ id="id",
+ parts=[
+ {
+ "text": "text",
+ "type": "text",
+ }
+ ],
+ model={
+ "model_id": "modelID",
+ "provider_id": "providerID",
+ },
+)
+print(response.model)
+```
+
## Handling errors
When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `opencode_ai.APIConnectionError` is raised.
@@ -367,7 +392,7 @@ print(opencode_ai.__version__)
## Requirements
-Python 3.8 or higher.
+Python 3.9 or higher.
## Contributing
diff --git a/api.md b/api.md
index a254328..7b123d7 100644
--- a/api.md
+++ b/api.md
@@ -14,32 +14,44 @@ from opencode_ai.types import EventListResponse
Methods:
-- client.event.list() -> EventListResponse
+- client.event.list(\*\*params) -> EventListResponse
+
+# Path
+
+Types:
+
+```python
+from opencode_ai.types import Path
+```
+
+Methods:
+
+- client.path.get(\*\*params) -> Path
# App
Types:
```python
-from opencode_ai.types import (
- App,
- Mode,
- Model,
- Provider,
- AppInitResponse,
- AppLogResponse,
- AppModesResponse,
- AppProvidersResponse,
-)
+from opencode_ai.types import Model, Provider, AppLogResponse, AppProvidersResponse
```
Methods:
-- client.app.get() -> App
-- client.app.init() -> AppInitResponse
- client.app.log(\*\*params) -> AppLogResponse
-- client.app.modes() -> AppModesResponse
-- client.app.providers() -> AppProvidersResponse
+- client.app.providers(\*\*params) -> AppProvidersResponse
+
+# Agent
+
+Types:
+
+```python
+from opencode_ai.types import Agent, AgentListResponse
+```
+
+Methods:
+
+- client.agent.list(\*\*params) -> AgentListResponse
# Find
@@ -60,25 +72,51 @@ Methods:
Types:
```python
-from opencode_ai.types import File, FileReadResponse, FileStatusResponse
+from opencode_ai.types import File, FileNode, FileListResponse, FileReadResponse, FileStatusResponse
```
Methods:
-- client.file.read(\*\*params) -> FileReadResponse
-- client.file.status() -> FileStatusResponse
+- client.file.list(\*\*params) -> FileListResponse
+- client.file.read(\*\*params) -> FileReadResponse
+- client.file.status(\*\*params) -> FileStatusResponse
# Config
Types:
```python
-from opencode_ai.types import Config, KeybindsConfig, McpLocalConfig, McpRemoteConfig, ModeConfig
+from opencode_ai.types import Config, KeybindsConfig, McpLocalConfig, McpRemoteConfig
+```
+
+Methods:
+
+- client.config.get(\*\*params) -> Config
+
+# Command
+
+Types:
+
+```python
+from opencode_ai.types import Command, CommandListResponse
+```
+
+Methods:
+
+- client.command.list(\*\*params) -> CommandListResponse
+
+# Project
+
+Types:
+
+```python
+from opencode_ai.types import Project, ProjectListResponse
```
Methods:
-- client.config.get() -> Config
+- client.project.list(\*\*params) -> ProjectListResponse
+- client.project.current(\*\*params) -> Project
# Session
@@ -86,6 +124,8 @@ Types:
```python
from opencode_ai.types import (
+ AgentPart,
+ AgentPartInput,
AssistantMessage,
FilePart,
FilePartInput,
@@ -94,6 +134,7 @@ from opencode_ai.types import (
FileSource,
Message,
Part,
+ ReasoningPart,
Session,
SnapshotPart,
StepFinishPart,
@@ -110,36 +151,75 @@ from opencode_ai.types import (
SessionListResponse,
SessionDeleteResponse,
SessionAbortResponse,
+ SessionChildrenResponse,
+ SessionCommandResponse,
SessionInitResponse,
+ SessionMessageResponse,
SessionMessagesResponse,
+ SessionPromptResponse,
SessionSummarizeResponse,
)
```
Methods:
-- client.session.create() -> Session
-- client.session.list() -> SessionListResponse
-- client.session.delete(id) -> SessionDeleteResponse
-- client.session.abort(id) -> SessionAbortResponse
-- client.session.chat(id, \*\*params) -> AssistantMessage
-- client.session.init(id, \*\*params) -> SessionInitResponse
-- client.session.messages(id) -> SessionMessagesResponse
-- client.session.revert(id, \*\*params) -> Session
-- client.session.share(id) -> Session
-- client.session.summarize(id, \*\*params) -> SessionSummarizeResponse
-- client.session.unrevert(id) -> Session
-- client.session.unshare(id) -> Session
+- client.session.create(\*\*params) -> Session
+- client.session.update(id, \*\*params) -> Session
+- client.session.list(\*\*params) -> SessionListResponse
+- client.session.delete(id, \*\*params) -> SessionDeleteResponse
+- client.session.abort(id, \*\*params) -> SessionAbortResponse
+- client.session.children(id, \*\*params) -> SessionChildrenResponse
+- client.session.command(id, \*\*params) -> SessionCommandResponse
+- client.session.get(id, \*\*params) -> Session
+- client.session.init(id, \*\*params) -> SessionInitResponse
+- client.session.message(message_id, \*, id, \*\*params) -> SessionMessageResponse
+- client.session.messages(id, \*\*params) -> SessionMessagesResponse
+- client.session.prompt(id, \*\*params) -> SessionPromptResponse
+- client.session.revert(id, \*\*params) -> Session
+- client.session.share(id, \*\*params) -> Session
+- client.session.shell(id, \*\*params) -> AssistantMessage
+- client.session.summarize(id, \*\*params) -> SessionSummarizeResponse
+- client.session.unrevert(id, \*\*params) -> Session
+- client.session.unshare(id, \*\*params) -> Session
+
+## Permissions
+
+Types:
+
+```python
+from opencode_ai.types.session import Permission, PermissionRespondResponse
+```
+
+Methods:
+
+- client.session.permissions.respond(permission_id, \*, id, \*\*params) -> PermissionRespondResponse
# Tui
Types:
```python
-from opencode_ai.types import TuiAppendPromptResponse, TuiOpenHelpResponse
+from opencode_ai.types import (
+ TuiAppendPromptResponse,
+ TuiClearPromptResponse,
+ TuiExecuteCommandResponse,
+ TuiOpenHelpResponse,
+ TuiOpenModelsResponse,
+ TuiOpenSessionsResponse,
+ TuiOpenThemesResponse,
+ TuiShowToastResponse,
+ TuiSubmitPromptResponse,
+)
```
Methods:
- client.tui.append_prompt(\*\*params) -> TuiAppendPromptResponse
-- client.tui.open_help() -> TuiOpenHelpResponse
+- client.tui.clear_prompt(\*\*params) -> TuiClearPromptResponse
+- client.tui.execute_command(\*\*params) -> TuiExecuteCommandResponse
+- client.tui.open_help(\*\*params) -> TuiOpenHelpResponse
+- client.tui.open_models(\*\*params) -> TuiOpenModelsResponse
+- client.tui.open_sessions(\*\*params) -> TuiOpenSessionsResponse
+- client.tui.open_themes(\*\*params) -> TuiOpenThemesResponse
+- client.tui.show_toast(\*\*params) -> TuiShowToastResponse
+- client.tui.submit_prompt(\*\*params) -> TuiSubmitPromptResponse
diff --git a/mypy.ini b/mypy.ini
deleted file mode 100644
index 34af79f..0000000
--- a/mypy.ini
+++ /dev/null
@@ -1,50 +0,0 @@
-[mypy]
-pretty = True
-show_error_codes = True
-
-# Exclude _files.py because mypy isn't smart enough to apply
-# the correct type narrowing and as this is an internal module
-# it's fine to just use Pyright.
-#
-# We also exclude our `tests` as mypy doesn't always infer
-# types correctly and Pyright will still catch any type errors.
-exclude = ^(src/opencode_ai/_files\.py|_dev/.*\.py|tests/.*)$
-
-strict_equality = True
-implicit_reexport = True
-check_untyped_defs = True
-no_implicit_optional = True
-
-warn_return_any = True
-warn_unreachable = True
-warn_unused_configs = True
-
-# Turn these options off as it could cause conflicts
-# with the Pyright options.
-warn_unused_ignores = False
-warn_redundant_casts = False
-
-disallow_any_generics = True
-disallow_untyped_defs = True
-disallow_untyped_calls = True
-disallow_subclassing_any = True
-disallow_incomplete_defs = True
-disallow_untyped_decorators = True
-cache_fine_grained = True
-
-# By default, mypy reports an error if you assign a value to the result
-# of a function call that doesn't return anything. We do this in our test
-# cases:
-# ```
-# result = ...
-# assert result is None
-# ```
-# Changing this codegen to make mypy happy would increase complexity
-# and would not be worth it.
-disable_error_code = func-returns-value,overload-cannot-match
-
-# https://github.com/python/mypy/issues/12162
-[mypy.overrides]
-module = "black.files.*"
-ignore_errors = true
-ignore_missing_imports = true
diff --git a/pyproject.toml b/pyproject.toml
index f5c99e3..b42a659 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,30 +1,32 @@
[project]
name = "opencode-ai"
-version = "0.1.0-alpha.36"
+version = "0.1.0-alpha.37"
description = "The official Python library for the opencode API"
dynamic = ["readme"]
license = "MIT"
authors = [
{ name = "Opencode", email = "support@sst.dev" },
]
+
dependencies = [
- "httpx>=0.23.0, <1",
- "pydantic>=1.9.0, <3",
- "typing-extensions>=4.10, <5",
- "anyio>=3.5.0, <5",
- "distro>=1.7.0, <2",
- "sniffio",
+ "httpx>=0.23.0, <1",
+ "pydantic>=1.9.0, <3",
+ "typing-extensions>=4.10, <5",
+ "anyio>=3.5.0, <5",
+ "distro>=1.7.0, <2",
+ "sniffio",
]
-requires-python = ">= 3.8"
+
+requires-python = ">= 3.9"
classifiers = [
"Typing :: Typed",
"Intended Audience :: Developers",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Operating System :: MacOS",
@@ -39,14 +41,14 @@ Homepage = "https://github.com/sst/opencode-sdk-python"
Repository = "https://github.com/sst/opencode-sdk-python"
[project.optional-dependencies]
-aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"]
+aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"]
[tool.rye]
managed = true
# version pins are in requirements-dev.lock
dev-dependencies = [
"pyright==1.1.399",
- "mypy",
+ "mypy==1.17",
"respx",
"pytest",
"pytest-asyncio",
@@ -56,7 +58,6 @@ dev-dependencies = [
"dirty-equals>=0.6.0",
"importlib-metadata>=6.7.0",
"rich>=13.7.1",
- "nest_asyncio==1.6.0",
"pytest-xdist>=3.6.1",
]
@@ -142,7 +143,7 @@ filterwarnings = [
# there are a couple of flags that are still disabled by
# default in strict mode as they are experimental and niche.
typeCheckingMode = "strict"
-pythonVersion = "3.8"
+pythonVersion = "3.9"
exclude = [
"_dev",
@@ -157,6 +158,58 @@ reportOverlappingOverload = false
reportImportCycles = false
reportPrivateUsage = false
+[tool.mypy]
+pretty = true
+show_error_codes = true
+
+# Exclude _files.py because mypy isn't smart enough to apply
+# the correct type narrowing and as this is an internal module
+# it's fine to just use Pyright.
+#
+# We also exclude our `tests` as mypy doesn't always infer
+# types correctly and Pyright will still catch any type errors.
+exclude = ['src/opencode_ai/_files.py', '_dev/.*.py', 'tests/.*']
+
+strict_equality = true
+implicit_reexport = true
+check_untyped_defs = true
+no_implicit_optional = true
+
+warn_return_any = true
+warn_unreachable = true
+warn_unused_configs = true
+
+# Turn these options off as it could cause conflicts
+# with the Pyright options.
+warn_unused_ignores = false
+warn_redundant_casts = false
+
+disallow_any_generics = true
+disallow_untyped_defs = true
+disallow_untyped_calls = true
+disallow_subclassing_any = true
+disallow_incomplete_defs = true
+disallow_untyped_decorators = true
+cache_fine_grained = true
+
+# By default, mypy reports an error if you assign a value to the result
+# of a function call that doesn't return anything. We do this in our test
+# cases:
+# ```
+# result = ...
+# assert result is None
+# ```
+# Changing this codegen to make mypy happy would increase complexity
+# and would not be worth it.
+disable_error_code = "func-returns-value,overload-cannot-match"
+
+# https://github.com/python/mypy/issues/12162
+[[tool.mypy.overrides]]
+module = "black.files.*"
+ignore_errors = true
+ignore_missing_imports = true
+
+
[tool.ruff]
line-length = 120
output-format = "grouped"
@@ -173,6 +226,8 @@ select = [
"B",
# remove unused imports
"F401",
+ # check for missing future annotations
+ "FA102",
# bare except statements
"E722",
# unused arguments
@@ -195,6 +250,8 @@ unfixable = [
"T203",
]
+extend-safe-fixes = ["FA102"]
+
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead"
diff --git a/requirements-dev.lock b/requirements-dev.lock
index d88557d..059e0e1 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -12,40 +12,45 @@
-e file:.
aiohappyeyeballs==2.6.1
# via aiohttp
-aiohttp==3.12.8
+aiohttp==3.13.2
# via httpx-aiohttp
# via opencode-ai
-aiosignal==1.3.2
+aiosignal==1.4.0
# via aiohttp
-annotated-types==0.6.0
+annotated-types==0.7.0
# via pydantic
-anyio==4.4.0
+anyio==4.12.0
# via httpx
# via opencode-ai
-argcomplete==3.1.2
+argcomplete==3.6.3
# via nox
async-timeout==5.0.1
# via aiohttp
-attrs==25.3.0
+attrs==25.4.0
# via aiohttp
-certifi==2023.7.22
+ # via nox
+backports-asyncio-runner==1.2.0
+ # via pytest-asyncio
+certifi==2025.11.12
# via httpcore
# via httpx
-colorlog==6.7.0
+colorlog==6.10.1
+ # via nox
+dependency-groups==1.3.1
# via nox
-dirty-equals==0.6.0
-distlib==0.3.7
+dirty-equals==0.11
+distlib==0.4.0
# via virtualenv
-distro==1.8.0
+distro==1.9.0
# via opencode-ai
-exceptiongroup==1.2.2
+exceptiongroup==1.3.1
# via anyio
# via pytest
-execnet==2.1.1
+execnet==2.1.2
# via pytest-xdist
-filelock==3.12.4
+filelock==3.19.1
# via virtualenv
-frozenlist==1.6.2
+frozenlist==1.8.0
# via aiohttp
# via aiosignal
h11==0.16.0
@@ -56,80 +61,89 @@ httpx==0.28.1
# via httpx-aiohttp
# via opencode-ai
# via respx
-httpx-aiohttp==0.1.8
+httpx-aiohttp==0.1.9
# via opencode-ai
-idna==3.4
+humanize==4.13.0
+ # via nox
+idna==3.11
# via anyio
# via httpx
# via yarl
-importlib-metadata==7.0.0
-iniconfig==2.0.0
+importlib-metadata==8.7.0
+iniconfig==2.1.0
# via pytest
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
-multidict==6.4.4
+multidict==6.7.0
# via aiohttp
# via yarl
-mypy==1.14.1
-mypy-extensions==1.0.0
+mypy==1.17.0
+mypy-extensions==1.1.0
# via mypy
-nest-asyncio==1.6.0
-nodeenv==1.8.0
+nodeenv==1.9.1
# via pyright
-nox==2023.4.22
-packaging==23.2
+nox==2025.11.12
+packaging==25.0
+ # via dependency-groups
# via nox
# via pytest
-platformdirs==3.11.0
+pathspec==0.12.1
+ # via mypy
+platformdirs==4.4.0
# via virtualenv
-pluggy==1.5.0
+pluggy==1.6.0
# via pytest
-propcache==0.3.1
+propcache==0.4.1
# via aiohttp
# via yarl
-pydantic==2.10.3
+pydantic==2.12.5
# via opencode-ai
-pydantic-core==2.27.1
+pydantic-core==2.41.5
# via pydantic
-pygments==2.18.0
+pygments==2.19.2
+ # via pytest
# via rich
pyright==1.1.399
-pytest==8.3.3
+pytest==8.4.2
# via pytest-asyncio
# via pytest-xdist
-pytest-asyncio==0.24.0
-pytest-xdist==3.7.0
-python-dateutil==2.8.2
+pytest-asyncio==1.2.0
+pytest-xdist==3.8.0
+python-dateutil==2.9.0.post0
# via time-machine
-pytz==2023.3.post1
- # via dirty-equals
respx==0.22.0
-rich==13.7.1
-ruff==0.9.4
-setuptools==68.2.2
- # via nodeenv
-six==1.16.0
+rich==14.2.0
+ruff==0.14.7
+six==1.17.0
# via python-dateutil
-sniffio==1.3.0
- # via anyio
+sniffio==1.3.1
# via opencode-ai
-time-machine==2.9.0
-tomli==2.0.2
+time-machine==2.19.0
+tomli==2.3.0
+ # via dependency-groups
# via mypy
+ # via nox
# via pytest
-typing-extensions==4.12.2
+typing-extensions==4.15.0
+ # via aiosignal
# via anyio
+ # via exceptiongroup
# via multidict
# via mypy
# via opencode-ai
# via pydantic
# via pydantic-core
# via pyright
-virtualenv==20.24.5
+ # via pytest-asyncio
+ # via typing-inspection
+ # via virtualenv
+typing-inspection==0.4.2
+ # via pydantic
+virtualenv==20.35.4
# via nox
-yarl==1.20.0
+yarl==1.22.0
# via aiohttp
-zipp==3.17.0
+zipp==3.23.0
# via importlib-metadata
diff --git a/requirements.lock b/requirements.lock
index 0ab22ae..2a3b675 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -12,28 +12,28 @@
-e file:.
aiohappyeyeballs==2.6.1
# via aiohttp
-aiohttp==3.12.8
+aiohttp==3.13.2
# via httpx-aiohttp
# via opencode-ai
-aiosignal==1.3.2
+aiosignal==1.4.0
# via aiohttp
-annotated-types==0.6.0
+annotated-types==0.7.0
# via pydantic
-anyio==4.4.0
+anyio==4.12.0
# via httpx
# via opencode-ai
async-timeout==5.0.1
# via aiohttp
-attrs==25.3.0
+attrs==25.4.0
# via aiohttp
-certifi==2023.7.22
+certifi==2025.11.12
# via httpcore
# via httpx
-distro==1.8.0
+distro==1.9.0
# via opencode-ai
-exceptiongroup==1.2.2
+exceptiongroup==1.3.1
# via anyio
-frozenlist==1.6.2
+frozenlist==1.8.0
# via aiohttp
# via aiosignal
h11==0.16.0
@@ -43,30 +43,34 @@ httpcore==1.0.9
httpx==0.28.1
# via httpx-aiohttp
# via opencode-ai
-httpx-aiohttp==0.1.8
+httpx-aiohttp==0.1.9
# via opencode-ai
-idna==3.4
+idna==3.11
# via anyio
# via httpx
# via yarl
-multidict==6.4.4
+multidict==6.7.0
# via aiohttp
# via yarl
-propcache==0.3.1
+propcache==0.4.1
# via aiohttp
# via yarl
-pydantic==2.10.3
+pydantic==2.12.5
# via opencode-ai
-pydantic-core==2.27.1
+pydantic-core==2.41.5
# via pydantic
-sniffio==1.3.0
- # via anyio
+sniffio==1.3.1
# via opencode-ai
-typing-extensions==4.12.2
+typing-extensions==4.15.0
+ # via aiosignal
# via anyio
+ # via exceptiongroup
# via multidict
# via opencode-ai
# via pydantic
# via pydantic-core
-yarl==1.20.0
+ # via typing-inspection
+typing-inspection==0.4.2
+ # via pydantic
+yarl==1.22.0
# via aiohttp
diff --git a/scripts/bootstrap b/scripts/bootstrap
index e84fe62..b430fee 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -4,10 +4,18 @@ set -e
cd "$(dirname "$0")/.."
-if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
brew bundle check >/dev/null 2>&1 || {
- echo "==> Installing Homebrew dependencies…"
- brew bundle
+ echo -n "==> Install Homebrew dependencies? (y/N): "
+ read -r response
+ case "$response" in
+ [yY][eE][sS]|[yY])
+ brew bundle
+ ;;
+ *)
+ ;;
+ esac
+ echo
}
fi
diff --git a/scripts/lint b/scripts/lint
index bac00da..7e47726 100755
--- a/scripts/lint
+++ b/scripts/lint
@@ -4,8 +4,13 @@ set -e
cd "$(dirname "$0")/.."
-echo "==> Running lints"
-rye run lint
+if [ "$1" = "--fix" ]; then
+ echo "==> Running lints with --fix"
+ rye run fix:ruff
+else
+ echo "==> Running lints"
+ rye run lint
+fi
echo "==> Making sure it imports"
rye run python -c 'import opencode_ai'
diff --git a/src/opencode/lib/.keep b/src/opencode/lib/.keep
deleted file mode 100644
index 5e2c99f..0000000
--- a/src/opencode/lib/.keep
+++ /dev/null
@@ -1,4 +0,0 @@
-File generated from our OpenAPI spec by Stainless.
-
-This directory can be used to store custom files to expand the SDK.
-It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
\ No newline at end of file
diff --git a/src/opencode_ai/__init__.py b/src/opencode_ai/__init__.py
index 7d8c13c..7debe41 100644
--- a/src/opencode_ai/__init__.py
+++ b/src/opencode_ai/__init__.py
@@ -3,7 +3,7 @@
import typing as _t
from . import types
-from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
+from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given
from ._utils import file_from_path
from ._client import (
Client,
@@ -48,7 +48,9 @@
"ProxiesTypes",
"NotGiven",
"NOT_GIVEN",
+ "not_given",
"Omit",
+ "omit",
"OpencodeError",
"APIError",
"APIStatusError",
diff --git a/src/opencode_ai/_base_client.py b/src/opencode_ai/_base_client.py
index eb05756..63e74df 100644
--- a/src/opencode_ai/_base_client.py
+++ b/src/opencode_ai/_base_client.py
@@ -42,7 +42,6 @@
from ._qs import Querystring
from ._files import to_httpx_files, async_to_httpx_files
from ._types import (
- NOT_GIVEN,
Body,
Omit,
Query,
@@ -57,9 +56,10 @@
RequestOptions,
HttpxRequestFiles,
ModelBuilderProtocol,
+ not_given,
)
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
-from ._compat import PYDANTIC_V2, model_copy, model_dump
+from ._compat import PYDANTIC_V1, model_copy, model_dump
from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
from ._response import (
APIResponse,
@@ -145,9 +145,9 @@ def __init__(
def __init__(
self,
*,
- url: URL | NotGiven = NOT_GIVEN,
- json: Body | NotGiven = NOT_GIVEN,
- params: Query | NotGiven = NOT_GIVEN,
+ url: URL | NotGiven = not_given,
+ json: Body | NotGiven = not_given,
+ params: Query | NotGiven = not_given,
) -> None:
self.url = url
self.json = json
@@ -232,7 +232,7 @@ def _set_private_attributes(
model: Type[_T],
options: FinalRequestOptions,
) -> None:
- if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None:
+ if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None:
self.__pydantic_private__ = {}
self._model = model
@@ -320,7 +320,7 @@ def _set_private_attributes(
client: AsyncAPIClient,
options: FinalRequestOptions,
) -> None:
- if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None:
+ if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None:
self.__pydantic_private__ = {}
self._model = model
@@ -595,7 +595,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques
# we internally support defining a temporary header to override the
# default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response`
# see _response.py for implementation details
- override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN)
+ override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given)
if is_given(override_cast_to):
options.headers = headers
return cast(Type[ResponseT], override_cast_to)
@@ -825,7 +825,7 @@ def __init__(
version: str,
base_url: str | URL,
max_retries: int = DEFAULT_MAX_RETRIES,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.Client | None = None,
custom_headers: Mapping[str, str] | None = None,
custom_query: Mapping[str, object] | None = None,
@@ -1247,9 +1247,12 @@ def patch(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
+ opts = FinalRequestOptions.construct(
+ method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
+ )
return self.request(cast_to, opts)
def put(
@@ -1356,7 +1359,7 @@ def __init__(
base_url: str | URL,
_strict_response_validation: bool,
max_retries: int = DEFAULT_MAX_RETRIES,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.AsyncClient | None = None,
custom_headers: Mapping[str, str] | None = None,
custom_query: Mapping[str, object] | None = None,
@@ -1767,9 +1770,12 @@ async def patch(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
+ opts = FinalRequestOptions.construct(
+ method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options
+ )
return await self.request(cast_to, opts)
async def put(
@@ -1818,8 +1824,8 @@ def make_request_options(
extra_query: Query | None = None,
extra_body: Body | None = None,
idempotency_key: str | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- post_parser: PostParser | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ post_parser: PostParser | NotGiven = not_given,
) -> RequestOptions:
"""Create a dict of type RequestOptions without keys of NotGiven values."""
options: RequestOptions = {}
diff --git a/src/opencode_ai/_client.py b/src/opencode_ai/_client.py
index cabe5b7..da2b42a 100644
--- a/src/opencode_ai/_client.py
+++ b/src/opencode_ai/_client.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import os
-from typing import Any, Union, Mapping
+from typing import TYPE_CHECKING, Any, Mapping
from typing_extensions import Self, override
import httpx
@@ -11,17 +11,17 @@
from . import _exceptions
from ._qs import Querystring
from ._types import (
- NOT_GIVEN,
Omit,
Timeout,
NotGiven,
Transport,
ProxiesTypes,
RequestOptions,
+ not_given,
)
from ._utils import is_given, get_async_library
+from ._compat import cached_property
from ._version import __version__
-from .resources import app, tui, file, find, event, config, session
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import APIStatusError
from ._base_client import (
@@ -30,6 +30,20 @@
AsyncAPIClient,
)
+if TYPE_CHECKING:
+ from .resources import app, tui, file, find, path, agent, event, config, command, project, session
+ from .resources.app import AppResource, AsyncAppResource
+ from .resources.tui import TuiResource, AsyncTuiResource
+ from .resources.file import FileResource, AsyncFileResource
+ from .resources.find import FindResource, AsyncFindResource
+ from .resources.path import PathResource, AsyncPathResource
+ from .resources.agent import AgentResource, AsyncAgentResource
+ from .resources.event import EventResource, AsyncEventResource
+ from .resources.config import ConfigResource, AsyncConfigResource
+ from .resources.command import CommandResource, AsyncCommandResource
+ from .resources.project import ProjectResource, AsyncProjectResource
+ from .resources.session.session import SessionResource, AsyncSessionResource
+
__all__ = [
"Timeout",
"Transport",
@@ -43,23 +57,13 @@
class Opencode(SyncAPIClient):
- event: event.EventResource
- app: app.AppResource
- find: find.FindResource
- file: file.FileResource
- config: config.ConfigResource
- session: session.SessionResource
- tui: tui.TuiResource
- with_raw_response: OpencodeWithRawResponse
- with_streaming_response: OpencodeWithStreamedResponse
-
# client options
def __init__(
self,
*,
base_url: str | httpx.URL | None = None,
- timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -96,15 +100,79 @@ def __init__(
self._default_stream_cls = Stream
- self.event = event.EventResource(self)
- self.app = app.AppResource(self)
- self.find = find.FindResource(self)
- self.file = file.FileResource(self)
- self.config = config.ConfigResource(self)
- self.session = session.SessionResource(self)
- self.tui = tui.TuiResource(self)
- self.with_raw_response = OpencodeWithRawResponse(self)
- self.with_streaming_response = OpencodeWithStreamedResponse(self)
+ @cached_property
+ def event(self) -> EventResource:
+ from .resources.event import EventResource
+
+ return EventResource(self)
+
+ @cached_property
+ def path(self) -> PathResource:
+ from .resources.path import PathResource
+
+ return PathResource(self)
+
+ @cached_property
+ def app(self) -> AppResource:
+ from .resources.app import AppResource
+
+ return AppResource(self)
+
+ @cached_property
+ def agent(self) -> AgentResource:
+ from .resources.agent import AgentResource
+
+ return AgentResource(self)
+
+ @cached_property
+ def find(self) -> FindResource:
+ from .resources.find import FindResource
+
+ return FindResource(self)
+
+ @cached_property
+ def file(self) -> FileResource:
+ from .resources.file import FileResource
+
+ return FileResource(self)
+
+ @cached_property
+ def config(self) -> ConfigResource:
+ from .resources.config import ConfigResource
+
+ return ConfigResource(self)
+
+ @cached_property
+ def command(self) -> CommandResource:
+ from .resources.command import CommandResource
+
+ return CommandResource(self)
+
+ @cached_property
+ def project(self) -> ProjectResource:
+ from .resources.project import ProjectResource
+
+ return ProjectResource(self)
+
+ @cached_property
+ def session(self) -> SessionResource:
+ from .resources.session import SessionResource
+
+ return SessionResource(self)
+
+ @cached_property
+ def tui(self) -> TuiResource:
+ from .resources.tui import TuiResource
+
+ return TuiResource(self)
+
+ @cached_property
+ def with_raw_response(self) -> OpencodeWithRawResponse:
+ return OpencodeWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> OpencodeWithStreamedResponse:
+ return OpencodeWithStreamedResponse(self)
@property
@override
@@ -124,9 +192,9 @@ def copy(
self,
*,
base_url: str | httpx.URL | None = None,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.Client | None = None,
- max_retries: int | NotGiven = NOT_GIVEN,
+ max_retries: int | NotGiven = not_given,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -204,23 +272,13 @@ def _make_status_error(
class AsyncOpencode(AsyncAPIClient):
- event: event.AsyncEventResource
- app: app.AsyncAppResource
- find: find.AsyncFindResource
- file: file.AsyncFileResource
- config: config.AsyncConfigResource
- session: session.AsyncSessionResource
- tui: tui.AsyncTuiResource
- with_raw_response: AsyncOpencodeWithRawResponse
- with_streaming_response: AsyncOpencodeWithStreamedResponse
-
# client options
def __init__(
self,
*,
base_url: str | httpx.URL | None = None,
- timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -257,15 +315,79 @@ def __init__(
self._default_stream_cls = AsyncStream
- self.event = event.AsyncEventResource(self)
- self.app = app.AsyncAppResource(self)
- self.find = find.AsyncFindResource(self)
- self.file = file.AsyncFileResource(self)
- self.config = config.AsyncConfigResource(self)
- self.session = session.AsyncSessionResource(self)
- self.tui = tui.AsyncTuiResource(self)
- self.with_raw_response = AsyncOpencodeWithRawResponse(self)
- self.with_streaming_response = AsyncOpencodeWithStreamedResponse(self)
+ @cached_property
+ def event(self) -> AsyncEventResource:
+ from .resources.event import AsyncEventResource
+
+ return AsyncEventResource(self)
+
+ @cached_property
+ def path(self) -> AsyncPathResource:
+ from .resources.path import AsyncPathResource
+
+ return AsyncPathResource(self)
+
+ @cached_property
+ def app(self) -> AsyncAppResource:
+ from .resources.app import AsyncAppResource
+
+ return AsyncAppResource(self)
+
+ @cached_property
+ def agent(self) -> AsyncAgentResource:
+ from .resources.agent import AsyncAgentResource
+
+ return AsyncAgentResource(self)
+
+ @cached_property
+ def find(self) -> AsyncFindResource:
+ from .resources.find import AsyncFindResource
+
+ return AsyncFindResource(self)
+
+ @cached_property
+ def file(self) -> AsyncFileResource:
+ from .resources.file import AsyncFileResource
+
+ return AsyncFileResource(self)
+
+ @cached_property
+ def config(self) -> AsyncConfigResource:
+ from .resources.config import AsyncConfigResource
+
+ return AsyncConfigResource(self)
+
+ @cached_property
+ def command(self) -> AsyncCommandResource:
+ from .resources.command import AsyncCommandResource
+
+ return AsyncCommandResource(self)
+
+ @cached_property
+ def project(self) -> AsyncProjectResource:
+ from .resources.project import AsyncProjectResource
+
+ return AsyncProjectResource(self)
+
+ @cached_property
+ def session(self) -> AsyncSessionResource:
+ from .resources.session import AsyncSessionResource
+
+ return AsyncSessionResource(self)
+
+ @cached_property
+ def tui(self) -> AsyncTuiResource:
+ from .resources.tui import AsyncTuiResource
+
+ return AsyncTuiResource(self)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncOpencodeWithRawResponse:
+ return AsyncOpencodeWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncOpencodeWithStreamedResponse:
+ return AsyncOpencodeWithStreamedResponse(self)
@property
@override
@@ -285,9 +407,9 @@ def copy(
self,
*,
base_url: str | httpx.URL | None = None,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.AsyncClient | None = None,
- max_retries: int | NotGiven = NOT_GIVEN,
+ max_retries: int | NotGiven = not_given,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -365,47 +487,295 @@ def _make_status_error(
class OpencodeWithRawResponse:
+ _client: Opencode
+
def __init__(self, client: Opencode) -> None:
- self.event = event.EventResourceWithRawResponse(client.event)
- self.app = app.AppResourceWithRawResponse(client.app)
- self.find = find.FindResourceWithRawResponse(client.find)
- self.file = file.FileResourceWithRawResponse(client.file)
- self.config = config.ConfigResourceWithRawResponse(client.config)
- self.session = session.SessionResourceWithRawResponse(client.session)
- self.tui = tui.TuiResourceWithRawResponse(client.tui)
+ self._client = client
+
+ @cached_property
+ def event(self) -> event.EventResourceWithRawResponse:
+ from .resources.event import EventResourceWithRawResponse
+
+ return EventResourceWithRawResponse(self._client.event)
+
+ @cached_property
+ def path(self) -> path.PathResourceWithRawResponse:
+ from .resources.path import PathResourceWithRawResponse
+
+ return PathResourceWithRawResponse(self._client.path)
+
+ @cached_property
+ def app(self) -> app.AppResourceWithRawResponse:
+ from .resources.app import AppResourceWithRawResponse
+
+ return AppResourceWithRawResponse(self._client.app)
+
+ @cached_property
+ def agent(self) -> agent.AgentResourceWithRawResponse:
+ from .resources.agent import AgentResourceWithRawResponse
+
+ return AgentResourceWithRawResponse(self._client.agent)
+
+ @cached_property
+ def find(self) -> find.FindResourceWithRawResponse:
+ from .resources.find import FindResourceWithRawResponse
+
+ return FindResourceWithRawResponse(self._client.find)
+
+ @cached_property
+ def file(self) -> file.FileResourceWithRawResponse:
+ from .resources.file import FileResourceWithRawResponse
+
+ return FileResourceWithRawResponse(self._client.file)
+
+ @cached_property
+ def config(self) -> config.ConfigResourceWithRawResponse:
+ from .resources.config import ConfigResourceWithRawResponse
+
+ return ConfigResourceWithRawResponse(self._client.config)
+
+ @cached_property
+ def command(self) -> command.CommandResourceWithRawResponse:
+ from .resources.command import CommandResourceWithRawResponse
+
+ return CommandResourceWithRawResponse(self._client.command)
+
+ @cached_property
+ def project(self) -> project.ProjectResourceWithRawResponse:
+ from .resources.project import ProjectResourceWithRawResponse
+
+ return ProjectResourceWithRawResponse(self._client.project)
+
+ @cached_property
+ def session(self) -> session.SessionResourceWithRawResponse:
+ from .resources.session import SessionResourceWithRawResponse
+
+ return SessionResourceWithRawResponse(self._client.session)
+
+ @cached_property
+ def tui(self) -> tui.TuiResourceWithRawResponse:
+ from .resources.tui import TuiResourceWithRawResponse
+
+ return TuiResourceWithRawResponse(self._client.tui)
class AsyncOpencodeWithRawResponse:
+ _client: AsyncOpencode
+
def __init__(self, client: AsyncOpencode) -> None:
- self.event = event.AsyncEventResourceWithRawResponse(client.event)
- self.app = app.AsyncAppResourceWithRawResponse(client.app)
- self.find = find.AsyncFindResourceWithRawResponse(client.find)
- self.file = file.AsyncFileResourceWithRawResponse(client.file)
- self.config = config.AsyncConfigResourceWithRawResponse(client.config)
- self.session = session.AsyncSessionResourceWithRawResponse(client.session)
- self.tui = tui.AsyncTuiResourceWithRawResponse(client.tui)
+ self._client = client
+
+ @cached_property
+ def event(self) -> event.AsyncEventResourceWithRawResponse:
+ from .resources.event import AsyncEventResourceWithRawResponse
+
+ return AsyncEventResourceWithRawResponse(self._client.event)
+
+ @cached_property
+ def path(self) -> path.AsyncPathResourceWithRawResponse:
+ from .resources.path import AsyncPathResourceWithRawResponse
+
+ return AsyncPathResourceWithRawResponse(self._client.path)
+
+ @cached_property
+ def app(self) -> app.AsyncAppResourceWithRawResponse:
+ from .resources.app import AsyncAppResourceWithRawResponse
+
+ return AsyncAppResourceWithRawResponse(self._client.app)
+
+ @cached_property
+ def agent(self) -> agent.AsyncAgentResourceWithRawResponse:
+ from .resources.agent import AsyncAgentResourceWithRawResponse
+
+ return AsyncAgentResourceWithRawResponse(self._client.agent)
+
+ @cached_property
+ def find(self) -> find.AsyncFindResourceWithRawResponse:
+ from .resources.find import AsyncFindResourceWithRawResponse
+
+ return AsyncFindResourceWithRawResponse(self._client.find)
+
+ @cached_property
+ def file(self) -> file.AsyncFileResourceWithRawResponse:
+ from .resources.file import AsyncFileResourceWithRawResponse
+
+ return AsyncFileResourceWithRawResponse(self._client.file)
+
+ @cached_property
+ def config(self) -> config.AsyncConfigResourceWithRawResponse:
+ from .resources.config import AsyncConfigResourceWithRawResponse
+
+ return AsyncConfigResourceWithRawResponse(self._client.config)
+
+ @cached_property
+ def command(self) -> command.AsyncCommandResourceWithRawResponse:
+ from .resources.command import AsyncCommandResourceWithRawResponse
+
+ return AsyncCommandResourceWithRawResponse(self._client.command)
+
+ @cached_property
+ def project(self) -> project.AsyncProjectResourceWithRawResponse:
+ from .resources.project import AsyncProjectResourceWithRawResponse
+
+ return AsyncProjectResourceWithRawResponse(self._client.project)
+
+ @cached_property
+ def session(self) -> session.AsyncSessionResourceWithRawResponse:
+ from .resources.session import AsyncSessionResourceWithRawResponse
+
+ return AsyncSessionResourceWithRawResponse(self._client.session)
+
+ @cached_property
+ def tui(self) -> tui.AsyncTuiResourceWithRawResponse:
+ from .resources.tui import AsyncTuiResourceWithRawResponse
+
+ return AsyncTuiResourceWithRawResponse(self._client.tui)
class OpencodeWithStreamedResponse:
+ _client: Opencode
+
def __init__(self, client: Opencode) -> None:
- self.event = event.EventResourceWithStreamingResponse(client.event)
- self.app = app.AppResourceWithStreamingResponse(client.app)
- self.find = find.FindResourceWithStreamingResponse(client.find)
- self.file = file.FileResourceWithStreamingResponse(client.file)
- self.config = config.ConfigResourceWithStreamingResponse(client.config)
- self.session = session.SessionResourceWithStreamingResponse(client.session)
- self.tui = tui.TuiResourceWithStreamingResponse(client.tui)
+ self._client = client
+
+ @cached_property
+ def event(self) -> event.EventResourceWithStreamingResponse:
+ from .resources.event import EventResourceWithStreamingResponse
+
+ return EventResourceWithStreamingResponse(self._client.event)
+
+ @cached_property
+ def path(self) -> path.PathResourceWithStreamingResponse:
+ from .resources.path import PathResourceWithStreamingResponse
+
+ return PathResourceWithStreamingResponse(self._client.path)
+
+ @cached_property
+ def app(self) -> app.AppResourceWithStreamingResponse:
+ from .resources.app import AppResourceWithStreamingResponse
+
+ return AppResourceWithStreamingResponse(self._client.app)
+
+ @cached_property
+ def agent(self) -> agent.AgentResourceWithStreamingResponse:
+ from .resources.agent import AgentResourceWithStreamingResponse
+
+ return AgentResourceWithStreamingResponse(self._client.agent)
+
+ @cached_property
+ def find(self) -> find.FindResourceWithStreamingResponse:
+ from .resources.find import FindResourceWithStreamingResponse
+
+ return FindResourceWithStreamingResponse(self._client.find)
+
+ @cached_property
+ def file(self) -> file.FileResourceWithStreamingResponse:
+ from .resources.file import FileResourceWithStreamingResponse
+
+ return FileResourceWithStreamingResponse(self._client.file)
+
+ @cached_property
+ def config(self) -> config.ConfigResourceWithStreamingResponse:
+ from .resources.config import ConfigResourceWithStreamingResponse
+
+ return ConfigResourceWithStreamingResponse(self._client.config)
+
+ @cached_property
+ def command(self) -> command.CommandResourceWithStreamingResponse:
+ from .resources.command import CommandResourceWithStreamingResponse
+
+ return CommandResourceWithStreamingResponse(self._client.command)
+
+ @cached_property
+ def project(self) -> project.ProjectResourceWithStreamingResponse:
+ from .resources.project import ProjectResourceWithStreamingResponse
+
+ return ProjectResourceWithStreamingResponse(self._client.project)
+
+ @cached_property
+ def session(self) -> session.SessionResourceWithStreamingResponse:
+ from .resources.session import SessionResourceWithStreamingResponse
+
+ return SessionResourceWithStreamingResponse(self._client.session)
+
+ @cached_property
+ def tui(self) -> tui.TuiResourceWithStreamingResponse:
+ from .resources.tui import TuiResourceWithStreamingResponse
+
+ return TuiResourceWithStreamingResponse(self._client.tui)
class AsyncOpencodeWithStreamedResponse:
+ _client: AsyncOpencode
+
def __init__(self, client: AsyncOpencode) -> None:
- self.event = event.AsyncEventResourceWithStreamingResponse(client.event)
- self.app = app.AsyncAppResourceWithStreamingResponse(client.app)
- self.find = find.AsyncFindResourceWithStreamingResponse(client.find)
- self.file = file.AsyncFileResourceWithStreamingResponse(client.file)
- self.config = config.AsyncConfigResourceWithStreamingResponse(client.config)
- self.session = session.AsyncSessionResourceWithStreamingResponse(client.session)
- self.tui = tui.AsyncTuiResourceWithStreamingResponse(client.tui)
+ self._client = client
+
+ @cached_property
+ def event(self) -> event.AsyncEventResourceWithStreamingResponse:
+ from .resources.event import AsyncEventResourceWithStreamingResponse
+
+ return AsyncEventResourceWithStreamingResponse(self._client.event)
+
+ @cached_property
+ def path(self) -> path.AsyncPathResourceWithStreamingResponse:
+ from .resources.path import AsyncPathResourceWithStreamingResponse
+
+ return AsyncPathResourceWithStreamingResponse(self._client.path)
+
+ @cached_property
+ def app(self) -> app.AsyncAppResourceWithStreamingResponse:
+ from .resources.app import AsyncAppResourceWithStreamingResponse
+
+ return AsyncAppResourceWithStreamingResponse(self._client.app)
+
+ @cached_property
+ def agent(self) -> agent.AsyncAgentResourceWithStreamingResponse:
+ from .resources.agent import AsyncAgentResourceWithStreamingResponse
+
+ return AsyncAgentResourceWithStreamingResponse(self._client.agent)
+
+ @cached_property
+ def find(self) -> find.AsyncFindResourceWithStreamingResponse:
+ from .resources.find import AsyncFindResourceWithStreamingResponse
+
+ return AsyncFindResourceWithStreamingResponse(self._client.find)
+
+ @cached_property
+ def file(self) -> file.AsyncFileResourceWithStreamingResponse:
+ from .resources.file import AsyncFileResourceWithStreamingResponse
+
+ return AsyncFileResourceWithStreamingResponse(self._client.file)
+
+ @cached_property
+ def config(self) -> config.AsyncConfigResourceWithStreamingResponse:
+ from .resources.config import AsyncConfigResourceWithStreamingResponse
+
+ return AsyncConfigResourceWithStreamingResponse(self._client.config)
+
+ @cached_property
+ def command(self) -> command.AsyncCommandResourceWithStreamingResponse:
+ from .resources.command import AsyncCommandResourceWithStreamingResponse
+
+ return AsyncCommandResourceWithStreamingResponse(self._client.command)
+
+ @cached_property
+ def project(self) -> project.AsyncProjectResourceWithStreamingResponse:
+ from .resources.project import AsyncProjectResourceWithStreamingResponse
+
+ return AsyncProjectResourceWithStreamingResponse(self._client.project)
+
+ @cached_property
+ def session(self) -> session.AsyncSessionResourceWithStreamingResponse:
+ from .resources.session import AsyncSessionResourceWithStreamingResponse
+
+ return AsyncSessionResourceWithStreamingResponse(self._client.session)
+
+ @cached_property
+ def tui(self) -> tui.AsyncTuiResourceWithStreamingResponse:
+ from .resources.tui import AsyncTuiResourceWithStreamingResponse
+
+ return AsyncTuiResourceWithStreamingResponse(self._client.tui)
Client = Opencode
diff --git a/src/opencode_ai/_compat.py b/src/opencode_ai/_compat.py
index 92d9ee6..bdef67f 100644
--- a/src/opencode_ai/_compat.py
+++ b/src/opencode_ai/_compat.py
@@ -12,14 +12,13 @@
_T = TypeVar("_T")
_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel)
-# --------------- Pydantic v2 compatibility ---------------
+# --------------- Pydantic v2, v3 compatibility ---------------
# Pyright incorrectly reports some of our functions as overriding a method when they don't
# pyright: reportIncompatibleMethodOverride=false
-PYDANTIC_V2 = pydantic.VERSION.startswith("2.")
+PYDANTIC_V1 = pydantic.VERSION.startswith("1.")
-# v1 re-exports
if TYPE_CHECKING:
def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001
@@ -44,90 +43,92 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001
...
else:
- if PYDANTIC_V2:
- from pydantic.v1.typing import (
+ # v1 re-exports
+ if PYDANTIC_V1:
+ from pydantic.typing import (
get_args as get_args,
is_union as is_union,
get_origin as get_origin,
is_typeddict as is_typeddict,
is_literal_type as is_literal_type,
)
- from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
+ from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
else:
- from pydantic.typing import (
+ from ._utils import (
get_args as get_args,
is_union as is_union,
get_origin as get_origin,
+ parse_date as parse_date,
is_typeddict as is_typeddict,
+ parse_datetime as parse_datetime,
is_literal_type as is_literal_type,
)
- from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
# refactored config
if TYPE_CHECKING:
from pydantic import ConfigDict as ConfigDict
else:
- if PYDANTIC_V2:
- from pydantic import ConfigDict
- else:
+ if PYDANTIC_V1:
# TODO: provide an error message here?
ConfigDict = None
+ else:
+ from pydantic import ConfigDict as ConfigDict
# renamed methods / properties
def parse_obj(model: type[_ModelT], value: object) -> _ModelT:
- if PYDANTIC_V2:
- return model.model_validate(value)
- else:
+ if PYDANTIC_V1:
return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
+ else:
+ return model.model_validate(value)
def field_is_required(field: FieldInfo) -> bool:
- if PYDANTIC_V2:
- return field.is_required()
- return field.required # type: ignore
+ if PYDANTIC_V1:
+ return field.required # type: ignore
+ return field.is_required()
def field_get_default(field: FieldInfo) -> Any:
value = field.get_default()
- if PYDANTIC_V2:
- from pydantic_core import PydanticUndefined
-
- if value == PydanticUndefined:
- return None
+ if PYDANTIC_V1:
return value
+ from pydantic_core import PydanticUndefined
+
+ if value == PydanticUndefined:
+ return None
return value
def field_outer_type(field: FieldInfo) -> Any:
- if PYDANTIC_V2:
- return field.annotation
- return field.outer_type_ # type: ignore
+ if PYDANTIC_V1:
+ return field.outer_type_ # type: ignore
+ return field.annotation
def get_model_config(model: type[pydantic.BaseModel]) -> Any:
- if PYDANTIC_V2:
- return model.model_config
- return model.__config__ # type: ignore
+ if PYDANTIC_V1:
+ return model.__config__ # type: ignore
+ return model.model_config
def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]:
- if PYDANTIC_V2:
- return model.model_fields
- return model.__fields__ # type: ignore
+ if PYDANTIC_V1:
+ return model.__fields__ # type: ignore
+ return model.model_fields
def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT:
- if PYDANTIC_V2:
- return model.model_copy(deep=deep)
- return model.copy(deep=deep) # type: ignore
+ if PYDANTIC_V1:
+ return model.copy(deep=deep) # type: ignore
+ return model.model_copy(deep=deep)
def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:
- if PYDANTIC_V2:
- return model.model_dump_json(indent=indent)
- return model.json(indent=indent) # type: ignore
+ if PYDANTIC_V1:
+ return model.json(indent=indent) # type: ignore
+ return model.model_dump_json(indent=indent)
def model_dump(
@@ -139,14 +140,14 @@ def model_dump(
warnings: bool = True,
mode: Literal["json", "python"] = "python",
) -> dict[str, Any]:
- if PYDANTIC_V2 or hasattr(model, "model_dump"):
+ if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
return model.model_dump(
mode=mode,
exclude=exclude,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
# warnings are not supported in Pydantic v1
- warnings=warnings if PYDANTIC_V2 else True,
+ warnings=True if PYDANTIC_V1 else warnings,
)
return cast(
"dict[str, Any]",
@@ -159,9 +160,9 @@ def model_dump(
def model_parse(model: type[_ModelT], data: Any) -> _ModelT:
- if PYDANTIC_V2:
- return model.model_validate(data)
- return model.parse_obj(data) # pyright: ignore[reportDeprecated]
+ if PYDANTIC_V1:
+ return model.parse_obj(data) # pyright: ignore[reportDeprecated]
+ return model.model_validate(data)
# generic models
@@ -170,17 +171,16 @@ def model_parse(model: type[_ModelT], data: Any) -> _ModelT:
class GenericModel(pydantic.BaseModel): ...
else:
- if PYDANTIC_V2:
+ if PYDANTIC_V1:
+ import pydantic.generics
+
+ class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ...
+ else:
# there no longer needs to be a distinction in v2 but
# we still have to create our own subclass to avoid
# inconsistent MRO ordering errors
class GenericModel(pydantic.BaseModel): ...
- else:
- import pydantic.generics
-
- class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ...
-
# cached properties
if TYPE_CHECKING:
diff --git a/src/opencode_ai/_models.py b/src/opencode_ai/_models.py
index 92f7c10..ca9500b 100644
--- a/src/opencode_ai/_models.py
+++ b/src/opencode_ai/_models.py
@@ -2,6 +2,7 @@
import os
import inspect
+import weakref
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
from datetime import date, datetime
from typing_extensions import (
@@ -50,7 +51,7 @@
strip_annotated_type,
)
from ._compat import (
- PYDANTIC_V2,
+ PYDANTIC_V1,
ConfigDict,
GenericModel as BaseGenericModel,
get_args,
@@ -81,11 +82,7 @@ class _ConfigProtocol(Protocol):
class BaseModel(pydantic.BaseModel):
- if PYDANTIC_V2:
- model_config: ClassVar[ConfigDict] = ConfigDict(
- extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true"))
- )
- else:
+ if PYDANTIC_V1:
@property
@override
@@ -95,6 +92,10 @@ def model_fields_set(self) -> set[str]:
class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated]
extra: Any = pydantic.Extra.allow # type: ignore
+ else:
+ model_config: ClassVar[ConfigDict] = ConfigDict(
+ extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true"))
+ )
def to_dict(
self,
@@ -215,25 +216,25 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride]
if key not in model_fields:
parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value
- if PYDANTIC_V2:
- _extra[key] = parsed
- else:
+ if PYDANTIC_V1:
_fields_set.add(key)
fields_values[key] = parsed
+ else:
+ _extra[key] = parsed
object.__setattr__(m, "__dict__", fields_values)
- if PYDANTIC_V2:
- # these properties are copied from Pydantic's `model_construct()` method
- object.__setattr__(m, "__pydantic_private__", None)
- object.__setattr__(m, "__pydantic_extra__", _extra)
- object.__setattr__(m, "__pydantic_fields_set__", _fields_set)
- else:
+ if PYDANTIC_V1:
# init_private_attributes() does not exist in v2
m._init_private_attributes() # type: ignore
# copied from Pydantic v1's `construct()` method
object.__setattr__(m, "__fields_set__", _fields_set)
+ else:
+ # these properties are copied from Pydantic's `model_construct()` method
+ object.__setattr__(m, "__pydantic_private__", None)
+ object.__setattr__(m, "__pydantic_extra__", _extra)
+ object.__setattr__(m, "__pydantic_fields_set__", _fields_set)
return m
@@ -243,7 +244,7 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride]
# although not in practice
model_construct = construct
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
# we define aliases for some of the new pydantic v2 methods so
# that we can just document these methods without having to specify
# a specific pydantic version as some users may not know which
@@ -256,13 +257,15 @@ def model_dump(
mode: Literal["json", "python"] | str = "python",
include: IncEx | None = None,
exclude: IncEx | None = None,
- by_alias: bool = False,
+ context: Any | None = None,
+ by_alias: bool | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
+ exclude_computed_fields: bool = False,
round_trip: bool = False,
warnings: bool | Literal["none", "warn", "error"] = True,
- context: dict[str, Any] | None = None,
+ fallback: Callable[[Any], Any] | None = None,
serialize_as_any: bool = False,
) -> dict[str, Any]:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump
@@ -271,16 +274,24 @@ def model_dump(
Args:
mode: The mode in which `to_python` should run.
- If mode is 'json', the dictionary will only contain JSON serializable types.
- If mode is 'python', the dictionary may contain any Python objects.
- include: A list of fields to include in the output.
- exclude: A list of fields to exclude from the output.
+ If mode is 'json', the output will only contain JSON serializable types.
+ If mode is 'python', the output may contain non-JSON-serializable Python objects.
+ include: A set of fields to include in the output.
+ exclude: A set of fields to exclude from the output.
+ context: Additional context to pass to the serializer.
by_alias: Whether to use the field's alias in the dictionary key if defined.
- exclude_unset: Whether to exclude fields that are unset or None from the output.
- exclude_defaults: Whether to exclude fields that are set to their default value from the output.
- exclude_none: Whether to exclude fields that have a value of `None` from the output.
- round_trip: Whether to enable serialization and deserialization round-trip support.
- warnings: Whether to log warnings when invalid fields are encountered.
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
+ exclude_defaults: Whether to exclude fields that are set to their default value.
+ exclude_none: Whether to exclude fields that have a value of `None`.
+ exclude_computed_fields: Whether to exclude computed fields.
+ While this can be useful for round-tripping, it is usually recommended to use the dedicated
+ `round_trip` parameter instead.
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
+ warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
+ "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+ fallback: A function to call when an unknown value is encountered. If not provided,
+ a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+ serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
Returns:
A dictionary representation of the model.
@@ -295,10 +306,14 @@ def model_dump(
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
+ if fallback is not None:
+ raise ValueError("fallback is only supported in Pydantic v2")
+ if exclude_computed_fields != False:
+ raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
dumped = super().dict( # pyright: ignore[reportDeprecated]
include=include,
exclude=exclude,
- by_alias=by_alias,
+ by_alias=by_alias if by_alias is not None else False,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
@@ -311,15 +326,18 @@ def model_dump_json(
self,
*,
indent: int | None = None,
+ ensure_ascii: bool = False,
include: IncEx | None = None,
exclude: IncEx | None = None,
- by_alias: bool = False,
+ context: Any | None = None,
+ by_alias: bool | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
+ exclude_computed_fields: bool = False,
round_trip: bool = False,
warnings: bool | Literal["none", "warn", "error"] = True,
- context: dict[str, Any] | None = None,
+ fallback: Callable[[Any], Any] | None = None,
serialize_as_any: bool = False,
) -> str:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json
@@ -348,11 +366,17 @@ def model_dump_json(
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
+ if fallback is not None:
+ raise ValueError("fallback is only supported in Pydantic v2")
+ if ensure_ascii != False:
+ raise ValueError("ensure_ascii is only supported in Pydantic v2")
+ if exclude_computed_fields != False:
+ raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
return super().json( # type: ignore[reportDeprecated]
indent=indent,
include=include,
exclude=exclude,
- by_alias=by_alias,
+ by_alias=by_alias if by_alias is not None else False,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
@@ -363,10 +387,10 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
if value is None:
return field_get_default(field)
- if PYDANTIC_V2:
- type_ = field.annotation
- else:
+ if PYDANTIC_V1:
type_ = cast(type, field.outer_type_) # type: ignore
+ else:
+ type_ = field.annotation # type: ignore
if type_ is None:
raise RuntimeError(f"Unexpected field type is None for {key}")
@@ -375,7 +399,7 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None:
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
# TODO
return None
@@ -567,6 +591,9 @@ class CachedDiscriminatorType(Protocol):
__discriminator__: DiscriminatorDetails
+DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary()
+
+
class DiscriminatorDetails:
field_name: str
"""The name of the discriminator field in the variant class, e.g.
@@ -609,8 +636,9 @@ def __init__(
def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:
- if isinstance(union, CachedDiscriminatorType):
- return union.__discriminator__
+ cached = DISCRIMINATOR_CACHE.get(union)
+ if cached is not None:
+ return cached
discriminator_field_name: str | None = None
@@ -628,30 +656,30 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
for variant in get_args(union):
variant = strip_annotated_type(variant)
if is_basemodel_type(variant):
- if PYDANTIC_V2:
- field = _extract_field_schema_pv2(variant, discriminator_field_name)
- if not field:
+ if PYDANTIC_V1:
+ field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
+ if not field_info:
continue
# Note: if one variant defines an alias then they all should
- discriminator_alias = field.get("serialization_alias")
-
- field_schema = field["schema"]
+ discriminator_alias = field_info.alias
- if field_schema["type"] == "literal":
- for entry in cast("LiteralSchema", field_schema)["expected"]:
+ if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation):
+ for entry in get_args(annotation):
if isinstance(entry, str):
mapping[entry] = variant
else:
- field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
- if not field_info:
+ field = _extract_field_schema_pv2(variant, discriminator_field_name)
+ if not field:
continue
# Note: if one variant defines an alias then they all should
- discriminator_alias = field_info.alias
+ discriminator_alias = field.get("serialization_alias")
- if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation):
- for entry in get_args(annotation):
+ field_schema = field["schema"]
+
+ if field_schema["type"] == "literal":
+ for entry in cast("LiteralSchema", field_schema)["expected"]:
if isinstance(entry, str):
mapping[entry] = variant
@@ -663,7 +691,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
discriminator_field=discriminator_field_name,
discriminator_alias=discriminator_alias,
)
- cast(CachedDiscriminatorType, union).__discriminator__ = details
+ DISCRIMINATOR_CACHE.setdefault(union, details)
return details
@@ -714,7 +742,7 @@ class GenericModel(BaseGenericModel, BaseModel):
pass
-if PYDANTIC_V2:
+if not PYDANTIC_V1:
from pydantic import TypeAdapter as _TypeAdapter
_CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter))
@@ -782,12 +810,12 @@ class FinalRequestOptions(pydantic.BaseModel):
json_data: Union[Body, None] = None
extra_json: Union[AnyMapping, None] = None
- if PYDANTIC_V2:
- model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
- else:
+ if PYDANTIC_V1:
class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated]
arbitrary_types_allowed: bool = True
+ else:
+ model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
def get_max_retries(self, max_retries: int) -> int:
if isinstance(self.max_retries, NotGiven):
@@ -820,9 +848,9 @@ def construct( # type: ignore
key: strip_not_given(value)
for key, value in values.items()
}
- if PYDANTIC_V2:
- return super().model_construct(_fields_set, **kwargs)
- return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated]
+ if PYDANTIC_V1:
+ return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated]
+ return super().model_construct(_fields_set, **kwargs)
if not TYPE_CHECKING:
# type checkers incorrectly complain about this assignment
diff --git a/src/opencode_ai/_qs.py b/src/opencode_ai/_qs.py
index 274320c..ada6fd3 100644
--- a/src/opencode_ai/_qs.py
+++ b/src/opencode_ai/_qs.py
@@ -4,7 +4,7 @@
from urllib.parse import parse_qs, urlencode
from typing_extensions import Literal, get_args
-from ._types import NOT_GIVEN, NotGiven, NotGivenOr
+from ._types import NotGiven, not_given
from ._utils import flatten
_T = TypeVar("_T")
@@ -41,8 +41,8 @@ def stringify(
self,
params: Params,
*,
- array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN,
- nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN,
+ array_format: ArrayFormat | NotGiven = not_given,
+ nested_format: NestedFormat | NotGiven = not_given,
) -> str:
return urlencode(
self.stringify_items(
@@ -56,8 +56,8 @@ def stringify_items(
self,
params: Params,
*,
- array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN,
- nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN,
+ array_format: ArrayFormat | NotGiven = not_given,
+ nested_format: NestedFormat | NotGiven = not_given,
) -> list[tuple[str, str]]:
opts = Options(
qs=self,
@@ -143,8 +143,8 @@ def __init__(
self,
qs: Querystring = _qs,
*,
- array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN,
- nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN,
+ array_format: ArrayFormat | NotGiven = not_given,
+ nested_format: NestedFormat | NotGiven = not_given,
) -> None:
self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format
self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format
diff --git a/src/opencode_ai/_streaming.py b/src/opencode_ai/_streaming.py
index 34499b5..ae72550 100644
--- a/src/opencode_ai/_streaming.py
+++ b/src/opencode_ai/_streaming.py
@@ -54,12 +54,12 @@ def __stream__(self) -> Iterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # Ensure the entire stream is consumed
- for _sse in iterator:
- ...
+ try:
+ for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ response.close()
def __enter__(self) -> Self:
return self
@@ -118,12 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- async for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # Ensure the entire stream is consumed
- async for _sse in iterator:
- ...
+ try:
+ async for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ await response.aclose()
async def __aenter__(self) -> Self:
return self
diff --git a/src/opencode_ai/_types.py b/src/opencode_ai/_types.py
index e9ba7eb..3a1e658 100644
--- a/src/opencode_ai/_types.py
+++ b/src/opencode_ai/_types.py
@@ -13,10 +13,21 @@
Mapping,
TypeVar,
Callable,
+ Iterator,
Optional,
Sequence,
)
-from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable
+from typing_extensions import (
+ Set,
+ Literal,
+ Protocol,
+ TypeAlias,
+ TypedDict,
+ SupportsIndex,
+ overload,
+ override,
+ runtime_checkable,
+)
import httpx
import pydantic
@@ -106,18 +117,21 @@ class RequestOptions(TypedDict, total=False):
# Sentinel class used until PEP 0661 is accepted
class NotGiven:
"""
- A sentinel singleton class used to distinguish omitted keyword arguments
- from those passed in with the value None (which may have different behavior).
+ For parameters with a meaningful None value, we need to distinguish between
+ the user explicitly passing None, and the user not passing the parameter at
+ all.
+
+ User code shouldn't need to use not_given directly.
For example:
```py
- def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ...
+ def create(timeout: Timeout | None | NotGiven = not_given): ...
- get(timeout=1) # 1s timeout
- get(timeout=None) # No timeout
- get() # Default timeout behavior, which may not be statically known at the method definition.
+ create(timeout=1) # 1s timeout
+ create(timeout=None) # No timeout
+ create() # Default timeout behavior
```
"""
@@ -129,13 +143,14 @@ def __repr__(self) -> str:
return "NOT_GIVEN"
-NotGivenOr = Union[_T, NotGiven]
+not_given = NotGiven()
+# for backwards compatibility:
NOT_GIVEN = NotGiven()
class Omit:
- """In certain situations you need to be able to represent a case where a default value has
- to be explicitly removed and `None` is not an appropriate substitute, for example:
+ """
+ To explicitly omit something from being sent in a request, use `omit`.
```py
# as the default `Content-Type` header is `application/json` that will be sent
@@ -145,8 +160,8 @@ class Omit:
# to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983'
client.post(..., headers={"Content-Type": "multipart/form-data"})
- # instead you can remove the default `application/json` header by passing Omit
- client.post(..., headers={"Content-Type": Omit()})
+ # instead you can remove the default `application/json` header by passing omit
+ client.post(..., headers={"Content-Type": omit})
```
"""
@@ -154,6 +169,9 @@ def __bool__(self) -> Literal[False]:
return False
+omit = Omit()
+
+
@runtime_checkable
class ModelBuilderProtocol(Protocol):
@classmethod
@@ -217,3 +235,27 @@ class _GenericAlias(Protocol):
class HttpxSendArgs(TypedDict, total=False):
auth: httpx.Auth
follow_redirects: bool
+
+
+_T_co = TypeVar("_T_co", covariant=True)
+
+
+if TYPE_CHECKING:
+ # This works because str.__contains__ does not accept object (either in typeshed or at runtime)
+ # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285
+ #
+ # Note: index() and count() methods are intentionally omitted to allow pyright to properly
+ # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr.
+ class SequenceNotStr(Protocol[_T_co]):
+ @overload
+ def __getitem__(self, index: SupportsIndex, /) -> _T_co: ...
+ @overload
+ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ...
+ def __contains__(self, value: object, /) -> bool: ...
+ def __len__(self) -> int: ...
+ def __iter__(self) -> Iterator[_T_co]: ...
+ def __reversed__(self) -> Iterator[_T_co]: ...
+else:
+ # just point this to a normal `Sequence` at runtime to avoid having to special case
+ # deserializing our custom sequence type
+ SequenceNotStr = Sequence
diff --git a/src/opencode_ai/_utils/__init__.py b/src/opencode_ai/_utils/__init__.py
index d4fda26..dc64e29 100644
--- a/src/opencode_ai/_utils/__init__.py
+++ b/src/opencode_ai/_utils/__init__.py
@@ -10,7 +10,6 @@
lru_cache as lru_cache,
is_mapping as is_mapping,
is_tuple_t as is_tuple_t,
- parse_date as parse_date,
is_iterable as is_iterable,
is_sequence as is_sequence,
coerce_float as coerce_float,
@@ -23,7 +22,6 @@
coerce_boolean as coerce_boolean,
coerce_integer as coerce_integer,
file_from_path as file_from_path,
- parse_datetime as parse_datetime,
strip_not_given as strip_not_given,
deepcopy_minimal as deepcopy_minimal,
get_async_library as get_async_library,
@@ -32,12 +30,20 @@
maybe_coerce_boolean as maybe_coerce_boolean,
maybe_coerce_integer as maybe_coerce_integer,
)
+from ._compat import (
+ get_args as get_args,
+ is_union as is_union,
+ get_origin as get_origin,
+ is_typeddict as is_typeddict,
+ is_literal_type as is_literal_type,
+)
from ._typing import (
is_list_type as is_list_type,
is_union_type as is_union_type,
extract_type_arg as extract_type_arg,
is_iterable_type as is_iterable_type,
is_required_type as is_required_type,
+ is_sequence_type as is_sequence_type,
is_annotated_type as is_annotated_type,
is_type_alias_type as is_type_alias_type,
strip_annotated_type as strip_annotated_type,
@@ -55,3 +61,4 @@
function_has_argument as function_has_argument,
assert_signatures_in_sync as assert_signatures_in_sync,
)
+from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
diff --git a/src/opencode_ai/_utils/_compat.py b/src/opencode_ai/_utils/_compat.py
new file mode 100644
index 0000000..dd70323
--- /dev/null
+++ b/src/opencode_ai/_utils/_compat.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+import sys
+import typing_extensions
+from typing import Any, Type, Union, Literal, Optional
+from datetime import date, datetime
+from typing_extensions import get_args as _get_args, get_origin as _get_origin
+
+from .._types import StrBytesIntFloat
+from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime
+
+_LITERAL_TYPES = {Literal, typing_extensions.Literal}
+
+
+def get_args(tp: type[Any]) -> tuple[Any, ...]:
+ return _get_args(tp)
+
+
+def get_origin(tp: type[Any]) -> type[Any] | None:
+ return _get_origin(tp)
+
+
+def is_union(tp: Optional[Type[Any]]) -> bool:
+ if sys.version_info < (3, 10):
+ return tp is Union # type: ignore[comparison-overlap]
+ else:
+ import types
+
+ return tp is Union or tp is types.UnionType
+
+
+def is_typeddict(tp: Type[Any]) -> bool:
+ return typing_extensions.is_typeddict(tp)
+
+
+def is_literal_type(tp: Type[Any]) -> bool:
+ return get_origin(tp) in _LITERAL_TYPES
+
+
+def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
+ return _parse_date(value)
+
+
+def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
+ return _parse_datetime(value)
diff --git a/src/opencode_ai/_utils/_datetime_parse.py b/src/opencode_ai/_utils/_datetime_parse.py
new file mode 100644
index 0000000..7cb9d9e
--- /dev/null
+++ b/src/opencode_ai/_utils/_datetime_parse.py
@@ -0,0 +1,136 @@
+"""
+This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py
+without the Pydantic v1 specific errors.
+"""
+
+from __future__ import annotations
+
+import re
+from typing import Dict, Union, Optional
+from datetime import date, datetime, timezone, timedelta
+
+from .._types import StrBytesIntFloat
+
+date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})"
+time_expr = (
+ r"(?P\d{1,2}):(?P\d{1,2})"
+ r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?"
+ r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$"
+)
+
+date_re = re.compile(f"{date_expr}$")
+datetime_re = re.compile(f"{date_expr}[T ]{time_expr}")
+
+
+EPOCH = datetime(1970, 1, 1)
+# if greater than this, the number is in ms, if less than or equal it's in seconds
+# (in seconds this is 11th October 2603, in ms it's 20th August 1970)
+MS_WATERSHED = int(2e10)
+# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
+MAX_NUMBER = int(3e20)
+
+
+def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]:
+ if isinstance(value, (int, float)):
+ return value
+ try:
+ return float(value)
+ except ValueError:
+ return None
+ except TypeError:
+ raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None
+
+
+def _from_unix_seconds(seconds: Union[int, float]) -> datetime:
+ if seconds > MAX_NUMBER:
+ return datetime.max
+ elif seconds < -MAX_NUMBER:
+ return datetime.min
+
+ while abs(seconds) > MS_WATERSHED:
+ seconds /= 1000
+ dt = EPOCH + timedelta(seconds=seconds)
+ return dt.replace(tzinfo=timezone.utc)
+
+
+def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]:
+ if value == "Z":
+ return timezone.utc
+ elif value is not None:
+ offset_mins = int(value[-2:]) if len(value) > 3 else 0
+ offset = 60 * int(value[1:3]) + offset_mins
+ if value[0] == "-":
+ offset = -offset
+ return timezone(timedelta(minutes=offset))
+ else:
+ return None
+
+
+def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
+ """
+ Parse a datetime/int/float/string and return a datetime.datetime.
+
+ This function supports time zone offsets. When the input contains one,
+ the output uses a timezone with a fixed offset from UTC.
+
+ Raise ValueError if the input is well formatted but not a valid datetime.
+ Raise ValueError if the input isn't well formatted.
+ """
+ if isinstance(value, datetime):
+ return value
+
+ number = _get_numeric(value, "datetime")
+ if number is not None:
+ return _from_unix_seconds(number)
+
+ if isinstance(value, bytes):
+ value = value.decode()
+
+ assert not isinstance(value, (float, int))
+
+ match = datetime_re.match(value)
+ if match is None:
+ raise ValueError("invalid datetime format")
+
+ kw = match.groupdict()
+ if kw["microsecond"]:
+ kw["microsecond"] = kw["microsecond"].ljust(6, "0")
+
+ tzinfo = _parse_timezone(kw.pop("tzinfo"))
+ kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None}
+ kw_["tzinfo"] = tzinfo
+
+ return datetime(**kw_) # type: ignore
+
+
+def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
+ """
+ Parse a date/int/float/string and return a datetime.date.
+
+ Raise ValueError if the input is well formatted but not a valid date.
+ Raise ValueError if the input isn't well formatted.
+ """
+ if isinstance(value, date):
+ if isinstance(value, datetime):
+ return value.date()
+ else:
+ return value
+
+ number = _get_numeric(value, "date")
+ if number is not None:
+ return _from_unix_seconds(number).date()
+
+ if isinstance(value, bytes):
+ value = value.decode()
+
+ assert not isinstance(value, (float, int))
+ match = date_re.match(value)
+ if match is None:
+ raise ValueError("invalid date format")
+
+ kw = {k: int(v) for k, v in match.groupdict().items()}
+
+ try:
+ return date(**kw)
+ except ValueError:
+ raise ValueError("invalid date format") from None
diff --git a/src/opencode_ai/_utils/_sync.py b/src/opencode_ai/_utils/_sync.py
index ad7ec71..f6027c1 100644
--- a/src/opencode_ai/_utils/_sync.py
+++ b/src/opencode_ai/_utils/_sync.py
@@ -1,10 +1,8 @@
from __future__ import annotations
-import sys
import asyncio
import functools
-import contextvars
-from typing import Any, TypeVar, Callable, Awaitable
+from typing import TypeVar, Callable, Awaitable
from typing_extensions import ParamSpec
import anyio
@@ -15,34 +13,11 @@
T_ParamSpec = ParamSpec("T_ParamSpec")
-if sys.version_info >= (3, 9):
- _asyncio_to_thread = asyncio.to_thread
-else:
- # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
- # for Python 3.8 support
- async def _asyncio_to_thread(
- func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
- ) -> Any:
- """Asynchronously run function *func* in a separate thread.
-
- Any *args and **kwargs supplied for this function are directly passed
- to *func*. Also, the current :class:`contextvars.Context` is propagated,
- allowing context variables from the main thread to be accessed in the
- separate thread.
-
- Returns a coroutine that can be awaited to get the eventual result of *func*.
- """
- loop = asyncio.events.get_running_loop()
- ctx = contextvars.copy_context()
- func_call = functools.partial(ctx.run, func, *args, **kwargs)
- return await loop.run_in_executor(None, func_call)
-
-
async def to_thread(
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
) -> T_Retval:
if sniffio.current_async_library() == "asyncio":
- return await _asyncio_to_thread(func, *args, **kwargs)
+ return await asyncio.to_thread(func, *args, **kwargs)
return await anyio.to_thread.run_sync(
functools.partial(func, *args, **kwargs),
@@ -53,10 +28,7 @@ async def to_thread(
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
"""
Take a blocking function and create an async one that receives the same
- positional and keyword arguments. For python version 3.9 and above, it uses
- asyncio.to_thread to run the function in a separate thread. For python version
- 3.8, it uses locally defined copy of the asyncio.to_thread function which was
- introduced in python 3.9.
+ positional and keyword arguments.
Usage:
diff --git a/src/opencode_ai/_utils/_transform.py b/src/opencode_ai/_utils/_transform.py
index b0cc20a..5207549 100644
--- a/src/opencode_ai/_utils/_transform.py
+++ b/src/opencode_ai/_utils/_transform.py
@@ -16,18 +16,20 @@
lru_cache,
is_mapping,
is_iterable,
+ is_sequence,
)
from .._files import is_base64_file_input
+from ._compat import get_origin, is_typeddict
from ._typing import (
is_list_type,
is_union_type,
extract_type_arg,
is_iterable_type,
is_required_type,
+ is_sequence_type,
is_annotated_type,
strip_annotated_type,
)
-from .._compat import get_origin, model_dump, is_typeddict
_T = TypeVar("_T")
@@ -167,6 +169,8 @@ def _transform_recursive(
Defaults to the same value as the `annotation` argument.
"""
+ from .._compat import model_dump
+
if inner_type is None:
inner_type = annotation
@@ -184,6 +188,8 @@ def _transform_recursive(
(is_list_type(stripped_type) and is_list(data))
# Iterable[T]
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
+ # Sequence[T]
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
):
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
# intended as an iterable, so we don't transform it.
@@ -262,7 +268,7 @@ def _transform_typeddict(
annotations = get_type_hints(expected_type, include_extras=True)
for key, value in data.items():
if not is_given(value):
- # we don't need to include `NotGiven` values here as they'll
+ # we don't need to include omitted values here as they'll
# be stripped out before the request is sent anyway
continue
@@ -329,6 +335,8 @@ async def _async_transform_recursive(
Defaults to the same value as the `annotation` argument.
"""
+ from .._compat import model_dump
+
if inner_type is None:
inner_type = annotation
@@ -346,6 +354,8 @@ async def _async_transform_recursive(
(is_list_type(stripped_type) and is_list(data))
# Iterable[T]
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
+ # Sequence[T]
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
):
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
# intended as an iterable, so we don't transform it.
@@ -424,7 +434,7 @@ async def _async_transform_typeddict(
annotations = get_type_hints(expected_type, include_extras=True)
for key, value in data.items():
if not is_given(value):
- # we don't need to include `NotGiven` values here as they'll
+ # we don't need to include omitted values here as they'll
# be stripped out before the request is sent anyway
continue
diff --git a/src/opencode_ai/_utils/_typing.py b/src/opencode_ai/_utils/_typing.py
index 1bac954..193109f 100644
--- a/src/opencode_ai/_utils/_typing.py
+++ b/src/opencode_ai/_utils/_typing.py
@@ -15,7 +15,7 @@
from ._utils import lru_cache
from .._types import InheritsGeneric
-from .._compat import is_union as _is_union
+from ._compat import is_union as _is_union
def is_annotated_type(typ: type) -> bool:
@@ -26,6 +26,11 @@ def is_list_type(typ: type) -> bool:
return (get_origin(typ) or typ) == list
+def is_sequence_type(typ: type) -> bool:
+ origin = get_origin(typ) or typ
+ return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence
+
+
def is_iterable_type(typ: type) -> bool:
"""If the given type is `typing.Iterable[T]`"""
origin = get_origin(typ) or typ
diff --git a/src/opencode_ai/_utils/_utils.py b/src/opencode_ai/_utils/_utils.py
index ea3cf3f..eec7f4a 100644
--- a/src/opencode_ai/_utils/_utils.py
+++ b/src/opencode_ai/_utils/_utils.py
@@ -21,8 +21,7 @@
import sniffio
-from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike
-from .._compat import parse_date as parse_date, parse_datetime as parse_datetime
+from .._types import Omit, NotGiven, FileTypes, HeadersLike
_T = TypeVar("_T")
_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
@@ -64,7 +63,7 @@ def _extract_items(
try:
key = path[index]
except IndexError:
- if isinstance(obj, NotGiven):
+ if not is_given(obj):
# no value was provided - we can safely ignore
return []
@@ -127,14 +126,14 @@ def _extract_items(
return []
-def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]:
- return not isinstance(obj, NotGiven)
+def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
+ return not isinstance(obj, NotGiven) and not isinstance(obj, Omit)
# Type safe methods for narrowing types with TypeVars.
# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
# however this cause Pyright to rightfully report errors. As we know we don't
-# care about the contained types we can safely use `object` in it's place.
+# care about the contained types we can safely use `object` in its place.
#
# There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
# `is_*` is for when you're dealing with an unknown input
diff --git a/src/opencode_ai/_version.py b/src/opencode_ai/_version.py
index 3e57475..565754d 100644
--- a/src/opencode_ai/_version.py
+++ b/src/opencode_ai/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "opencode_ai"
-__version__ = "0.1.0-alpha.36" # x-release-please-version
+__version__ = "0.1.0-alpha.37" # x-release-please-version
diff --git a/src/opencode_ai/resources/__init__.py b/src/opencode_ai/resources/__init__.py
index ee6a647..c01ce0e 100644
--- a/src/opencode_ai/resources/__init__.py
+++ b/src/opencode_ai/resources/__init__.py
@@ -32,6 +32,22 @@
FindResourceWithStreamingResponse,
AsyncFindResourceWithStreamingResponse,
)
+from .path import (
+ PathResource,
+ AsyncPathResource,
+ PathResourceWithRawResponse,
+ AsyncPathResourceWithRawResponse,
+ PathResourceWithStreamingResponse,
+ AsyncPathResourceWithStreamingResponse,
+)
+from .agent import (
+ AgentResource,
+ AsyncAgentResource,
+ AgentResourceWithRawResponse,
+ AsyncAgentResourceWithRawResponse,
+ AgentResourceWithStreamingResponse,
+ AsyncAgentResourceWithStreamingResponse,
+)
from .event import (
EventResource,
AsyncEventResource,
@@ -48,6 +64,22 @@
ConfigResourceWithStreamingResponse,
AsyncConfigResourceWithStreamingResponse,
)
+from .command import (
+ CommandResource,
+ AsyncCommandResource,
+ CommandResourceWithRawResponse,
+ AsyncCommandResourceWithRawResponse,
+ CommandResourceWithStreamingResponse,
+ AsyncCommandResourceWithStreamingResponse,
+)
+from .project import (
+ ProjectResource,
+ AsyncProjectResource,
+ ProjectResourceWithRawResponse,
+ AsyncProjectResourceWithRawResponse,
+ ProjectResourceWithStreamingResponse,
+ AsyncProjectResourceWithStreamingResponse,
+)
from .session import (
SessionResource,
AsyncSessionResource,
@@ -64,12 +96,24 @@
"AsyncEventResourceWithRawResponse",
"EventResourceWithStreamingResponse",
"AsyncEventResourceWithStreamingResponse",
+ "PathResource",
+ "AsyncPathResource",
+ "PathResourceWithRawResponse",
+ "AsyncPathResourceWithRawResponse",
+ "PathResourceWithStreamingResponse",
+ "AsyncPathResourceWithStreamingResponse",
"AppResource",
"AsyncAppResource",
"AppResourceWithRawResponse",
"AsyncAppResourceWithRawResponse",
"AppResourceWithStreamingResponse",
"AsyncAppResourceWithStreamingResponse",
+ "AgentResource",
+ "AsyncAgentResource",
+ "AgentResourceWithRawResponse",
+ "AsyncAgentResourceWithRawResponse",
+ "AgentResourceWithStreamingResponse",
+ "AsyncAgentResourceWithStreamingResponse",
"FindResource",
"AsyncFindResource",
"FindResourceWithRawResponse",
@@ -88,6 +132,18 @@
"AsyncConfigResourceWithRawResponse",
"ConfigResourceWithStreamingResponse",
"AsyncConfigResourceWithStreamingResponse",
+ "CommandResource",
+ "AsyncCommandResource",
+ "CommandResourceWithRawResponse",
+ "AsyncCommandResourceWithRawResponse",
+ "CommandResourceWithStreamingResponse",
+ "AsyncCommandResourceWithStreamingResponse",
+ "ProjectResource",
+ "AsyncProjectResource",
+ "ProjectResourceWithRawResponse",
+ "AsyncProjectResourceWithRawResponse",
+ "ProjectResourceWithStreamingResponse",
+ "AsyncProjectResourceWithStreamingResponse",
"SessionResource",
"AsyncSessionResource",
"SessionResourceWithRawResponse",
diff --git a/src/opencode_ai/resources/agent.py b/src/opencode_ai/resources/agent.py
new file mode 100644
index 0000000..32bcfbf
--- /dev/null
+++ b/src/opencode_ai/resources/agent.py
@@ -0,0 +1,169 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..types import agent_list_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.agent_list_response import AgentListResponse
+
+__all__ = ["AgentResource", "AsyncAgentResource"]
+
+
+class AgentResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AgentResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AgentResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AgentResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return AgentResourceWithStreamingResponse(self)
+
+ def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentListResponse:
+ """
+ List all agents
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/agent",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, agent_list_params.AgentListParams),
+ ),
+ cast_to=AgentListResponse,
+ )
+
+
+class AsyncAgentResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncAgentResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAgentResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAgentResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return AsyncAgentResourceWithStreamingResponse(self)
+
+ async def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentListResponse:
+ """
+ List all agents
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/agent",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, agent_list_params.AgentListParams),
+ ),
+ cast_to=AgentListResponse,
+ )
+
+
+class AgentResourceWithRawResponse:
+ def __init__(self, agent: AgentResource) -> None:
+ self._agent = agent
+
+ self.list = to_raw_response_wrapper(
+ agent.list,
+ )
+
+
+class AsyncAgentResourceWithRawResponse:
+ def __init__(self, agent: AsyncAgentResource) -> None:
+ self._agent = agent
+
+ self.list = async_to_raw_response_wrapper(
+ agent.list,
+ )
+
+
+class AgentResourceWithStreamingResponse:
+ def __init__(self, agent: AgentResource) -> None:
+ self._agent = agent
+
+ self.list = to_streamed_response_wrapper(
+ agent.list,
+ )
+
+
+class AsyncAgentResourceWithStreamingResponse:
+ def __init__(self, agent: AsyncAgentResource) -> None:
+ self._agent = agent
+
+ self.list = async_to_streamed_response_wrapper(
+ agent.list,
+ )
diff --git a/src/opencode_ai/resources/app.py b/src/opencode_ai/resources/app.py
index e9505ce..2c139b2 100644
--- a/src/opencode_ai/resources/app.py
+++ b/src/opencode_ai/resources/app.py
@@ -7,8 +7,8 @@
import httpx
-from ..types import app_log_params
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..types import app_log_params, app_providers_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
@@ -18,11 +18,8 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ..types.app import App
from .._base_client import make_request_options
from ..types.app_log_response import AppLogResponse
-from ..types.app_init_response import AppInitResponse
-from ..types.app_modes_response import AppModesResponse
from ..types.app_providers_response import AppProvidersResponse
__all__ = ["AppResource", "AsyncAppResource"]
@@ -48,57 +45,20 @@ def with_streaming_response(self) -> AppResourceWithStreamingResponse:
"""
return AppResourceWithStreamingResponse(self)
- def get(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> App:
- """Get app info"""
- return self._get(
- "/app",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=App,
- )
-
- def init(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AppInitResponse:
- """Initialize the app"""
- return self._post(
- "/app/init",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AppInitResponse,
- )
-
def log(
self,
*,
level: Literal["debug", "info", "error", "warn"],
message: str,
service: str,
- extra: Dict[str, object] | NotGiven = NOT_GIVEN,
+ directory: str | Omit = omit,
+ extra: Dict[str, object] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AppLogResponse:
"""
Write a log entry to the server logs
@@ -132,45 +92,46 @@ def log(
app_log_params.AppLogParams,
),
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, app_log_params.AppLogParams),
),
cast_to=AppLogResponse,
)
- def modes(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AppModesResponse:
- """List all modes"""
- return self._get(
- "/mode",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AppModesResponse,
- )
-
def providers(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AppProvidersResponse:
- """List all providers"""
+ """
+ List all providers
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return self._get(
"/config/providers",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, app_providers_params.AppProvidersParams),
),
cast_to=AppProvidersResponse,
)
@@ -196,57 +157,20 @@ def with_streaming_response(self) -> AsyncAppResourceWithStreamingResponse:
"""
return AsyncAppResourceWithStreamingResponse(self)
- async def get(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> App:
- """Get app info"""
- return await self._get(
- "/app",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=App,
- )
-
- async def init(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AppInitResponse:
- """Initialize the app"""
- return await self._post(
- "/app/init",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AppInitResponse,
- )
-
async def log(
self,
*,
level: Literal["debug", "info", "error", "warn"],
message: str,
service: str,
- extra: Dict[str, object] | NotGiven = NOT_GIVEN,
+ directory: str | Omit = omit,
+ extra: Dict[str, object] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AppLogResponse:
"""
Write a log entry to the server logs
@@ -280,45 +204,46 @@ async def log(
app_log_params.AppLogParams,
),
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, app_log_params.AppLogParams),
),
cast_to=AppLogResponse,
)
- async def modes(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AppModesResponse:
- """List all modes"""
- return await self._get(
- "/mode",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AppModesResponse,
- )
-
async def providers(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AppProvidersResponse:
- """List all providers"""
+ """
+ List all providers
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return await self._get(
"/config/providers",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, app_providers_params.AppProvidersParams),
),
cast_to=AppProvidersResponse,
)
@@ -328,18 +253,9 @@ class AppResourceWithRawResponse:
def __init__(self, app: AppResource) -> None:
self._app = app
- self.get = to_raw_response_wrapper(
- app.get,
- )
- self.init = to_raw_response_wrapper(
- app.init,
- )
self.log = to_raw_response_wrapper(
app.log,
)
- self.modes = to_raw_response_wrapper(
- app.modes,
- )
self.providers = to_raw_response_wrapper(
app.providers,
)
@@ -349,18 +265,9 @@ class AsyncAppResourceWithRawResponse:
def __init__(self, app: AsyncAppResource) -> None:
self._app = app
- self.get = async_to_raw_response_wrapper(
- app.get,
- )
- self.init = async_to_raw_response_wrapper(
- app.init,
- )
self.log = async_to_raw_response_wrapper(
app.log,
)
- self.modes = async_to_raw_response_wrapper(
- app.modes,
- )
self.providers = async_to_raw_response_wrapper(
app.providers,
)
@@ -370,18 +277,9 @@ class AppResourceWithStreamingResponse:
def __init__(self, app: AppResource) -> None:
self._app = app
- self.get = to_streamed_response_wrapper(
- app.get,
- )
- self.init = to_streamed_response_wrapper(
- app.init,
- )
self.log = to_streamed_response_wrapper(
app.log,
)
- self.modes = to_streamed_response_wrapper(
- app.modes,
- )
self.providers = to_streamed_response_wrapper(
app.providers,
)
@@ -391,18 +289,9 @@ class AsyncAppResourceWithStreamingResponse:
def __init__(self, app: AsyncAppResource) -> None:
self._app = app
- self.get = async_to_streamed_response_wrapper(
- app.get,
- )
- self.init = async_to_streamed_response_wrapper(
- app.init,
- )
self.log = async_to_streamed_response_wrapper(
app.log,
)
- self.modes = async_to_streamed_response_wrapper(
- app.modes,
- )
self.providers = async_to_streamed_response_wrapper(
app.providers,
)
diff --git a/src/opencode_ai/resources/command.py b/src/opencode_ai/resources/command.py
new file mode 100644
index 0000000..e31c6b4
--- /dev/null
+++ b/src/opencode_ai/resources/command.py
@@ -0,0 +1,169 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..types import command_list_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.command_list_response import CommandListResponse
+
+__all__ = ["CommandResource", "AsyncCommandResource"]
+
+
+class CommandResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> CommandResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return CommandResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> CommandResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return CommandResourceWithStreamingResponse(self)
+
+ def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> CommandListResponse:
+ """
+ List all commands
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/command",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, command_list_params.CommandListParams),
+ ),
+ cast_to=CommandListResponse,
+ )
+
+
+class AsyncCommandResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncCommandResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncCommandResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncCommandResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return AsyncCommandResourceWithStreamingResponse(self)
+
+ async def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> CommandListResponse:
+ """
+ List all commands
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/command",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, command_list_params.CommandListParams),
+ ),
+ cast_to=CommandListResponse,
+ )
+
+
+class CommandResourceWithRawResponse:
+ def __init__(self, command: CommandResource) -> None:
+ self._command = command
+
+ self.list = to_raw_response_wrapper(
+ command.list,
+ )
+
+
+class AsyncCommandResourceWithRawResponse:
+ def __init__(self, command: AsyncCommandResource) -> None:
+ self._command = command
+
+ self.list = async_to_raw_response_wrapper(
+ command.list,
+ )
+
+
+class CommandResourceWithStreamingResponse:
+ def __init__(self, command: CommandResource) -> None:
+ self._command = command
+
+ self.list = to_streamed_response_wrapper(
+ command.list,
+ )
+
+
+class AsyncCommandResourceWithStreamingResponse:
+ def __init__(self, command: AsyncCommandResource) -> None:
+ self._command = command
+
+ self.list = async_to_streamed_response_wrapper(
+ command.list,
+ )
diff --git a/src/opencode_ai/resources/config.py b/src/opencode_ai/resources/config.py
index 88ff3a3..70c1c59 100644
--- a/src/opencode_ai/resources/config.py
+++ b/src/opencode_ai/resources/config.py
@@ -4,7 +4,9 @@
import httpx
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..types import config_get_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -42,18 +44,34 @@ def with_streaming_response(self) -> ConfigResourceWithStreamingResponse:
def get(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Config:
- """Get config info"""
+ """
+ Get config info
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return self._get(
"/config",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, config_get_params.ConfigGetParams),
),
cast_to=Config,
)
@@ -82,18 +100,34 @@ def with_streaming_response(self) -> AsyncConfigResourceWithStreamingResponse:
async def get(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Config:
- """Get config info"""
+ """
+ Get config info
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return await self._get(
"/config",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, config_get_params.ConfigGetParams),
),
cast_to=Config,
)
diff --git a/src/opencode_ai/resources/event.py b/src/opencode_ai/resources/event.py
index 3e9baa3..6c33d60 100644
--- a/src/opencode_ai/resources/event.py
+++ b/src/opencode_ai/resources/event.py
@@ -6,7 +6,9 @@
import httpx
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..types import event_list_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -45,18 +47,35 @@ def with_streaming_response(self) -> EventResourceWithStreamingResponse:
def list(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Stream[EventListResponse]:
- """Get events"""
+ """
+ Get events
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
return self._get(
"/event",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, event_list_params.EventListParams),
),
cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system
stream=True,
@@ -87,18 +106,35 @@ def with_streaming_response(self) -> AsyncEventResourceWithStreamingResponse:
async def list(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AsyncStream[EventListResponse]:
- """Get events"""
+ """
+ Get events
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
return await self._get(
"/event",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, event_list_params.EventListParams),
),
cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system
stream=True,
diff --git a/src/opencode_ai/resources/file.py b/src/opencode_ai/resources/file.py
index 464bb93..711df13 100644
--- a/src/opencode_ai/resources/file.py
+++ b/src/opencode_ai/resources/file.py
@@ -4,8 +4,8 @@
import httpx
-from ..types import file_read_params
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..types import file_list_params, file_read_params, file_status_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
@@ -16,6 +16,7 @@
async_to_streamed_response_wrapper,
)
from .._base_client import make_request_options
+from ..types.file_list_response import FileListResponse
from ..types.file_read_response import FileReadResponse
from ..types.file_status_response import FileStatusResponse
@@ -42,16 +43,59 @@ def with_streaming_response(self) -> FileResourceWithStreamingResponse:
"""
return FileResourceWithStreamingResponse(self)
+ def list(
+ self,
+ *,
+ path: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FileListResponse:
+ """
+ List files and directories
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/file",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "path": path,
+ "directory": directory,
+ },
+ file_list_params.FileListParams,
+ ),
+ ),
+ cast_to=FileListResponse,
+ )
+
def read(
self,
*,
path: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileReadResponse:
"""
Read a file
@@ -66,13 +110,19 @@ def read(
timeout: Override the client-level default timeout for this request, in seconds
"""
return self._get(
- "/file",
+ "/file/content",
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=maybe_transform({"path": path}, file_read_params.FileReadParams),
+ query=maybe_transform(
+ {
+ "path": path,
+ "directory": directory,
+ },
+ file_read_params.FileReadParams,
+ ),
),
cast_to=FileReadResponse,
)
@@ -80,18 +130,34 @@ def read(
def status(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileStatusResponse:
- """Get file status"""
+ """
+ Get file status
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return self._get(
"/file/status",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, file_status_params.FileStatusParams),
),
cast_to=FileStatusResponse,
)
@@ -117,16 +183,59 @@ def with_streaming_response(self) -> AsyncFileResourceWithStreamingResponse:
"""
return AsyncFileResourceWithStreamingResponse(self)
+ async def list(
+ self,
+ *,
+ path: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FileListResponse:
+ """
+ List files and directories
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/file",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "path": path,
+ "directory": directory,
+ },
+ file_list_params.FileListParams,
+ ),
+ ),
+ cast_to=FileListResponse,
+ )
+
async def read(
self,
*,
path: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileReadResponse:
"""
Read a file
@@ -141,13 +250,19 @@ async def read(
timeout: Override the client-level default timeout for this request, in seconds
"""
return await self._get(
- "/file",
+ "/file/content",
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=await async_maybe_transform({"path": path}, file_read_params.FileReadParams),
+ query=await async_maybe_transform(
+ {
+ "path": path,
+ "directory": directory,
+ },
+ file_read_params.FileReadParams,
+ ),
),
cast_to=FileReadResponse,
)
@@ -155,18 +270,34 @@ async def read(
async def status(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileStatusResponse:
- """Get file status"""
+ """
+ Get file status
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return await self._get(
"/file/status",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, file_status_params.FileStatusParams),
),
cast_to=FileStatusResponse,
)
@@ -176,6 +307,9 @@ class FileResourceWithRawResponse:
def __init__(self, file: FileResource) -> None:
self._file = file
+ self.list = to_raw_response_wrapper(
+ file.list,
+ )
self.read = to_raw_response_wrapper(
file.read,
)
@@ -188,6 +322,9 @@ class AsyncFileResourceWithRawResponse:
def __init__(self, file: AsyncFileResource) -> None:
self._file = file
+ self.list = async_to_raw_response_wrapper(
+ file.list,
+ )
self.read = async_to_raw_response_wrapper(
file.read,
)
@@ -200,6 +337,9 @@ class FileResourceWithStreamingResponse:
def __init__(self, file: FileResource) -> None:
self._file = file
+ self.list = to_streamed_response_wrapper(
+ file.list,
+ )
self.read = to_streamed_response_wrapper(
file.read,
)
@@ -212,6 +352,9 @@ class AsyncFileResourceWithStreamingResponse:
def __init__(self, file: AsyncFileResource) -> None:
self._file = file
+ self.list = async_to_streamed_response_wrapper(
+ file.list,
+ )
self.read = async_to_streamed_response_wrapper(
file.read,
)
diff --git a/src/opencode_ai/resources/find.py b/src/opencode_ai/resources/find.py
index 1558e7c..7374a04 100644
--- a/src/opencode_ai/resources/find.py
+++ b/src/opencode_ai/resources/find.py
@@ -5,7 +5,7 @@
import httpx
from ..types import find_text_params, find_files_params, find_symbols_params
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
@@ -47,12 +47,13 @@ def files(
self,
*,
query: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FindFilesResponse:
"""
Find files
@@ -73,7 +74,13 @@ def files(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=maybe_transform({"query": query}, find_files_params.FindFilesParams),
+ query=maybe_transform(
+ {
+ "query": query,
+ "directory": directory,
+ },
+ find_files_params.FindFilesParams,
+ ),
),
cast_to=FindFilesResponse,
)
@@ -82,12 +89,13 @@ def symbols(
self,
*,
query: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FindSymbolsResponse:
"""
Find workspace symbols
@@ -108,7 +116,13 @@ def symbols(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=maybe_transform({"query": query}, find_symbols_params.FindSymbolsParams),
+ query=maybe_transform(
+ {
+ "query": query,
+ "directory": directory,
+ },
+ find_symbols_params.FindSymbolsParams,
+ ),
),
cast_to=FindSymbolsResponse,
)
@@ -117,12 +131,13 @@ def text(
self,
*,
pattern: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FindTextResponse:
"""
Find text in files
@@ -143,7 +158,13 @@ def text(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=maybe_transform({"pattern": pattern}, find_text_params.FindTextParams),
+ query=maybe_transform(
+ {
+ "pattern": pattern,
+ "directory": directory,
+ },
+ find_text_params.FindTextParams,
+ ),
),
cast_to=FindTextResponse,
)
@@ -173,12 +194,13 @@ async def files(
self,
*,
query: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FindFilesResponse:
"""
Find files
@@ -199,7 +221,13 @@ async def files(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=await async_maybe_transform({"query": query}, find_files_params.FindFilesParams),
+ query=await async_maybe_transform(
+ {
+ "query": query,
+ "directory": directory,
+ },
+ find_files_params.FindFilesParams,
+ ),
),
cast_to=FindFilesResponse,
)
@@ -208,12 +236,13 @@ async def symbols(
self,
*,
query: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FindSymbolsResponse:
"""
Find workspace symbols
@@ -234,7 +263,13 @@ async def symbols(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=await async_maybe_transform({"query": query}, find_symbols_params.FindSymbolsParams),
+ query=await async_maybe_transform(
+ {
+ "query": query,
+ "directory": directory,
+ },
+ find_symbols_params.FindSymbolsParams,
+ ),
),
cast_to=FindSymbolsResponse,
)
@@ -243,12 +278,13 @@ async def text(
self,
*,
pattern: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FindTextResponse:
"""
Find text in files
@@ -269,7 +305,13 @@ async def text(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=await async_maybe_transform({"pattern": pattern}, find_text_params.FindTextParams),
+ query=await async_maybe_transform(
+ {
+ "pattern": pattern,
+ "directory": directory,
+ },
+ find_text_params.FindTextParams,
+ ),
),
cast_to=FindTextResponse,
)
diff --git a/src/opencode_ai/resources/path.py b/src/opencode_ai/resources/path.py
new file mode 100644
index 0000000..6afee36
--- /dev/null
+++ b/src/opencode_ai/resources/path.py
@@ -0,0 +1,169 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..types import path_get_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..types.path import Path
+from .._base_client import make_request_options
+
+__all__ = ["PathResource", "AsyncPathResource"]
+
+
+class PathResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> PathResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return PathResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> PathResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return PathResourceWithStreamingResponse(self)
+
+ def get(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Path:
+ """
+ Get the current path
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/path",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, path_get_params.PathGetParams),
+ ),
+ cast_to=Path,
+ )
+
+
+class AsyncPathResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncPathResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncPathResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncPathResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return AsyncPathResourceWithStreamingResponse(self)
+
+ async def get(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Path:
+ """
+ Get the current path
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/path",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, path_get_params.PathGetParams),
+ ),
+ cast_to=Path,
+ )
+
+
+class PathResourceWithRawResponse:
+ def __init__(self, path: PathResource) -> None:
+ self._path = path
+
+ self.get = to_raw_response_wrapper(
+ path.get,
+ )
+
+
+class AsyncPathResourceWithRawResponse:
+ def __init__(self, path: AsyncPathResource) -> None:
+ self._path = path
+
+ self.get = async_to_raw_response_wrapper(
+ path.get,
+ )
+
+
+class PathResourceWithStreamingResponse:
+ def __init__(self, path: PathResource) -> None:
+ self._path = path
+
+ self.get = to_streamed_response_wrapper(
+ path.get,
+ )
+
+
+class AsyncPathResourceWithStreamingResponse:
+ def __init__(self, path: AsyncPathResource) -> None:
+ self._path = path
+
+ self.get = async_to_streamed_response_wrapper(
+ path.get,
+ )
diff --git a/src/opencode_ai/resources/project.py b/src/opencode_ai/resources/project.py
new file mode 100644
index 0000000..e0b94b5
--- /dev/null
+++ b/src/opencode_ai/resources/project.py
@@ -0,0 +1,254 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..types import project_list_params, project_current_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.project import Project
+from ..types.project_list_response import ProjectListResponse
+
+__all__ = ["ProjectResource", "AsyncProjectResource"]
+
+
+class ProjectResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ProjectResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return ProjectResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ProjectResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return ProjectResourceWithStreamingResponse(self)
+
+ def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProjectListResponse:
+ """
+ List all projects
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/project",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, project_list_params.ProjectListParams),
+ ),
+ cast_to=ProjectListResponse,
+ )
+
+ def current(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Project:
+ """
+ Get the current project
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/project/current",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, project_current_params.ProjectCurrentParams),
+ ),
+ cast_to=Project,
+ )
+
+
+class AsyncProjectResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncProjectResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncProjectResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return AsyncProjectResourceWithStreamingResponse(self)
+
+ async def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProjectListResponse:
+ """
+ List all projects
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/project",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, project_list_params.ProjectListParams),
+ ),
+ cast_to=ProjectListResponse,
+ )
+
+ async def current(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Project:
+ """
+ Get the current project
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/project/current",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, project_current_params.ProjectCurrentParams
+ ),
+ ),
+ cast_to=Project,
+ )
+
+
+class ProjectResourceWithRawResponse:
+ def __init__(self, project: ProjectResource) -> None:
+ self._project = project
+
+ self.list = to_raw_response_wrapper(
+ project.list,
+ )
+ self.current = to_raw_response_wrapper(
+ project.current,
+ )
+
+
+class AsyncProjectResourceWithRawResponse:
+ def __init__(self, project: AsyncProjectResource) -> None:
+ self._project = project
+
+ self.list = async_to_raw_response_wrapper(
+ project.list,
+ )
+ self.current = async_to_raw_response_wrapper(
+ project.current,
+ )
+
+
+class ProjectResourceWithStreamingResponse:
+ def __init__(self, project: ProjectResource) -> None:
+ self._project = project
+
+ self.list = to_streamed_response_wrapper(
+ project.list,
+ )
+ self.current = to_streamed_response_wrapper(
+ project.current,
+ )
+
+
+class AsyncProjectResourceWithStreamingResponse:
+ def __init__(self, project: AsyncProjectResource) -> None:
+ self._project = project
+
+ self.list = async_to_streamed_response_wrapper(
+ project.list,
+ )
+ self.current = async_to_streamed_response_wrapper(
+ project.current,
+ )
diff --git a/src/opencode_ai/resources/session.py b/src/opencode_ai/resources/session.py
deleted file mode 100644
index 7e50413..0000000
--- a/src/opencode_ai/resources/session.py
+++ /dev/null
@@ -1,1088 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import Dict, Iterable
-
-import httpx
-
-from ..types import session_chat_params, session_init_params, session_revert_params, session_summarize_params
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from .._utils import maybe_transform, async_maybe_transform
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
- to_raw_response_wrapper,
- to_streamed_response_wrapper,
- async_to_raw_response_wrapper,
- async_to_streamed_response_wrapper,
-)
-from .._base_client import make_request_options
-from ..types.session import Session
-from ..types.assistant_message import AssistantMessage
-from ..types.session_init_response import SessionInitResponse
-from ..types.session_list_response import SessionListResponse
-from ..types.session_abort_response import SessionAbortResponse
-from ..types.session_delete_response import SessionDeleteResponse
-from ..types.session_messages_response import SessionMessagesResponse
-from ..types.session_summarize_response import SessionSummarizeResponse
-
-__all__ = ["SessionResource", "AsyncSessionResource"]
-
-
-class SessionResource(SyncAPIResource):
- @cached_property
- def with_raw_response(self) -> SessionResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
- """
- return SessionResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> SessionResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
- """
- return SessionResourceWithStreamingResponse(self)
-
- def create(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """Create a new session"""
- return self._post(
- "/session",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- def list(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionListResponse:
- """List all sessions"""
- return self._get(
- "/session",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionListResponse,
- )
-
- def delete(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionDeleteResponse:
- """
- Delete a session and all its data
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._delete(
- f"/session/{id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionDeleteResponse,
- )
-
- def abort(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionAbortResponse:
- """
- Abort a session
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/session/{id}/abort",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionAbortResponse,
- )
-
- def chat(
- self,
- id: str,
- *,
- model_id: str,
- parts: Iterable[session_chat_params.Part],
- provider_id: str,
- message_id: str | NotGiven = NOT_GIVEN,
- mode: str | NotGiven = NOT_GIVEN,
- system: str | NotGiven = NOT_GIVEN,
- tools: Dict[str, bool] | NotGiven = NOT_GIVEN,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AssistantMessage:
- """
- Create and send a new message to a session
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/session/{id}/message",
- body=maybe_transform(
- {
- "model_id": model_id,
- "parts": parts,
- "provider_id": provider_id,
- "message_id": message_id,
- "mode": mode,
- "system": system,
- "tools": tools,
- },
- session_chat_params.SessionChatParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AssistantMessage,
- )
-
- def init(
- self,
- id: str,
- *,
- message_id: str,
- model_id: str,
- provider_id: str,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionInitResponse:
- """
- Analyze the app and create an AGENTS.md file
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/session/{id}/init",
- body=maybe_transform(
- {
- "message_id": message_id,
- "model_id": model_id,
- "provider_id": provider_id,
- },
- session_init_params.SessionInitParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionInitResponse,
- )
-
- def messages(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionMessagesResponse:
- """
- List messages for a session
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._get(
- f"/session/{id}/message",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionMessagesResponse,
- )
-
- def revert(
- self,
- id: str,
- *,
- message_id: str,
- part_id: str | NotGiven = NOT_GIVEN,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Revert a message
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/session/{id}/revert",
- body=maybe_transform(
- {
- "message_id": message_id,
- "part_id": part_id,
- },
- session_revert_params.SessionRevertParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- def share(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Share a session
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/session/{id}/share",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- def summarize(
- self,
- id: str,
- *,
- model_id: str,
- provider_id: str,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionSummarizeResponse:
- """
- Summarize the session
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/session/{id}/summarize",
- body=maybe_transform(
- {
- "model_id": model_id,
- "provider_id": provider_id,
- },
- session_summarize_params.SessionSummarizeParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionSummarizeResponse,
- )
-
- def unrevert(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Restore all reverted messages
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/session/{id}/unrevert",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- def unshare(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Unshare the session
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._delete(
- f"/session/{id}/share",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
-
-class AsyncSessionResource(AsyncAPIResource):
- @cached_property
- def with_raw_response(self) -> AsyncSessionResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
- """
- return AsyncSessionResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> AsyncSessionResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
- """
- return AsyncSessionResourceWithStreamingResponse(self)
-
- async def create(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """Create a new session"""
- return await self._post(
- "/session",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- async def list(
- self,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionListResponse:
- """List all sessions"""
- return await self._get(
- "/session",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionListResponse,
- )
-
- async def delete(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionDeleteResponse:
- """
- Delete a session and all its data
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._delete(
- f"/session/{id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionDeleteResponse,
- )
-
- async def abort(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionAbortResponse:
- """
- Abort a session
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/session/{id}/abort",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionAbortResponse,
- )
-
- async def chat(
- self,
- id: str,
- *,
- model_id: str,
- parts: Iterable[session_chat_params.Part],
- provider_id: str,
- message_id: str | NotGiven = NOT_GIVEN,
- mode: str | NotGiven = NOT_GIVEN,
- system: str | NotGiven = NOT_GIVEN,
- tools: Dict[str, bool] | NotGiven = NOT_GIVEN,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AssistantMessage:
- """
- Create and send a new message to a session
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/session/{id}/message",
- body=await async_maybe_transform(
- {
- "model_id": model_id,
- "parts": parts,
- "provider_id": provider_id,
- "message_id": message_id,
- "mode": mode,
- "system": system,
- "tools": tools,
- },
- session_chat_params.SessionChatParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AssistantMessage,
- )
-
- async def init(
- self,
- id: str,
- *,
- message_id: str,
- model_id: str,
- provider_id: str,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionInitResponse:
- """
- Analyze the app and create an AGENTS.md file
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/session/{id}/init",
- body=await async_maybe_transform(
- {
- "message_id": message_id,
- "model_id": model_id,
- "provider_id": provider_id,
- },
- session_init_params.SessionInitParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionInitResponse,
- )
-
- async def messages(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionMessagesResponse:
- """
- List messages for a session
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._get(
- f"/session/{id}/message",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionMessagesResponse,
- )
-
- async def revert(
- self,
- id: str,
- *,
- message_id: str,
- part_id: str | NotGiven = NOT_GIVEN,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Revert a message
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/session/{id}/revert",
- body=await async_maybe_transform(
- {
- "message_id": message_id,
- "part_id": part_id,
- },
- session_revert_params.SessionRevertParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- async def share(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Share a session
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/session/{id}/share",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- async def summarize(
- self,
- id: str,
- *,
- model_id: str,
- provider_id: str,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SessionSummarizeResponse:
- """
- Summarize the session
-
- Args:
- id: Session ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/session/{id}/summarize",
- body=await async_maybe_transform(
- {
- "model_id": model_id,
- "provider_id": provider_id,
- },
- session_summarize_params.SessionSummarizeParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SessionSummarizeResponse,
- )
-
- async def unrevert(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Restore all reverted messages
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/session/{id}/unrevert",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
- async def unshare(
- self,
- id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Session:
- """
- Unshare the session
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._delete(
- f"/session/{id}/share",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=Session,
- )
-
-
-class SessionResourceWithRawResponse:
- def __init__(self, session: SessionResource) -> None:
- self._session = session
-
- self.create = to_raw_response_wrapper(
- session.create,
- )
- self.list = to_raw_response_wrapper(
- session.list,
- )
- self.delete = to_raw_response_wrapper(
- session.delete,
- )
- self.abort = to_raw_response_wrapper(
- session.abort,
- )
- self.chat = to_raw_response_wrapper(
- session.chat,
- )
- self.init = to_raw_response_wrapper(
- session.init,
- )
- self.messages = to_raw_response_wrapper(
- session.messages,
- )
- self.revert = to_raw_response_wrapper(
- session.revert,
- )
- self.share = to_raw_response_wrapper(
- session.share,
- )
- self.summarize = to_raw_response_wrapper(
- session.summarize,
- )
- self.unrevert = to_raw_response_wrapper(
- session.unrevert,
- )
- self.unshare = to_raw_response_wrapper(
- session.unshare,
- )
-
-
-class AsyncSessionResourceWithRawResponse:
- def __init__(self, session: AsyncSessionResource) -> None:
- self._session = session
-
- self.create = async_to_raw_response_wrapper(
- session.create,
- )
- self.list = async_to_raw_response_wrapper(
- session.list,
- )
- self.delete = async_to_raw_response_wrapper(
- session.delete,
- )
- self.abort = async_to_raw_response_wrapper(
- session.abort,
- )
- self.chat = async_to_raw_response_wrapper(
- session.chat,
- )
- self.init = async_to_raw_response_wrapper(
- session.init,
- )
- self.messages = async_to_raw_response_wrapper(
- session.messages,
- )
- self.revert = async_to_raw_response_wrapper(
- session.revert,
- )
- self.share = async_to_raw_response_wrapper(
- session.share,
- )
- self.summarize = async_to_raw_response_wrapper(
- session.summarize,
- )
- self.unrevert = async_to_raw_response_wrapper(
- session.unrevert,
- )
- self.unshare = async_to_raw_response_wrapper(
- session.unshare,
- )
-
-
-class SessionResourceWithStreamingResponse:
- def __init__(self, session: SessionResource) -> None:
- self._session = session
-
- self.create = to_streamed_response_wrapper(
- session.create,
- )
- self.list = to_streamed_response_wrapper(
- session.list,
- )
- self.delete = to_streamed_response_wrapper(
- session.delete,
- )
- self.abort = to_streamed_response_wrapper(
- session.abort,
- )
- self.chat = to_streamed_response_wrapper(
- session.chat,
- )
- self.init = to_streamed_response_wrapper(
- session.init,
- )
- self.messages = to_streamed_response_wrapper(
- session.messages,
- )
- self.revert = to_streamed_response_wrapper(
- session.revert,
- )
- self.share = to_streamed_response_wrapper(
- session.share,
- )
- self.summarize = to_streamed_response_wrapper(
- session.summarize,
- )
- self.unrevert = to_streamed_response_wrapper(
- session.unrevert,
- )
- self.unshare = to_streamed_response_wrapper(
- session.unshare,
- )
-
-
-class AsyncSessionResourceWithStreamingResponse:
- def __init__(self, session: AsyncSessionResource) -> None:
- self._session = session
-
- self.create = async_to_streamed_response_wrapper(
- session.create,
- )
- self.list = async_to_streamed_response_wrapper(
- session.list,
- )
- self.delete = async_to_streamed_response_wrapper(
- session.delete,
- )
- self.abort = async_to_streamed_response_wrapper(
- session.abort,
- )
- self.chat = async_to_streamed_response_wrapper(
- session.chat,
- )
- self.init = async_to_streamed_response_wrapper(
- session.init,
- )
- self.messages = async_to_streamed_response_wrapper(
- session.messages,
- )
- self.revert = async_to_streamed_response_wrapper(
- session.revert,
- )
- self.share = async_to_streamed_response_wrapper(
- session.share,
- )
- self.summarize = async_to_streamed_response_wrapper(
- session.summarize,
- )
- self.unrevert = async_to_streamed_response_wrapper(
- session.unrevert,
- )
- self.unshare = async_to_streamed_response_wrapper(
- session.unshare,
- )
diff --git a/src/opencode_ai/resources/session/__init__.py b/src/opencode_ai/resources/session/__init__.py
new file mode 100644
index 0000000..60a023e
--- /dev/null
+++ b/src/opencode_ai/resources/session/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .session import (
+ SessionResource,
+ AsyncSessionResource,
+ SessionResourceWithRawResponse,
+ AsyncSessionResourceWithRawResponse,
+ SessionResourceWithStreamingResponse,
+ AsyncSessionResourceWithStreamingResponse,
+)
+from .permissions import (
+ PermissionsResource,
+ AsyncPermissionsResource,
+ PermissionsResourceWithRawResponse,
+ AsyncPermissionsResourceWithRawResponse,
+ PermissionsResourceWithStreamingResponse,
+ AsyncPermissionsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "PermissionsResource",
+ "AsyncPermissionsResource",
+ "PermissionsResourceWithRawResponse",
+ "AsyncPermissionsResourceWithRawResponse",
+ "PermissionsResourceWithStreamingResponse",
+ "AsyncPermissionsResourceWithStreamingResponse",
+ "SessionResource",
+ "AsyncSessionResource",
+ "SessionResourceWithRawResponse",
+ "AsyncSessionResourceWithRawResponse",
+ "SessionResourceWithStreamingResponse",
+ "AsyncSessionResourceWithStreamingResponse",
+]
diff --git a/src/opencode_ai/resources/session/permissions.py b/src/opencode_ai/resources/session/permissions.py
new file mode 100644
index 0000000..a4d9c23
--- /dev/null
+++ b/src/opencode_ai/resources/session/permissions.py
@@ -0,0 +1,189 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.session import permission_respond_params
+from ...types.session.permission_respond_response import PermissionRespondResponse
+
+__all__ = ["PermissionsResource", "AsyncPermissionsResource"]
+
+
+class PermissionsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> PermissionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return PermissionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> PermissionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return PermissionsResourceWithStreamingResponse(self)
+
+ def respond(
+ self,
+ permission_id: str,
+ *,
+ id: str,
+ response: Literal["once", "always", "reject"],
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> PermissionRespondResponse:
+ """
+ Respond to a permission request
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not permission_id:
+ raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}")
+ return self._post(
+ f"/session/{id}/permissions/{permission_id}",
+ body=maybe_transform({"response": response}, permission_respond_params.PermissionRespondParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, permission_respond_params.PermissionRespondParams),
+ ),
+ cast_to=PermissionRespondResponse,
+ )
+
+
+class AsyncPermissionsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncPermissionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncPermissionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncPermissionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return AsyncPermissionsResourceWithStreamingResponse(self)
+
+ async def respond(
+ self,
+ permission_id: str,
+ *,
+ id: str,
+ response: Literal["once", "always", "reject"],
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> PermissionRespondResponse:
+ """
+ Respond to a permission request
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not permission_id:
+ raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}")
+ return await self._post(
+ f"/session/{id}/permissions/{permission_id}",
+ body=await async_maybe_transform({"response": response}, permission_respond_params.PermissionRespondParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, permission_respond_params.PermissionRespondParams
+ ),
+ ),
+ cast_to=PermissionRespondResponse,
+ )
+
+
+class PermissionsResourceWithRawResponse:
+ def __init__(self, permissions: PermissionsResource) -> None:
+ self._permissions = permissions
+
+ self.respond = to_raw_response_wrapper(
+ permissions.respond,
+ )
+
+
+class AsyncPermissionsResourceWithRawResponse:
+ def __init__(self, permissions: AsyncPermissionsResource) -> None:
+ self._permissions = permissions
+
+ self.respond = async_to_raw_response_wrapper(
+ permissions.respond,
+ )
+
+
+class PermissionsResourceWithStreamingResponse:
+ def __init__(self, permissions: PermissionsResource) -> None:
+ self._permissions = permissions
+
+ self.respond = to_streamed_response_wrapper(
+ permissions.respond,
+ )
+
+
+class AsyncPermissionsResourceWithStreamingResponse:
+ def __init__(self, permissions: AsyncPermissionsResource) -> None:
+ self._permissions = permissions
+
+ self.respond = async_to_streamed_response_wrapper(
+ permissions.respond,
+ )
diff --git a/src/opencode_ai/resources/session/session.py b/src/opencode_ai/resources/session/session.py
new file mode 100644
index 0000000..950acf1
--- /dev/null
+++ b/src/opencode_ai/resources/session/session.py
@@ -0,0 +1,1937 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable
+
+import httpx
+
+from ...types import (
+ session_get_params,
+ session_init_params,
+ session_list_params,
+ session_abort_params,
+ session_share_params,
+ session_shell_params,
+ session_create_params,
+ session_delete_params,
+ session_prompt_params,
+ session_revert_params,
+ session_update_params,
+ session_command_params,
+ session_message_params,
+ session_unshare_params,
+ session_children_params,
+ session_messages_params,
+ session_unrevert_params,
+ session_summarize_params,
+)
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .permissions import (
+ PermissionsResource,
+ AsyncPermissionsResource,
+ PermissionsResourceWithRawResponse,
+ AsyncPermissionsResourceWithRawResponse,
+ PermissionsResourceWithStreamingResponse,
+ AsyncPermissionsResourceWithStreamingResponse,
+)
+from ..._base_client import make_request_options
+from ...types.session.session import Session
+from ...types.assistant_message import AssistantMessage
+from ...types.session_init_response import SessionInitResponse
+from ...types.session_list_response import SessionListResponse
+from ...types.session_abort_response import SessionAbortResponse
+from ...types.session_delete_response import SessionDeleteResponse
+from ...types.session_prompt_response import SessionPromptResponse
+from ...types.session_command_response import SessionCommandResponse
+from ...types.session_message_response import SessionMessageResponse
+from ...types.session_children_response import SessionChildrenResponse
+from ...types.session_messages_response import SessionMessagesResponse
+from ...types.session_summarize_response import SessionSummarizeResponse
+
+__all__ = ["SessionResource", "AsyncSessionResource"]
+
+
+class SessionResource(SyncAPIResource):
+ @cached_property
+ def permissions(self) -> PermissionsResource:
+ return PermissionsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> SessionResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return SessionResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SessionResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return SessionResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ directory: str | Omit = omit,
+ parent_id: str | Omit = omit,
+ title: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Create a new session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/session",
+ body=maybe_transform(
+ {
+ "parent_id": parent_id,
+ "title": title,
+ },
+ session_create_params.SessionCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_create_params.SessionCreateParams),
+ ),
+ cast_to=Session,
+ )
+
+ def update(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ title: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Update session properties
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._patch(
+ f"/session/{id}",
+ body=maybe_transform({"title": title}, session_update_params.SessionUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_update_params.SessionUpdateParams),
+ ),
+ cast_to=Session,
+ )
+
+ def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionListResponse:
+ """
+ List all sessions
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/session",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_list_params.SessionListParams),
+ ),
+ cast_to=SessionListResponse,
+ )
+
+ def delete(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionDeleteResponse:
+ """
+ Delete a session and all its data
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._delete(
+ f"/session/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_delete_params.SessionDeleteParams),
+ ),
+ cast_to=SessionDeleteResponse,
+ )
+
+ def abort(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionAbortResponse:
+ """
+ Abort a session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/abort",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_abort_params.SessionAbortParams),
+ ),
+ cast_to=SessionAbortResponse,
+ )
+
+ def children(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionChildrenResponse:
+ """
+ Get a session's children
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/session/{id}/children",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_children_params.SessionChildrenParams),
+ ),
+ cast_to=SessionChildrenResponse,
+ )
+
+ def command(
+ self,
+ id: str,
+ *,
+ arguments: str,
+ command: str,
+ directory: str | Omit = omit,
+ agent: str | Omit = omit,
+ message_id: str | Omit = omit,
+ model: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionCommandResponse:
+ """
+ Send a new command to a session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/command",
+ body=maybe_transform(
+ {
+ "arguments": arguments,
+ "command": command,
+ "agent": agent,
+ "message_id": message_id,
+ "model": model,
+ },
+ session_command_params.SessionCommandParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_command_params.SessionCommandParams),
+ ),
+ cast_to=SessionCommandResponse,
+ )
+
+ def get(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Get session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/session/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_get_params.SessionGetParams),
+ ),
+ cast_to=Session,
+ )
+
+ def init(
+ self,
+ id: str,
+ *,
+ message_id: str,
+ model_id: str,
+ provider_id: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionInitResponse:
+ """
+ Analyze the app and create an AGENTS.md file
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/init",
+ body=maybe_transform(
+ {
+ "message_id": message_id,
+ "model_id": model_id,
+ "provider_id": provider_id,
+ },
+ session_init_params.SessionInitParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_init_params.SessionInitParams),
+ ),
+ cast_to=SessionInitResponse,
+ )
+
+ def message(
+ self,
+ message_id: str,
+ *,
+ id: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionMessageResponse:
+ """
+ Get a message from a session
+
+ Args:
+ id: Session ID
+
+ message_id: Message ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not message_id:
+ raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}")
+ return self._get(
+ f"/session/{id}/message/{message_id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_message_params.SessionMessageParams),
+ ),
+ cast_to=SessionMessageResponse,
+ )
+
+ def messages(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionMessagesResponse:
+ """
+ List messages for a session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/session/{id}/message",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_messages_params.SessionMessagesParams),
+ ),
+ cast_to=SessionMessagesResponse,
+ )
+
+ def prompt(
+ self,
+ id: str,
+ *,
+ parts: Iterable[session_prompt_params.Part],
+ directory: str | Omit = omit,
+ agent: str | Omit = omit,
+ message_id: str | Omit = omit,
+ model: session_prompt_params.Model | Omit = omit,
+ system: str | Omit = omit,
+ tools: Dict[str, bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionPromptResponse:
+ """
+ Create and send a new message to a session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/message",
+ body=maybe_transform(
+ {
+ "parts": parts,
+ "agent": agent,
+ "message_id": message_id,
+ "model": model,
+ "system": system,
+ "tools": tools,
+ },
+ session_prompt_params.SessionPromptParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_prompt_params.SessionPromptParams),
+ ),
+ cast_to=SessionPromptResponse,
+ )
+
+ def revert(
+ self,
+ id: str,
+ *,
+ message_id: str,
+ directory: str | Omit = omit,
+ part_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Revert a message
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/revert",
+ body=maybe_transform(
+ {
+ "message_id": message_id,
+ "part_id": part_id,
+ },
+ session_revert_params.SessionRevertParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_revert_params.SessionRevertParams),
+ ),
+ cast_to=Session,
+ )
+
+ def share(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Share a session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/share",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_share_params.SessionShareParams),
+ ),
+ cast_to=Session,
+ )
+
+ def shell(
+ self,
+ id: str,
+ *,
+ agent: str,
+ command: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AssistantMessage:
+ """
+ Run a shell command
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/shell",
+ body=maybe_transform(
+ {
+ "agent": agent,
+ "command": command,
+ },
+ session_shell_params.SessionShellParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_shell_params.SessionShellParams),
+ ),
+ cast_to=AssistantMessage,
+ )
+
+ def summarize(
+ self,
+ id: str,
+ *,
+ model_id: str,
+ provider_id: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionSummarizeResponse:
+ """
+ Summarize the session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/summarize",
+ body=maybe_transform(
+ {
+ "model_id": model_id,
+ "provider_id": provider_id,
+ },
+ session_summarize_params.SessionSummarizeParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_summarize_params.SessionSummarizeParams),
+ ),
+ cast_to=SessionSummarizeResponse,
+ )
+
+ def unrevert(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Restore all reverted messages
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/session/{id}/unrevert",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_unrevert_params.SessionUnrevertParams),
+ ),
+ cast_to=Session,
+ )
+
+ def unshare(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Unshare the session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._delete(
+ f"/session/{id}/share",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, session_unshare_params.SessionUnshareParams),
+ ),
+ cast_to=Session,
+ )
+
+
+class AsyncSessionResource(AsyncAPIResource):
+ @cached_property
+ def permissions(self) -> AsyncPermissionsResource:
+ return AsyncPermissionsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncSessionResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSessionResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSessionResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response
+ """
+ return AsyncSessionResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ directory: str | Omit = omit,
+ parent_id: str | Omit = omit,
+ title: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Create a new session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/session",
+ body=await async_maybe_transform(
+ {
+ "parent_id": parent_id,
+ "title": title,
+ },
+ session_create_params.SessionCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_create_params.SessionCreateParams),
+ ),
+ cast_to=Session,
+ )
+
+ async def update(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ title: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Update session properties
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._patch(
+ f"/session/{id}",
+ body=await async_maybe_transform({"title": title}, session_update_params.SessionUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_update_params.SessionUpdateParams),
+ ),
+ cast_to=Session,
+ )
+
+ async def list(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionListResponse:
+ """
+ List all sessions
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/session",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_list_params.SessionListParams),
+ ),
+ cast_to=SessionListResponse,
+ )
+
+ async def delete(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionDeleteResponse:
+ """
+ Delete a session and all its data
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._delete(
+ f"/session/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_delete_params.SessionDeleteParams),
+ ),
+ cast_to=SessionDeleteResponse,
+ )
+
+ async def abort(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionAbortResponse:
+ """
+ Abort a session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/abort",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_abort_params.SessionAbortParams),
+ ),
+ cast_to=SessionAbortResponse,
+ )
+
+ async def children(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionChildrenResponse:
+ """
+ Get a session's children
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/session/{id}/children",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, session_children_params.SessionChildrenParams
+ ),
+ ),
+ cast_to=SessionChildrenResponse,
+ )
+
+ async def command(
+ self,
+ id: str,
+ *,
+ arguments: str,
+ command: str,
+ directory: str | Omit = omit,
+ agent: str | Omit = omit,
+ message_id: str | Omit = omit,
+ model: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionCommandResponse:
+ """
+ Send a new command to a session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/command",
+ body=await async_maybe_transform(
+ {
+ "arguments": arguments,
+ "command": command,
+ "agent": agent,
+ "message_id": message_id,
+ "model": model,
+ },
+ session_command_params.SessionCommandParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, session_command_params.SessionCommandParams
+ ),
+ ),
+ cast_to=SessionCommandResponse,
+ )
+
+ async def get(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Get session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/session/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_get_params.SessionGetParams),
+ ),
+ cast_to=Session,
+ )
+
+ async def init(
+ self,
+ id: str,
+ *,
+ message_id: str,
+ model_id: str,
+ provider_id: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionInitResponse:
+ """
+ Analyze the app and create an AGENTS.md file
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/init",
+ body=await async_maybe_transform(
+ {
+ "message_id": message_id,
+ "model_id": model_id,
+ "provider_id": provider_id,
+ },
+ session_init_params.SessionInitParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_init_params.SessionInitParams),
+ ),
+ cast_to=SessionInitResponse,
+ )
+
+ async def message(
+ self,
+ message_id: str,
+ *,
+ id: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionMessageResponse:
+ """
+ Get a message from a session
+
+ Args:
+ id: Session ID
+
+ message_id: Message ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not message_id:
+ raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}")
+ return await self._get(
+ f"/session/{id}/message/{message_id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, session_message_params.SessionMessageParams
+ ),
+ ),
+ cast_to=SessionMessageResponse,
+ )
+
+ async def messages(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionMessagesResponse:
+ """
+ List messages for a session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/session/{id}/message",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, session_messages_params.SessionMessagesParams
+ ),
+ ),
+ cast_to=SessionMessagesResponse,
+ )
+
+ async def prompt(
+ self,
+ id: str,
+ *,
+ parts: Iterable[session_prompt_params.Part],
+ directory: str | Omit = omit,
+ agent: str | Omit = omit,
+ message_id: str | Omit = omit,
+ model: session_prompt_params.Model | Omit = omit,
+ system: str | Omit = omit,
+ tools: Dict[str, bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionPromptResponse:
+ """
+ Create and send a new message to a session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/message",
+ body=await async_maybe_transform(
+ {
+ "parts": parts,
+ "agent": agent,
+ "message_id": message_id,
+ "model": model,
+ "system": system,
+ "tools": tools,
+ },
+ session_prompt_params.SessionPromptParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_prompt_params.SessionPromptParams),
+ ),
+ cast_to=SessionPromptResponse,
+ )
+
+ async def revert(
+ self,
+ id: str,
+ *,
+ message_id: str,
+ directory: str | Omit = omit,
+ part_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Revert a message
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/revert",
+ body=await async_maybe_transform(
+ {
+ "message_id": message_id,
+ "part_id": part_id,
+ },
+ session_revert_params.SessionRevertParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_revert_params.SessionRevertParams),
+ ),
+ cast_to=Session,
+ )
+
+ async def share(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Share a session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/share",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_share_params.SessionShareParams),
+ ),
+ cast_to=Session,
+ )
+
+ async def shell(
+ self,
+ id: str,
+ *,
+ agent: str,
+ command: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AssistantMessage:
+ """
+ Run a shell command
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/shell",
+ body=await async_maybe_transform(
+ {
+ "agent": agent,
+ "command": command,
+ },
+ session_shell_params.SessionShellParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, session_shell_params.SessionShellParams),
+ ),
+ cast_to=AssistantMessage,
+ )
+
+ async def summarize(
+ self,
+ id: str,
+ *,
+ model_id: str,
+ provider_id: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SessionSummarizeResponse:
+ """
+ Summarize the session
+
+ Args:
+ id: Session ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/summarize",
+ body=await async_maybe_transform(
+ {
+ "model_id": model_id,
+ "provider_id": provider_id,
+ },
+ session_summarize_params.SessionSummarizeParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, session_summarize_params.SessionSummarizeParams
+ ),
+ ),
+ cast_to=SessionSummarizeResponse,
+ )
+
+ async def unrevert(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Restore all reverted messages
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/session/{id}/unrevert",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, session_unrevert_params.SessionUnrevertParams
+ ),
+ ),
+ cast_to=Session,
+ )
+
+ async def unshare(
+ self,
+ id: str,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> Session:
+ """
+ Unshare the session
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._delete(
+ f"/session/{id}/share",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, session_unshare_params.SessionUnshareParams
+ ),
+ ),
+ cast_to=Session,
+ )
+
+
+class SessionResourceWithRawResponse:
+ def __init__(self, session: SessionResource) -> None:
+ self._session = session
+
+ self.create = to_raw_response_wrapper(
+ session.create,
+ )
+ self.update = to_raw_response_wrapper(
+ session.update,
+ )
+ self.list = to_raw_response_wrapper(
+ session.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ session.delete,
+ )
+ self.abort = to_raw_response_wrapper(
+ session.abort,
+ )
+ self.children = to_raw_response_wrapper(
+ session.children,
+ )
+ self.command = to_raw_response_wrapper(
+ session.command,
+ )
+ self.get = to_raw_response_wrapper(
+ session.get,
+ )
+ self.init = to_raw_response_wrapper(
+ session.init,
+ )
+ self.message = to_raw_response_wrapper(
+ session.message,
+ )
+ self.messages = to_raw_response_wrapper(
+ session.messages,
+ )
+ self.prompt = to_raw_response_wrapper(
+ session.prompt,
+ )
+ self.revert = to_raw_response_wrapper(
+ session.revert,
+ )
+ self.share = to_raw_response_wrapper(
+ session.share,
+ )
+ self.shell = to_raw_response_wrapper(
+ session.shell,
+ )
+ self.summarize = to_raw_response_wrapper(
+ session.summarize,
+ )
+ self.unrevert = to_raw_response_wrapper(
+ session.unrevert,
+ )
+ self.unshare = to_raw_response_wrapper(
+ session.unshare,
+ )
+
+ @cached_property
+ def permissions(self) -> PermissionsResourceWithRawResponse:
+ return PermissionsResourceWithRawResponse(self._session.permissions)
+
+
+class AsyncSessionResourceWithRawResponse:
+ def __init__(self, session: AsyncSessionResource) -> None:
+ self._session = session
+
+ self.create = async_to_raw_response_wrapper(
+ session.create,
+ )
+ self.update = async_to_raw_response_wrapper(
+ session.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ session.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ session.delete,
+ )
+ self.abort = async_to_raw_response_wrapper(
+ session.abort,
+ )
+ self.children = async_to_raw_response_wrapper(
+ session.children,
+ )
+ self.command = async_to_raw_response_wrapper(
+ session.command,
+ )
+ self.get = async_to_raw_response_wrapper(
+ session.get,
+ )
+ self.init = async_to_raw_response_wrapper(
+ session.init,
+ )
+ self.message = async_to_raw_response_wrapper(
+ session.message,
+ )
+ self.messages = async_to_raw_response_wrapper(
+ session.messages,
+ )
+ self.prompt = async_to_raw_response_wrapper(
+ session.prompt,
+ )
+ self.revert = async_to_raw_response_wrapper(
+ session.revert,
+ )
+ self.share = async_to_raw_response_wrapper(
+ session.share,
+ )
+ self.shell = async_to_raw_response_wrapper(
+ session.shell,
+ )
+ self.summarize = async_to_raw_response_wrapper(
+ session.summarize,
+ )
+ self.unrevert = async_to_raw_response_wrapper(
+ session.unrevert,
+ )
+ self.unshare = async_to_raw_response_wrapper(
+ session.unshare,
+ )
+
+ @cached_property
+ def permissions(self) -> AsyncPermissionsResourceWithRawResponse:
+ return AsyncPermissionsResourceWithRawResponse(self._session.permissions)
+
+
+class SessionResourceWithStreamingResponse:
+ def __init__(self, session: SessionResource) -> None:
+ self._session = session
+
+ self.create = to_streamed_response_wrapper(
+ session.create,
+ )
+ self.update = to_streamed_response_wrapper(
+ session.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ session.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ session.delete,
+ )
+ self.abort = to_streamed_response_wrapper(
+ session.abort,
+ )
+ self.children = to_streamed_response_wrapper(
+ session.children,
+ )
+ self.command = to_streamed_response_wrapper(
+ session.command,
+ )
+ self.get = to_streamed_response_wrapper(
+ session.get,
+ )
+ self.init = to_streamed_response_wrapper(
+ session.init,
+ )
+ self.message = to_streamed_response_wrapper(
+ session.message,
+ )
+ self.messages = to_streamed_response_wrapper(
+ session.messages,
+ )
+ self.prompt = to_streamed_response_wrapper(
+ session.prompt,
+ )
+ self.revert = to_streamed_response_wrapper(
+ session.revert,
+ )
+ self.share = to_streamed_response_wrapper(
+ session.share,
+ )
+ self.shell = to_streamed_response_wrapper(
+ session.shell,
+ )
+ self.summarize = to_streamed_response_wrapper(
+ session.summarize,
+ )
+ self.unrevert = to_streamed_response_wrapper(
+ session.unrevert,
+ )
+ self.unshare = to_streamed_response_wrapper(
+ session.unshare,
+ )
+
+ @cached_property
+ def permissions(self) -> PermissionsResourceWithStreamingResponse:
+ return PermissionsResourceWithStreamingResponse(self._session.permissions)
+
+
+class AsyncSessionResourceWithStreamingResponse:
+ def __init__(self, session: AsyncSessionResource) -> None:
+ self._session = session
+
+ self.create = async_to_streamed_response_wrapper(
+ session.create,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ session.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ session.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ session.delete,
+ )
+ self.abort = async_to_streamed_response_wrapper(
+ session.abort,
+ )
+ self.children = async_to_streamed_response_wrapper(
+ session.children,
+ )
+ self.command = async_to_streamed_response_wrapper(
+ session.command,
+ )
+ self.get = async_to_streamed_response_wrapper(
+ session.get,
+ )
+ self.init = async_to_streamed_response_wrapper(
+ session.init,
+ )
+ self.message = async_to_streamed_response_wrapper(
+ session.message,
+ )
+ self.messages = async_to_streamed_response_wrapper(
+ session.messages,
+ )
+ self.prompt = async_to_streamed_response_wrapper(
+ session.prompt,
+ )
+ self.revert = async_to_streamed_response_wrapper(
+ session.revert,
+ )
+ self.share = async_to_streamed_response_wrapper(
+ session.share,
+ )
+ self.shell = async_to_streamed_response_wrapper(
+ session.shell,
+ )
+ self.summarize = async_to_streamed_response_wrapper(
+ session.summarize,
+ )
+ self.unrevert = async_to_streamed_response_wrapper(
+ session.unrevert,
+ )
+ self.unshare = async_to_streamed_response_wrapper(
+ session.unshare,
+ )
+
+ @cached_property
+ def permissions(self) -> AsyncPermissionsResourceWithStreamingResponse:
+ return AsyncPermissionsResourceWithStreamingResponse(self._session.permissions)
diff --git a/src/opencode_ai/resources/tui.py b/src/opencode_ai/resources/tui.py
index 7194e7a..11ffd2d 100644
--- a/src/opencode_ai/resources/tui.py
+++ b/src/opencode_ai/resources/tui.py
@@ -2,10 +2,22 @@
from __future__ import annotations
+from typing_extensions import Literal
+
import httpx
-from ..types import tui_append_prompt_params
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..types import (
+ tui_open_help_params,
+ tui_show_toast_params,
+ tui_open_models_params,
+ tui_open_themes_params,
+ tui_clear_prompt_params,
+ tui_append_prompt_params,
+ tui_open_sessions_params,
+ tui_submit_prompt_params,
+ tui_execute_command_params,
+)
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
@@ -17,7 +29,14 @@
)
from .._base_client import make_request_options
from ..types.tui_open_help_response import TuiOpenHelpResponse
+from ..types.tui_show_toast_response import TuiShowToastResponse
+from ..types.tui_open_models_response import TuiOpenModelsResponse
+from ..types.tui_open_themes_response import TuiOpenThemesResponse
+from ..types.tui_clear_prompt_response import TuiClearPromptResponse
from ..types.tui_append_prompt_response import TuiAppendPromptResponse
+from ..types.tui_open_sessions_response import TuiOpenSessionsResponse
+from ..types.tui_submit_prompt_response import TuiSubmitPromptResponse
+from ..types.tui_execute_command_response import TuiExecuteCommandResponse
__all__ = ["TuiResource", "AsyncTuiResource"]
@@ -46,12 +65,13 @@ def append_prompt(
self,
*,
text: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TuiAppendPromptResponse:
"""
Append prompt to the TUI
@@ -69,30 +89,309 @@ def append_prompt(
"/tui/append-prompt",
body=maybe_transform({"text": text}, tui_append_prompt_params.TuiAppendPromptParams),
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_append_prompt_params.TuiAppendPromptParams),
),
cast_to=TuiAppendPromptResponse,
)
+ def clear_prompt(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiClearPromptResponse:
+ """
+ Clear the prompt
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/tui/clear-prompt",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_clear_prompt_params.TuiClearPromptParams),
+ ),
+ cast_to=TuiClearPromptResponse,
+ )
+
+ def execute_command(
+ self,
+ *,
+ command: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiExecuteCommandResponse:
+ """Execute a TUI command (e.g.
+
+ agent_cycle)
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/tui/execute-command",
+ body=maybe_transform({"command": command}, tui_execute_command_params.TuiExecuteCommandParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_execute_command_params.TuiExecuteCommandParams),
+ ),
+ cast_to=TuiExecuteCommandResponse,
+ )
+
def open_help(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TuiOpenHelpResponse:
- """Open the help dialog"""
+ """
+ Open the help dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return self._post(
"/tui/open-help",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_open_help_params.TuiOpenHelpParams),
),
cast_to=TuiOpenHelpResponse,
)
+ def open_models(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiOpenModelsResponse:
+ """
+ Open the model dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/tui/open-models",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_open_models_params.TuiOpenModelsParams),
+ ),
+ cast_to=TuiOpenModelsResponse,
+ )
+
+ def open_sessions(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiOpenSessionsResponse:
+ """
+ Open the session dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/tui/open-sessions",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_open_sessions_params.TuiOpenSessionsParams),
+ ),
+ cast_to=TuiOpenSessionsResponse,
+ )
+
+ def open_themes(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiOpenThemesResponse:
+ """
+ Open the theme dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/tui/open-themes",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_open_themes_params.TuiOpenThemesParams),
+ ),
+ cast_to=TuiOpenThemesResponse,
+ )
+
+ def show_toast(
+ self,
+ *,
+ message: str,
+ variant: Literal["info", "success", "warning", "error"],
+ directory: str | Omit = omit,
+ title: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiShowToastResponse:
+ """
+ Show a toast notification in the TUI
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/tui/show-toast",
+ body=maybe_transform(
+ {
+ "message": message,
+ "variant": variant,
+ "title": title,
+ },
+ tui_show_toast_params.TuiShowToastParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_show_toast_params.TuiShowToastParams),
+ ),
+ cast_to=TuiShowToastResponse,
+ )
+
+ def submit_prompt(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiSubmitPromptResponse:
+ """
+ Submit the prompt
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/tui/submit-prompt",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"directory": directory}, tui_submit_prompt_params.TuiSubmitPromptParams),
+ ),
+ cast_to=TuiSubmitPromptResponse,
+ )
+
class AsyncTuiResource(AsyncAPIResource):
@cached_property
@@ -118,12 +417,13 @@ async def append_prompt(
self,
*,
text: str,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TuiAppendPromptResponse:
"""
Append prompt to the TUI
@@ -141,30 +441,319 @@ async def append_prompt(
"/tui/append-prompt",
body=await async_maybe_transform({"text": text}, tui_append_prompt_params.TuiAppendPromptParams),
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, tui_append_prompt_params.TuiAppendPromptParams
+ ),
),
cast_to=TuiAppendPromptResponse,
)
+ async def clear_prompt(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiClearPromptResponse:
+ """
+ Clear the prompt
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/tui/clear-prompt",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, tui_clear_prompt_params.TuiClearPromptParams
+ ),
+ ),
+ cast_to=TuiClearPromptResponse,
+ )
+
+ async def execute_command(
+ self,
+ *,
+ command: str,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiExecuteCommandResponse:
+ """Execute a TUI command (e.g.
+
+ agent_cycle)
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/tui/execute-command",
+ body=await async_maybe_transform({"command": command}, tui_execute_command_params.TuiExecuteCommandParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, tui_execute_command_params.TuiExecuteCommandParams
+ ),
+ ),
+ cast_to=TuiExecuteCommandResponse,
+ )
+
async def open_help(
self,
*,
+ directory: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TuiOpenHelpResponse:
- """Open the help dialog"""
+ """
+ Open the help dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
return await self._post(
"/tui/open-help",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, tui_open_help_params.TuiOpenHelpParams),
),
cast_to=TuiOpenHelpResponse,
)
+ async def open_models(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiOpenModelsResponse:
+ """
+ Open the model dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/tui/open-models",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, tui_open_models_params.TuiOpenModelsParams),
+ ),
+ cast_to=TuiOpenModelsResponse,
+ )
+
+ async def open_sessions(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiOpenSessionsResponse:
+ """
+ Open the session dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/tui/open-sessions",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, tui_open_sessions_params.TuiOpenSessionsParams
+ ),
+ ),
+ cast_to=TuiOpenSessionsResponse,
+ )
+
+ async def open_themes(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiOpenThemesResponse:
+ """
+ Open the theme dialog
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/tui/open-themes",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, tui_open_themes_params.TuiOpenThemesParams),
+ ),
+ cast_to=TuiOpenThemesResponse,
+ )
+
+ async def show_toast(
+ self,
+ *,
+ message: str,
+ variant: Literal["info", "success", "warning", "error"],
+ directory: str | Omit = omit,
+ title: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiShowToastResponse:
+ """
+ Show a toast notification in the TUI
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/tui/show-toast",
+ body=await async_maybe_transform(
+ {
+ "message": message,
+ "variant": variant,
+ "title": title,
+ },
+ tui_show_toast_params.TuiShowToastParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"directory": directory}, tui_show_toast_params.TuiShowToastParams),
+ ),
+ cast_to=TuiShowToastResponse,
+ )
+
+ async def submit_prompt(
+ self,
+ *,
+ directory: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> TuiSubmitPromptResponse:
+ """
+ Submit the prompt
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/tui/submit-prompt",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"directory": directory}, tui_submit_prompt_params.TuiSubmitPromptParams
+ ),
+ ),
+ cast_to=TuiSubmitPromptResponse,
+ )
+
class TuiResourceWithRawResponse:
def __init__(self, tui: TuiResource) -> None:
@@ -173,9 +762,30 @@ def __init__(self, tui: TuiResource) -> None:
self.append_prompt = to_raw_response_wrapper(
tui.append_prompt,
)
+ self.clear_prompt = to_raw_response_wrapper(
+ tui.clear_prompt,
+ )
+ self.execute_command = to_raw_response_wrapper(
+ tui.execute_command,
+ )
self.open_help = to_raw_response_wrapper(
tui.open_help,
)
+ self.open_models = to_raw_response_wrapper(
+ tui.open_models,
+ )
+ self.open_sessions = to_raw_response_wrapper(
+ tui.open_sessions,
+ )
+ self.open_themes = to_raw_response_wrapper(
+ tui.open_themes,
+ )
+ self.show_toast = to_raw_response_wrapper(
+ tui.show_toast,
+ )
+ self.submit_prompt = to_raw_response_wrapper(
+ tui.submit_prompt,
+ )
class AsyncTuiResourceWithRawResponse:
@@ -185,9 +795,30 @@ def __init__(self, tui: AsyncTuiResource) -> None:
self.append_prompt = async_to_raw_response_wrapper(
tui.append_prompt,
)
+ self.clear_prompt = async_to_raw_response_wrapper(
+ tui.clear_prompt,
+ )
+ self.execute_command = async_to_raw_response_wrapper(
+ tui.execute_command,
+ )
self.open_help = async_to_raw_response_wrapper(
tui.open_help,
)
+ self.open_models = async_to_raw_response_wrapper(
+ tui.open_models,
+ )
+ self.open_sessions = async_to_raw_response_wrapper(
+ tui.open_sessions,
+ )
+ self.open_themes = async_to_raw_response_wrapper(
+ tui.open_themes,
+ )
+ self.show_toast = async_to_raw_response_wrapper(
+ tui.show_toast,
+ )
+ self.submit_prompt = async_to_raw_response_wrapper(
+ tui.submit_prompt,
+ )
class TuiResourceWithStreamingResponse:
@@ -197,9 +828,30 @@ def __init__(self, tui: TuiResource) -> None:
self.append_prompt = to_streamed_response_wrapper(
tui.append_prompt,
)
+ self.clear_prompt = to_streamed_response_wrapper(
+ tui.clear_prompt,
+ )
+ self.execute_command = to_streamed_response_wrapper(
+ tui.execute_command,
+ )
self.open_help = to_streamed_response_wrapper(
tui.open_help,
)
+ self.open_models = to_streamed_response_wrapper(
+ tui.open_models,
+ )
+ self.open_sessions = to_streamed_response_wrapper(
+ tui.open_sessions,
+ )
+ self.open_themes = to_streamed_response_wrapper(
+ tui.open_themes,
+ )
+ self.show_toast = to_streamed_response_wrapper(
+ tui.show_toast,
+ )
+ self.submit_prompt = to_streamed_response_wrapper(
+ tui.submit_prompt,
+ )
class AsyncTuiResourceWithStreamingResponse:
@@ -209,6 +861,27 @@ def __init__(self, tui: AsyncTuiResource) -> None:
self.append_prompt = async_to_streamed_response_wrapper(
tui.append_prompt,
)
+ self.clear_prompt = async_to_streamed_response_wrapper(
+ tui.clear_prompt,
+ )
+ self.execute_command = async_to_streamed_response_wrapper(
+ tui.execute_command,
+ )
self.open_help = async_to_streamed_response_wrapper(
tui.open_help,
)
+ self.open_models = async_to_streamed_response_wrapper(
+ tui.open_models,
+ )
+ self.open_sessions = async_to_streamed_response_wrapper(
+ tui.open_sessions,
+ )
+ self.open_themes = async_to_streamed_response_wrapper(
+ tui.open_themes,
+ )
+ self.show_toast = async_to_streamed_response_wrapper(
+ tui.show_toast,
+ )
+ self.submit_prompt = async_to_streamed_response_wrapper(
+ tui.submit_prompt,
+ )
diff --git a/src/opencode_ai/types/__init__.py b/src/opencode_ai/types/__init__.py
index 1d0663b..83a7701 100644
--- a/src/opencode_ai/types/__init__.py
+++ b/src/opencode_ai/types/__init__.py
@@ -2,10 +2,10 @@
from __future__ import annotations
-from .app import App as App
from .file import File as File
-from .mode import Mode as Mode
from .part import Part as Part
+from .path import Path as Path
+from .agent import Agent as Agent
from .model import Model as Model
from .config import Config as Config
from .shared import (
@@ -14,60 +14,110 @@
MessageAbortedError as MessageAbortedError,
)
from .symbol import Symbol as Symbol
+from .command import Command as Command
from .message import Message as Message
+from .project import Project as Project
from .session import Session as Session
from .provider import Provider as Provider
+from .file_node import FileNode as FileNode
from .file_part import FilePart as FilePart
from .text_part import TextPart as TextPart
from .tool_part import ToolPart as ToolPart
+from .agent_part import AgentPart as AgentPart
from .file_source import FileSource as FileSource
-from .mode_config import ModeConfig as ModeConfig
from .user_message import UserMessage as UserMessage
from .snapshot_part import SnapshotPart as SnapshotPart
from .symbol_source import SymbolSource as SymbolSource
from .app_log_params import AppLogParams as AppLogParams
+from .reasoning_part import ReasoningPart as ReasoningPart
from .keybinds_config import KeybindsConfig as KeybindsConfig
+from .path_get_params import PathGetParams as PathGetParams
from .step_start_part import StepStartPart as StepStartPart
from .app_log_response import AppLogResponse as AppLogResponse
+from .file_list_params import FileListParams as FileListParams
from .file_part_source import FilePartSource as FilePartSource
from .file_read_params import FileReadParams as FileReadParams
from .find_text_params import FindTextParams as FindTextParams
from .mcp_local_config import McpLocalConfig as McpLocalConfig
from .step_finish_part import StepFinishPart as StepFinishPart
from .tool_state_error import ToolStateError as ToolStateError
-from .app_init_response import AppInitResponse as AppInitResponse
+from .agent_list_params import AgentListParams as AgentListParams
from .assistant_message import AssistantMessage as AssistantMessage
+from .config_get_params import ConfigGetParams as ConfigGetParams
+from .event_list_params import EventListParams as EventListParams
from .file_source_param import FileSourceParam as FileSourceParam
from .find_files_params import FindFilesParams as FindFilesParams
from .mcp_remote_config import McpRemoteConfig as McpRemoteConfig
-from .app_modes_response import AppModesResponse as AppModesResponse
+from .file_list_response import FileListResponse as FileListResponse
from .file_read_response import FileReadResponse as FileReadResponse
+from .file_status_params import FileStatusParams as FileStatusParams
from .find_text_response import FindTextResponse as FindTextResponse
+from .session_get_params import SessionGetParams as SessionGetParams
from .tool_state_pending import ToolStatePending as ToolStatePending
from .tool_state_running import ToolStateRunning as ToolStateRunning
+from .agent_list_response import AgentListResponse as AgentListResponse
+from .command_list_params import CommandListParams as CommandListParams
from .event_list_response import EventListResponse as EventListResponse
from .find_files_response import FindFilesResponse as FindFilesResponse
from .find_symbols_params import FindSymbolsParams as FindSymbolsParams
-from .session_chat_params import SessionChatParams as SessionChatParams
+from .project_list_params import ProjectListParams as ProjectListParams
from .session_init_params import SessionInitParams as SessionInitParams
+from .session_list_params import SessionListParams as SessionListParams
from .symbol_source_param import SymbolSourceParam as SymbolSourceParam
+from .app_providers_params import AppProvidersParams as AppProvidersParams
from .file_status_response import FileStatusResponse as FileStatusResponse
+from .session_abort_params import SessionAbortParams as SessionAbortParams
+from .session_share_params import SessionShareParams as SessionShareParams
+from .session_shell_params import SessionShellParams as SessionShellParams
from .tool_state_completed import ToolStateCompleted as ToolStateCompleted
+from .tui_open_help_params import TuiOpenHelpParams as TuiOpenHelpParams
+from .command_list_response import CommandListResponse as CommandListResponse
from .file_part_input_param import FilePartInputParam as FilePartInputParam
from .file_part_source_text import FilePartSourceText as FilePartSourceText
from .find_symbols_response import FindSymbolsResponse as FindSymbolsResponse
+from .project_list_response import ProjectListResponse as ProjectListResponse
+from .session_create_params import SessionCreateParams as SessionCreateParams
+from .session_delete_params import SessionDeleteParams as SessionDeleteParams
from .session_init_response import SessionInitResponse as SessionInitResponse
from .session_list_response import SessionListResponse as SessionListResponse
+from .session_prompt_params import SessionPromptParams as SessionPromptParams
from .session_revert_params import SessionRevertParams as SessionRevertParams
+from .session_update_params import SessionUpdateParams as SessionUpdateParams
from .text_part_input_param import TextPartInputParam as TextPartInputParam
+from .tui_show_toast_params import TuiShowToastParams as TuiShowToastParams
+from .agent_part_input_param import AgentPartInputParam as AgentPartInputParam
from .app_providers_response import AppProvidersResponse as AppProvidersResponse
from .file_part_source_param import FilePartSourceParam as FilePartSourceParam
+from .project_current_params import ProjectCurrentParams as ProjectCurrentParams
from .session_abort_response import SessionAbortResponse as SessionAbortResponse
+from .session_command_params import SessionCommandParams as SessionCommandParams
+from .session_message_params import SessionMessageParams as SessionMessageParams
+from .session_unshare_params import SessionUnshareParams as SessionUnshareParams
from .tui_open_help_response import TuiOpenHelpResponse as TuiOpenHelpResponse
+from .tui_open_models_params import TuiOpenModelsParams as TuiOpenModelsParams
+from .tui_open_themes_params import TuiOpenThemesParams as TuiOpenThemesParams
+from .session_children_params import SessionChildrenParams as SessionChildrenParams
from .session_delete_response import SessionDeleteResponse as SessionDeleteResponse
+from .session_messages_params import SessionMessagesParams as SessionMessagesParams
+from .session_prompt_response import SessionPromptResponse as SessionPromptResponse
+from .session_unrevert_params import SessionUnrevertParams as SessionUnrevertParams
+from .tui_clear_prompt_params import TuiClearPromptParams as TuiClearPromptParams
+from .tui_show_toast_response import TuiShowToastResponse as TuiShowToastResponse
+from .session_command_response import SessionCommandResponse as SessionCommandResponse
+from .session_message_response import SessionMessageResponse as SessionMessageResponse
from .session_summarize_params import SessionSummarizeParams as SessionSummarizeParams
from .tui_append_prompt_params import TuiAppendPromptParams as TuiAppendPromptParams
+from .tui_open_models_response import TuiOpenModelsResponse as TuiOpenModelsResponse
+from .tui_open_sessions_params import TuiOpenSessionsParams as TuiOpenSessionsParams
+from .tui_open_themes_response import TuiOpenThemesResponse as TuiOpenThemesResponse
+from .tui_submit_prompt_params import TuiSubmitPromptParams as TuiSubmitPromptParams
+from .session_children_response import SessionChildrenResponse as SessionChildrenResponse
from .session_messages_response import SessionMessagesResponse as SessionMessagesResponse
+from .tui_clear_prompt_response import TuiClearPromptResponse as TuiClearPromptResponse
from .session_summarize_response import SessionSummarizeResponse as SessionSummarizeResponse
from .tui_append_prompt_response import TuiAppendPromptResponse as TuiAppendPromptResponse
+from .tui_execute_command_params import TuiExecuteCommandParams as TuiExecuteCommandParams
+from .tui_open_sessions_response import TuiOpenSessionsResponse as TuiOpenSessionsResponse
+from .tui_submit_prompt_response import TuiSubmitPromptResponse as TuiSubmitPromptResponse
from .file_part_source_text_param import FilePartSourceTextParam as FilePartSourceTextParam
+from .tui_execute_command_response import TuiExecuteCommandResponse as TuiExecuteCommandResponse
diff --git a/src/opencode_ai/types/agent.py b/src/opencode_ai/types/agent.py
new file mode 100644
index 0000000..34f5c9f
--- /dev/null
+++ b/src/opencode_ai/types/agent.py
@@ -0,0 +1,48 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["Agent", "Permission", "Model"]
+
+
+class Permission(BaseModel):
+ bash: Dict[str, Literal["ask", "allow", "deny"]]
+
+ edit: Literal["ask", "allow", "deny"]
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class Model(BaseModel):
+ api_model_id: str = FieldInfo(alias="modelID")
+
+ provider_id: str = FieldInfo(alias="providerID")
+
+
+class Agent(BaseModel):
+ built_in: bool = FieldInfo(alias="builtIn")
+
+ mode: Literal["subagent", "primary", "all"]
+
+ name: str
+
+ options: Dict[str, object]
+
+ permission: Permission
+
+ tools: Dict[str, bool]
+
+ description: Optional[str] = None
+
+ model: Optional[Model] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ top_p: Optional[float] = FieldInfo(alias="topP", default=None)
diff --git a/src/opencode_ai/types/agent_list_params.py b/src/opencode_ai/types/agent_list_params.py
new file mode 100644
index 0000000..a5ee594
--- /dev/null
+++ b/src/opencode_ai/types/agent_list_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["AgentListParams"]
+
+
+class AgentListParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/app_modes_response.py b/src/opencode_ai/types/agent_list_response.py
similarity index 60%
rename from src/opencode_ai/types/app_modes_response.py
rename to src/opencode_ai/types/agent_list_response.py
index 8d76f89..f33d201 100644
--- a/src/opencode_ai/types/app_modes_response.py
+++ b/src/opencode_ai/types/agent_list_response.py
@@ -3,8 +3,8 @@
from typing import List
from typing_extensions import TypeAlias
-from .mode import Mode
+from .agent import Agent
-__all__ = ["AppModesResponse"]
+__all__ = ["AgentListResponse"]
-AppModesResponse: TypeAlias = List[Mode]
+AgentListResponse: TypeAlias = List[Agent]
diff --git a/src/opencode_ai/types/agent_part.py b/src/opencode_ai/types/agent_part.py
new file mode 100644
index 0000000..3b0fd98
--- /dev/null
+++ b/src/opencode_ai/types/agent_part.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["AgentPart", "Source"]
+
+
+class Source(BaseModel):
+ end: int
+
+ start: int
+
+ value: str
+
+
+class AgentPart(BaseModel):
+ id: str
+
+ message_id: str = FieldInfo(alias="messageID")
+
+ name: str
+
+ session_id: str = FieldInfo(alias="sessionID")
+
+ type: Literal["agent"]
+
+ source: Optional[Source] = None
diff --git a/src/opencode_ai/types/agent_part_input_param.py b/src/opencode_ai/types/agent_part_input_param.py
new file mode 100644
index 0000000..a500028
--- /dev/null
+++ b/src/opencode_ai/types/agent_part_input_param.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["AgentPartInputParam", "Source"]
+
+
+class Source(TypedDict, total=False):
+ end: Required[int]
+
+ start: Required[int]
+
+ value: Required[str]
+
+
+class AgentPartInputParam(TypedDict, total=False):
+ name: Required[str]
+
+ type: Required[Literal["agent"]]
+
+ id: str
+
+ source: Source
diff --git a/src/opencode_ai/types/app_log_params.py b/src/opencode_ai/types/app_log_params.py
index 8b24c11..cb5a8ee 100644
--- a/src/opencode_ai/types/app_log_params.py
+++ b/src/opencode_ai/types/app_log_params.py
@@ -18,5 +18,7 @@ class AppLogParams(TypedDict, total=False):
service: Required[str]
"""Service name for the log entry"""
+ directory: str
+
extra: Dict[str, object]
"""Additional metadata for the log entry"""
diff --git a/src/opencode_ai/types/app_providers_params.py b/src/opencode_ai/types/app_providers_params.py
new file mode 100644
index 0000000..75e4783
--- /dev/null
+++ b/src/opencode_ai/types/app_providers_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["AppProvidersParams"]
+
+
+class AppProvidersParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/command.py b/src/opencode_ai/types/command.py
new file mode 100644
index 0000000..116ab0b
--- /dev/null
+++ b/src/opencode_ai/types/command.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+
+__all__ = ["Command"]
+
+
+class Command(BaseModel):
+ name: str
+
+ template: str
+
+ agent: Optional[str] = None
+
+ description: Optional[str] = None
+
+ model: Optional[str] = None
diff --git a/src/opencode_ai/types/command_list_params.py b/src/opencode_ai/types/command_list_params.py
new file mode 100644
index 0000000..96da71f
--- /dev/null
+++ b/src/opencode_ai/types/command_list_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["CommandListParams"]
+
+
+class CommandListParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/command_list_response.py b/src/opencode_ai/types/command_list_response.py
new file mode 100644
index 0000000..2b89a1e
--- /dev/null
+++ b/src/opencode_ai/types/command_list_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from .command import Command
+
+__all__ = ["CommandListResponse"]
+
+CommandListResponse: TypeAlias = List[Command]
diff --git a/src/opencode_ai/types/config.py b/src/opencode_ai/types/config.py
index 76f9348..c9bcec8 100644
--- a/src/opencode_ai/types/config.py
+++ b/src/opencode_ai/types/config.py
@@ -7,7 +7,6 @@
from .._utils import PropertyInfo
from .._models import BaseModel
-from .mode_config import ModeConfig
from .keybinds_config import KeybindsConfig
from .mcp_local_config import McpLocalConfig
from .mcp_remote_config import McpRemoteConfig
@@ -15,39 +14,235 @@
__all__ = [
"Config",
"Agent",
+ "AgentBuild",
+ "AgentBuildPermission",
"AgentGeneral",
+ "AgentGeneralPermission",
+ "AgentPlan",
+ "AgentPlanPermission",
"AgentAgentItem",
+ "AgentAgentItemPermission",
+ "Command",
"Experimental",
"ExperimentalHook",
"ExperimentalHookFileEdited",
"ExperimentalHookSessionCompleted",
+ "Formatter",
+ "Lsp",
+ "LspDisabled",
+ "LspUnionMember1",
"Mcp",
"Mode",
+ "ModeBuild",
+ "ModeBuildPermission",
+ "ModePlan",
+ "ModePlanPermission",
+ "ModeModeItem",
+ "ModeModeItemPermission",
+ "Permission",
"Provider",
"ProviderModels",
"ProviderModelsCost",
"ProviderModelsLimit",
"ProviderOptions",
+ "Tui",
]
-class AgentGeneral(ModeConfig):
- description: str
+class AgentBuildPermission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
-class AgentAgentItem(ModeConfig):
- description: str
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class AgentBuild(BaseModel):
+ description: Optional[str] = None
+ """Description of when to use the agent"""
+
+ disable: Optional[bool] = None
+
+ mode: Optional[Literal["subagent", "primary", "all"]] = None
+
+ model: Optional[str] = None
+
+ permission: Optional[AgentBuildPermission] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ tools: Optional[Dict[str, bool]] = None
+
+ top_p: Optional[float] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class AgentGeneralPermission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class AgentGeneral(BaseModel):
+ description: Optional[str] = None
+ """Description of when to use the agent"""
+
+ disable: Optional[bool] = None
+
+ mode: Optional[Literal["subagent", "primary", "all"]] = None
+
+ model: Optional[str] = None
+
+ permission: Optional[AgentGeneralPermission] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ tools: Optional[Dict[str, bool]] = None
+
+ top_p: Optional[float] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class AgentPlanPermission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class AgentPlan(BaseModel):
+ description: Optional[str] = None
+ """Description of when to use the agent"""
+
+ disable: Optional[bool] = None
+
+ mode: Optional[Literal["subagent", "primary", "all"]] = None
+
+ model: Optional[str] = None
+
+ permission: Optional[AgentPlanPermission] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ tools: Optional[Dict[str, bool]] = None
+
+ top_p: Optional[float] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class AgentAgentItemPermission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class AgentAgentItem(BaseModel):
+ description: Optional[str] = None
+ """Description of when to use the agent"""
+
+ disable: Optional[bool] = None
+
+ mode: Optional[Literal["subagent", "primary", "all"]] = None
+
+ model: Optional[str] = None
+
+ permission: Optional[AgentAgentItemPermission] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ tools: Optional[Dict[str, bool]] = None
+
+ top_p: Optional[float] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
class Agent(BaseModel):
+ """Agent configuration, see https://opencode.ai/docs/agent"""
+
+ build: Optional[AgentBuild] = None
+
general: Optional[AgentGeneral] = None
- __pydantic_extra__: Dict[str, AgentAgentItem] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+ plan: Optional[AgentPlan] = None
+
if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, AgentAgentItem] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
# Stub to indicate that arbitrary properties are accepted.
# To access properties that are not valid identifiers you can use `getattr`, e.g.
# `getattr(obj, '$type')`
def __getattr__(self, attr: str) -> AgentAgentItem: ...
+ else:
+ __pydantic_extra__: Dict[str, AgentAgentItem]
+
+
+class Command(BaseModel):
+ template: str
+
+ agent: Optional[str] = None
+
+ description: Optional[str] = None
+
+ model: Optional[str] = None
class ExperimentalHookFileEdited(BaseModel):
@@ -72,20 +267,186 @@ class Experimental(BaseModel):
hook: Optional[ExperimentalHook] = None
+class Formatter(BaseModel):
+ command: Optional[List[str]] = None
+
+ disabled: Optional[bool] = None
+
+ environment: Optional[Dict[str, str]] = None
+
+ extensions: Optional[List[str]] = None
+
+
+class LspDisabled(BaseModel):
+ disabled: Literal[True]
+
+
+class LspUnionMember1(BaseModel):
+ command: List[str]
+
+ disabled: Optional[bool] = None
+
+ env: Optional[Dict[str, str]] = None
+
+ extensions: Optional[List[str]] = None
+
+ initialization: Optional[Dict[str, object]] = None
+
+
+Lsp: TypeAlias = Union[LspDisabled, LspUnionMember1]
+
Mcp: TypeAlias = Annotated[Union[McpLocalConfig, McpRemoteConfig], PropertyInfo(discriminator="type")]
+class ModeBuildPermission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class ModeBuild(BaseModel):
+ description: Optional[str] = None
+ """Description of when to use the agent"""
+
+ disable: Optional[bool] = None
+
+ mode: Optional[Literal["subagent", "primary", "all"]] = None
+
+ model: Optional[str] = None
+
+ permission: Optional[ModeBuildPermission] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ tools: Optional[Dict[str, bool]] = None
+
+ top_p: Optional[float] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ModePlanPermission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class ModePlan(BaseModel):
+ description: Optional[str] = None
+ """Description of when to use the agent"""
+
+ disable: Optional[bool] = None
+
+ mode: Optional[Literal["subagent", "primary", "all"]] = None
+
+ model: Optional[str] = None
+
+ permission: Optional[ModePlanPermission] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ tools: Optional[Dict[str, bool]] = None
+
+ top_p: Optional[float] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ModeModeItemPermission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
+
+
+class ModeModeItem(BaseModel):
+ description: Optional[str] = None
+ """Description of when to use the agent"""
+
+ disable: Optional[bool] = None
+
+ mode: Optional[Literal["subagent", "primary", "all"]] = None
+
+ model: Optional[str] = None
+
+ permission: Optional[ModeModeItemPermission] = None
+
+ prompt: Optional[str] = None
+
+ temperature: Optional[float] = None
+
+ tools: Optional[Dict[str, bool]] = None
+
+ top_p: Optional[float] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
class Mode(BaseModel):
- build: Optional[ModeConfig] = None
+ """@deprecated Use `agent` field instead."""
+
+ build: Optional[ModeBuild] = None
- plan: Optional[ModeConfig] = None
+ plan: Optional[ModePlan] = None
- __pydantic_extra__: Dict[str, ModeConfig] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, ModeModeItem] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
# Stub to indicate that arbitrary properties are accepted.
# To access properties that are not valid identifiers you can use `getattr`, e.g.
# `getattr(obj, '$type')`
- def __getattr__(self, attr: str) -> ModeConfig: ...
+ def __getattr__(self, attr: str) -> ModeModeItem: ...
+ else:
+ __pydantic_extra__: Dict[str, ModeModeItem]
+
+
+class Permission(BaseModel):
+ bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None
+
+ edit: Optional[Literal["ask", "allow", "deny"]] = None
+
+ webfetch: Optional[Literal["ask", "allow", "deny"]] = None
class ProviderModelsCost(BaseModel):
@@ -131,23 +492,34 @@ class ProviderOptions(BaseModel):
base_url: Optional[str] = FieldInfo(alias="baseURL", default=None)
- __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+ timeout: Union[int, bool, None] = None
+ """Timeout in milliseconds for requests to this provider.
+
+ Default is 300000 (5 minutes). Set to false to disable timeout.
+ """
+
if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
# Stub to indicate that arbitrary properties are accepted.
# To access properties that are not valid identifiers you can use `getattr`, e.g.
# `getattr(obj, '$type')`
def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
class Provider(BaseModel):
- models: Dict[str, ProviderModels]
-
id: Optional[str] = None
api: Optional[str] = None
env: Optional[List[str]] = None
+ models: Optional[Dict[str, ProviderModels]] = None
+
name: Optional[str] = None
npm: Optional[str] = None
@@ -155,12 +527,19 @@ class Provider(BaseModel):
options: Optional[ProviderOptions] = None
+class Tui(BaseModel):
+ """TUI specific settings"""
+
+ scroll_speed: float
+ """TUI scroll speed"""
+
+
class Config(BaseModel):
schema_: Optional[str] = FieldInfo(alias="$schema", default=None)
"""JSON schema reference for configuration validation"""
agent: Optional[Agent] = None
- """Modes configuration, see https://opencode.ai/docs/modes"""
+ """Agent configuration, see https://opencode.ai/docs/agent"""
autoshare: Optional[bool] = None
"""@deprecated Use 'share' field instead.
@@ -171,11 +550,16 @@ class Config(BaseModel):
autoupdate: Optional[bool] = None
"""Automatically update to the latest version"""
+ command: Optional[Dict[str, Command]] = None
+ """Command configuration, see https://opencode.ai/docs/commands"""
+
disabled_providers: Optional[List[str]] = None
"""Disable providers that are loaded automatically"""
experimental: Optional[Experimental] = None
+ formatter: Optional[Dict[str, Formatter]] = None
+
instructions: Optional[List[str]] = None
"""Additional instruction files or patterns to include"""
@@ -185,15 +569,21 @@ class Config(BaseModel):
layout: Optional[Literal["auto", "stretch"]] = None
"""@deprecated Always uses stretch layout."""
+ lsp: Optional[Dict[str, Lsp]] = None
+
mcp: Optional[Dict[str, Mcp]] = None
"""MCP (Model Context Protocol) server configurations"""
mode: Optional[Mode] = None
- """Modes configuration, see https://opencode.ai/docs/modes"""
+ """@deprecated Use `agent` field instead."""
model: Optional[str] = None
"""Model to use in the format of provider/model, eg anthropic/claude-2"""
+ permission: Optional[Permission] = None
+
+ plugin: Optional[List[str]] = None
+
provider: Optional[Dict[str, Provider]] = None
"""Custom provider configurations and model overrides"""
@@ -205,12 +595,19 @@ class Config(BaseModel):
small_model: Optional[str] = None
"""
- Small model to use for tasks like summarization and title generation in the
- format of provider/model
+ Small model to use for tasks like title generation in the format of
+ provider/model
"""
+ snapshot: Optional[bool] = None
+
theme: Optional[str] = None
"""Theme name to use for the interface"""
+ tools: Optional[Dict[str, bool]] = None
+
+ tui: Optional[Tui] = None
+ """TUI specific settings"""
+
username: Optional[str] = None
"""Custom username to display in conversations instead of system username"""
diff --git a/src/opencode_ai/types/config_get_params.py b/src/opencode_ai/types/config_get_params.py
new file mode 100644
index 0000000..338d65c
--- /dev/null
+++ b/src/opencode_ai/types/config_get_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["ConfigGetParams"]
+
+
+class ConfigGetParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/event_list_params.py b/src/opencode_ai/types/event_list_params.py
new file mode 100644
index 0000000..988815e
--- /dev/null
+++ b/src/opencode_ai/types/event_list_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["EventListParams"]
+
+
+class EventListParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/event_list_response.py b/src/opencode_ai/types/event_list_response.py
index fa52d22..2df9d35 100644
--- a/src/opencode_ai/types/event_list_response.py
+++ b/src/opencode_ai/types/event_list_response.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Dict, Union, Optional
+from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
from pydantic import Field as FieldInfo
@@ -8,8 +8,9 @@
from .part import Part
from .._utils import PropertyInfo
from .message import Message
-from .session import Session
from .._models import BaseModel
+from .session.session import Session
+from .session.permission import Permission
from .shared.unknown_error import UnknownError
from .shared.provider_auth_error import ProviderAuthError
from .shared.message_aborted_error import MessageAbortedError
@@ -28,11 +29,9 @@
"EventMessagePartUpdatedProperties",
"EventMessagePartRemoved",
"EventMessagePartRemovedProperties",
- "EventStorageWrite",
- "EventStorageWriteProperties",
"EventPermissionUpdated",
- "EventPermissionUpdatedProperties",
- "EventPermissionUpdatedPropertiesTime",
+ "EventPermissionReplied",
+ "EventPermissionRepliedProperties",
"EventFileEdited",
"EventFileEditedProperties",
"EventSessionUpdated",
@@ -46,10 +45,6 @@
"EventSessionErrorPropertiesError",
"EventSessionErrorPropertiesErrorMessageOutputLengthError",
"EventServerConnected",
- "EventFileWatcherUpdated",
- "EventFileWatcherUpdatedProperties",
- "EventIdeInstalled",
- "EventIdeInstalledProperties",
]
@@ -121,38 +116,24 @@ class EventMessagePartRemoved(BaseModel):
type: Literal["message.part.removed"]
-class EventStorageWriteProperties(BaseModel):
- key: str
-
- content: Optional[object] = None
-
-
-class EventStorageWrite(BaseModel):
- properties: EventStorageWriteProperties
-
- type: Literal["storage.write"]
-
+class EventPermissionUpdated(BaseModel):
+ properties: Permission
-class EventPermissionUpdatedPropertiesTime(BaseModel):
- created: float
+ type: Literal["permission.updated"]
-class EventPermissionUpdatedProperties(BaseModel):
- id: str
+class EventPermissionRepliedProperties(BaseModel):
+ permission_id: str = FieldInfo(alias="permissionID")
- metadata: Dict[str, object]
+ response: str
session_id: str = FieldInfo(alias="sessionID")
- time: EventPermissionUpdatedPropertiesTime
-
- title: str
-
-class EventPermissionUpdated(BaseModel):
- properties: EventPermissionUpdatedProperties
+class EventPermissionReplied(BaseModel):
+ properties: EventPermissionRepliedProperties
- type: Literal["permission.updated"]
+ type: Literal["permission.replied"]
class EventFileEditedProperties(BaseModel):
@@ -227,28 +208,6 @@ class EventServerConnected(BaseModel):
type: Literal["server.connected"]
-class EventFileWatcherUpdatedProperties(BaseModel):
- event: Literal["rename", "change"]
-
- file: str
-
-
-class EventFileWatcherUpdated(BaseModel):
- properties: EventFileWatcherUpdatedProperties
-
- type: Literal["file.watcher.updated"]
-
-
-class EventIdeInstalledProperties(BaseModel):
- ide: str
-
-
-class EventIdeInstalled(BaseModel):
- properties: EventIdeInstalledProperties
-
- type: Literal["ide.installed"]
-
-
EventListResponse: TypeAlias = Annotated[
Union[
EventInstallationUpdated,
@@ -257,16 +216,14 @@ class EventIdeInstalled(BaseModel):
EventMessageRemoved,
EventMessagePartUpdated,
EventMessagePartRemoved,
- EventStorageWrite,
EventPermissionUpdated,
+ EventPermissionReplied,
EventFileEdited,
EventSessionUpdated,
EventSessionDeleted,
EventSessionIdle,
EventSessionError,
EventServerConnected,
- EventFileWatcherUpdated,
- EventIdeInstalled,
],
PropertyInfo(discriminator="type"),
]
diff --git a/src/opencode_ai/types/file_list_params.py b/src/opencode_ai/types/file_list_params.py
new file mode 100644
index 0000000..7fc1cbf
--- /dev/null
+++ b/src/opencode_ai/types/file_list_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FileListParams"]
+
+
+class FileListParams(TypedDict, total=False):
+ path: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/file_list_response.py b/src/opencode_ai/types/file_list_response.py
new file mode 100644
index 0000000..30e17d8
--- /dev/null
+++ b/src/opencode_ai/types/file_list_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from .file_node import FileNode
+
+__all__ = ["FileListResponse"]
+
+FileListResponse: TypeAlias = List[FileNode]
diff --git a/src/opencode_ai/types/file_node.py b/src/opencode_ai/types/file_node.py
new file mode 100644
index 0000000..56b8ded
--- /dev/null
+++ b/src/opencode_ai/types/file_node.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["FileNode"]
+
+
+class FileNode(BaseModel):
+ ignored: bool
+
+ name: str
+
+ path: str
+
+ type: Literal["file", "directory"]
diff --git a/src/opencode_ai/types/file_read_params.py b/src/opencode_ai/types/file_read_params.py
index b251a07..087534e 100644
--- a/src/opencode_ai/types/file_read_params.py
+++ b/src/opencode_ai/types/file_read_params.py
@@ -9,3 +9,5 @@
class FileReadParams(TypedDict, total=False):
path: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/file_status_params.py b/src/opencode_ai/types/file_status_params.py
new file mode 100644
index 0000000..d9d0922
--- /dev/null
+++ b/src/opencode_ai/types/file_status_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["FileStatusParams"]
+
+
+class FileStatusParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/find_files_params.py b/src/opencode_ai/types/find_files_params.py
index c2b3093..5b23a52 100644
--- a/src/opencode_ai/types/find_files_params.py
+++ b/src/opencode_ai/types/find_files_params.py
@@ -9,3 +9,5 @@
class FindFilesParams(TypedDict, total=False):
query: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/find_symbols_params.py b/src/opencode_ai/types/find_symbols_params.py
index 60f9e9e..4cd4e34 100644
--- a/src/opencode_ai/types/find_symbols_params.py
+++ b/src/opencode_ai/types/find_symbols_params.py
@@ -9,3 +9,5 @@
class FindSymbolsParams(TypedDict, total=False):
query: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/find_text_params.py b/src/opencode_ai/types/find_text_params.py
index f35ea4f..c1c31df 100644
--- a/src/opencode_ai/types/find_text_params.py
+++ b/src/opencode_ai/types/find_text_params.py
@@ -9,3 +9,5 @@
class FindTextParams(TypedDict, total=False):
pattern: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/keybinds_config.py b/src/opencode_ai/types/keybinds_config.py
index c7cb57c..452fa2b 100644
--- a/src/opencode_ai/types/keybinds_config.py
+++ b/src/opencode_ai/types/keybinds_config.py
@@ -8,6 +8,15 @@
class KeybindsConfig(BaseModel):
+ agent_cycle: str
+ """Next agent"""
+
+ agent_cycle_reverse: str
+ """Previous agent"""
+
+ agent_list: str
+ """List agents"""
+
app_exit: str
"""Exit the application"""
@@ -18,16 +27,16 @@ class KeybindsConfig(BaseModel):
"""Open external editor"""
file_close: str
- """Close file"""
+ """@deprecated Close file"""
file_diff_toggle: str
- """Split/unified diff"""
+ """@deprecated Split/unified diff"""
file_list: str
- """List files"""
+ """@deprecated Currently not available. List files"""
file_search: str
- """Search file"""
+ """@deprecated Search file"""
input_clear: str
"""Clear input field"""
@@ -60,10 +69,10 @@ class KeybindsConfig(BaseModel):
"""Navigate to last message"""
messages_layout_toggle: str
- """Toggle layout"""
+ """@deprecated Toggle layout"""
messages_next: str
- """Navigate to next message"""
+ """@deprecated Navigate to next message"""
messages_page_down: str
"""Scroll messages down by one page"""
@@ -72,7 +81,7 @@ class KeybindsConfig(BaseModel):
"""Scroll messages up by one page"""
messages_previous: str
- """Navigate to previous message"""
+ """@deprecated Navigate to previous message"""
messages_redo: str
"""Redo message"""
@@ -83,12 +92,24 @@ class KeybindsConfig(BaseModel):
messages_undo: str
"""Undo message"""
+ api_model_cycle_recent: str = FieldInfo(alias="model_cycle_recent")
+ """Next recent model"""
+
+ api_model_cycle_recent_reverse: str = FieldInfo(alias="model_cycle_recent_reverse")
+ """Previous recent model"""
+
api_model_list: str = FieldInfo(alias="model_list")
"""List available models"""
project_init: str
"""Create/update AGENTS.md"""
+ session_child_cycle: str
+ """Cycle to next child session"""
+
+ session_child_cycle_reverse: str
+ """Cycle to previous child session"""
+
session_compact: str
"""Compact the session"""
@@ -107,17 +128,29 @@ class KeybindsConfig(BaseModel):
session_share: str
"""Share current session"""
+ session_timeline: str
+ """Show session timeline"""
+
session_unshare: str
"""Unshare current session"""
+ switch_agent: str
+ """@deprecated use agent_cycle. Next agent"""
+
+ switch_agent_reverse: str
+ """@deprecated use agent_cycle_reverse. Previous agent"""
+
switch_mode: str
- """Next mode"""
+ """@deprecated use agent_cycle. Next mode"""
switch_mode_reverse: str
- """Previous Mode"""
+ """@deprecated use agent_cycle_reverse. Previous mode"""
theme_list: str
"""List available themes"""
+ thinking_blocks: str
+ """Toggle thinking blocks"""
+
tool_details: str
"""Toggle tool details"""
diff --git a/src/opencode_ai/types/mode.py b/src/opencode_ai/types/mode.py
deleted file mode 100644
index 041b7f3..0000000
--- a/src/opencode_ai/types/mode.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, Optional
-
-from pydantic import Field as FieldInfo
-
-from .._models import BaseModel
-
-__all__ = ["Mode", "Model"]
-
-
-class Model(BaseModel):
- api_model_id: str = FieldInfo(alias="modelID")
-
- provider_id: str = FieldInfo(alias="providerID")
-
-
-class Mode(BaseModel):
- name: str
-
- tools: Dict[str, bool]
-
- model: Optional[Model] = None
-
- prompt: Optional[str] = None
-
- temperature: Optional[float] = None
diff --git a/src/opencode_ai/types/mode_config.py b/src/opencode_ai/types/mode_config.py
deleted file mode 100644
index d7561c2..0000000
--- a/src/opencode_ai/types/mode_config.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, Optional
-
-from .._models import BaseModel
-
-__all__ = ["ModeConfig"]
-
-
-class ModeConfig(BaseModel):
- disable: Optional[bool] = None
-
- model: Optional[str] = None
-
- prompt: Optional[str] = None
-
- temperature: Optional[float] = None
-
- tools: Optional[Dict[str, bool]] = None
diff --git a/src/opencode_ai/types/part.py b/src/opencode_ai/types/part.py
index 5a74aa5..820c1e1 100644
--- a/src/opencode_ai/types/part.py
+++ b/src/opencode_ai/types/part.py
@@ -10,7 +10,9 @@
from .file_part import FilePart
from .text_part import TextPart
from .tool_part import ToolPart
+from .agent_part import AgentPart
from .snapshot_part import SnapshotPart
+from .reasoning_part import ReasoningPart
from .step_start_part import StepStartPart
from .step_finish_part import StepFinishPart
@@ -32,6 +34,8 @@ class PatchPart(BaseModel):
Part: TypeAlias = Annotated[
- Union[TextPart, FilePart, ToolPart, StepStartPart, StepFinishPart, SnapshotPart, PatchPart],
+ Union[
+ TextPart, ReasoningPart, FilePart, ToolPart, StepStartPart, StepFinishPart, SnapshotPart, PatchPart, AgentPart
+ ],
PropertyInfo(discriminator="type"),
]
diff --git a/src/opencode_ai/types/path.py b/src/opencode_ai/types/path.py
new file mode 100644
index 0000000..8885465
--- /dev/null
+++ b/src/opencode_ai/types/path.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+
+__all__ = ["Path"]
+
+
+class Path(BaseModel):
+ config: str
+
+ directory: str
+
+ state: str
+
+ worktree: str
diff --git a/src/opencode_ai/types/path_get_params.py b/src/opencode_ai/types/path_get_params.py
new file mode 100644
index 0000000..49bd72f
--- /dev/null
+++ b/src/opencode_ai/types/path_get_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["PathGetParams"]
+
+
+class PathGetParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/app.py b/src/opencode_ai/types/project.py
similarity index 53%
rename from src/opencode_ai/types/app.py
rename to src/opencode_ai/types/project.py
index d60c600..f19b103 100644
--- a/src/opencode_ai/types/app.py
+++ b/src/opencode_ai/types/project.py
@@ -1,33 +1,24 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from typing import Optional
+from typing_extensions import Literal
from .._models import BaseModel
-__all__ = ["App", "Path", "Time"]
-
-
-class Path(BaseModel):
- config: str
-
- cwd: str
-
- data: str
-
- root: str
-
- state: str
+__all__ = ["Project", "Time"]
class Time(BaseModel):
+ created: float
+
initialized: Optional[float] = None
-class App(BaseModel):
- git: bool
+class Project(BaseModel):
+ id: str
- hostname: str
+ time: Time
- path: Path
+ worktree: str
- time: Time
+ vcs: Optional[Literal["git"]] = None
diff --git a/src/opencode_ai/types/project_current_params.py b/src/opencode_ai/types/project_current_params.py
new file mode 100644
index 0000000..6b9a645
--- /dev/null
+++ b/src/opencode_ai/types/project_current_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["ProjectCurrentParams"]
+
+
+class ProjectCurrentParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/project_list_params.py b/src/opencode_ai/types/project_list_params.py
new file mode 100644
index 0000000..c60aaca
--- /dev/null
+++ b/src/opencode_ai/types/project_list_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["ProjectListParams"]
+
+
+class ProjectListParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/project_list_response.py b/src/opencode_ai/types/project_list_response.py
new file mode 100644
index 0000000..2d05a23
--- /dev/null
+++ b/src/opencode_ai/types/project_list_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from .project import Project
+
+__all__ = ["ProjectListResponse"]
+
+ProjectListResponse: TypeAlias = List[Project]
diff --git a/src/opencode_ai/types/reasoning_part.py b/src/opencode_ai/types/reasoning_part.py
new file mode 100644
index 0000000..71e0052
--- /dev/null
+++ b/src/opencode_ai/types/reasoning_part.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["ReasoningPart", "Time"]
+
+
+class Time(BaseModel):
+ start: float
+
+ end: Optional[float] = None
+
+
+class ReasoningPart(BaseModel):
+ id: str
+
+ message_id: str = FieldInfo(alias="messageID")
+
+ session_id: str = FieldInfo(alias="sessionID")
+
+ text: str
+
+ time: Time
+
+ type: Literal["reasoning"]
+
+ metadata: Optional[Dict[str, object]] = None
diff --git a/src/opencode_ai/types/session/__init__.py b/src/opencode_ai/types/session/__init__.py
new file mode 100644
index 0000000..b360854
--- /dev/null
+++ b/src/opencode_ai/types/session/__init__.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .session import Session as Session
+from .permission import Permission as Permission
+from .permission_respond_params import PermissionRespondParams as PermissionRespondParams
+from .permission_respond_response import PermissionRespondResponse as PermissionRespondResponse
diff --git a/src/opencode_ai/types/session/permission.py b/src/opencode_ai/types/session/permission.py
new file mode 100644
index 0000000..88fc6cb
--- /dev/null
+++ b/src/opencode_ai/types/session/permission.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["Permission", "Time"]
+
+
+class Time(BaseModel):
+ created: float
+
+
+class Permission(BaseModel):
+ id: str
+
+ message_id: str = FieldInfo(alias="messageID")
+
+ metadata: Dict[str, object]
+
+ session_id: str = FieldInfo(alias="sessionID")
+
+ time: Time
+
+ title: str
+
+ type: str
+
+ call_id: Optional[str] = FieldInfo(alias="callID", default=None)
+
+ pattern: Optional[str] = None
diff --git a/src/opencode_ai/types/session/permission_respond_params.py b/src/opencode_ai/types/session/permission_respond_params.py
new file mode 100644
index 0000000..8698254
--- /dev/null
+++ b/src/opencode_ai/types/session/permission_respond_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["PermissionRespondParams"]
+
+
+class PermissionRespondParams(TypedDict, total=False):
+ id: Required[str]
+
+ response: Required[Literal["once", "always", "reject"]]
+
+ directory: str
diff --git a/src/opencode_ai/types/session/permission_respond_response.py b/src/opencode_ai/types/session/permission_respond_response.py
new file mode 100644
index 0000000..0cc1480
--- /dev/null
+++ b/src/opencode_ai/types/session/permission_respond_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["PermissionRespondResponse"]
+
+PermissionRespondResponse: TypeAlias = bool
diff --git a/src/opencode_ai/types/session.py b/src/opencode_ai/types/session/session.py
similarity index 87%
rename from src/opencode_ai/types/session.py
rename to src/opencode_ai/types/session/session.py
index a0ed929..af9fcd3 100644
--- a/src/opencode_ai/types/session.py
+++ b/src/opencode_ai/types/session/session.py
@@ -4,7 +4,7 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
__all__ = ["Session", "Time", "Revert", "Share"]
@@ -32,6 +32,10 @@ class Share(BaseModel):
class Session(BaseModel):
id: str
+ directory: str
+
+ project_id: str = FieldInfo(alias="projectID")
+
time: Time
title: str
diff --git a/src/opencode_ai/types/session_abort_params.py b/src/opencode_ai/types/session_abort_params.py
new file mode 100644
index 0000000..15a923b
--- /dev/null
+++ b/src/opencode_ai/types/session_abort_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionAbortParams"]
+
+
+class SessionAbortParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_children_params.py b/src/opencode_ai/types/session_children_params.py
new file mode 100644
index 0000000..49004c4
--- /dev/null
+++ b/src/opencode_ai/types/session_children_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionChildrenParams"]
+
+
+class SessionChildrenParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_children_response.py b/src/opencode_ai/types/session_children_response.py
new file mode 100644
index 0000000..975bc30
--- /dev/null
+++ b/src/opencode_ai/types/session_children_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from .session.session import Session
+
+__all__ = ["SessionChildrenResponse"]
+
+SessionChildrenResponse: TypeAlias = List[Session]
diff --git a/src/opencode_ai/types/session_command_params.py b/src/opencode_ai/types/session_command_params.py
new file mode 100644
index 0000000..0ae5a37
--- /dev/null
+++ b/src/opencode_ai/types/session_command_params.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["SessionCommandParams"]
+
+
+class SessionCommandParams(TypedDict, total=False):
+ arguments: Required[str]
+
+ command: Required[str]
+
+ directory: str
+
+ agent: str
+
+ message_id: Annotated[str, PropertyInfo(alias="messageID")]
+
+ model: str
diff --git a/src/opencode_ai/types/session_command_response.py b/src/opencode_ai/types/session_command_response.py
new file mode 100644
index 0000000..c697bbd
--- /dev/null
+++ b/src/opencode_ai/types/session_command_response.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .part import Part
+from .._models import BaseModel
+from .assistant_message import AssistantMessage
+
+__all__ = ["SessionCommandResponse"]
+
+
+class SessionCommandResponse(BaseModel):
+ info: AssistantMessage
+
+ parts: List[Part]
diff --git a/src/opencode_ai/types/session_create_params.py b/src/opencode_ai/types/session_create_params.py
new file mode 100644
index 0000000..d41fa79
--- /dev/null
+++ b/src/opencode_ai/types/session_create_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["SessionCreateParams"]
+
+
+class SessionCreateParams(TypedDict, total=False):
+ directory: str
+
+ parent_id: Annotated[str, PropertyInfo(alias="parentID")]
+
+ title: str
diff --git a/src/opencode_ai/types/session_delete_params.py b/src/opencode_ai/types/session_delete_params.py
new file mode 100644
index 0000000..3549cea
--- /dev/null
+++ b/src/opencode_ai/types/session_delete_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionDeleteParams"]
+
+
+class SessionDeleteParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_get_params.py b/src/opencode_ai/types/session_get_params.py
new file mode 100644
index 0000000..f77e9b0
--- /dev/null
+++ b/src/opencode_ai/types/session_get_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionGetParams"]
+
+
+class SessionGetParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_init_params.py b/src/opencode_ai/types/session_init_params.py
index 8d6d0d8..cf370b8 100644
--- a/src/opencode_ai/types/session_init_params.py
+++ b/src/opencode_ai/types/session_init_params.py
@@ -15,3 +15,5 @@ class SessionInitParams(TypedDict, total=False):
model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]]
provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]]
+
+ directory: str
diff --git a/src/opencode_ai/types/session_list_params.py b/src/opencode_ai/types/session_list_params.py
new file mode 100644
index 0000000..21d63f1
--- /dev/null
+++ b/src/opencode_ai/types/session_list_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionListParams"]
+
+
+class SessionListParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_list_response.py b/src/opencode_ai/types/session_list_response.py
index ca162dd..5f221fa 100644
--- a/src/opencode_ai/types/session_list_response.py
+++ b/src/opencode_ai/types/session_list_response.py
@@ -3,7 +3,7 @@
from typing import List
from typing_extensions import TypeAlias
-from .session import Session
+from .session.session import Session
__all__ = ["SessionListResponse"]
diff --git a/src/opencode_ai/types/session_message_params.py b/src/opencode_ai/types/session_message_params.py
new file mode 100644
index 0000000..aa00f7c
--- /dev/null
+++ b/src/opencode_ai/types/session_message_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["SessionMessageParams"]
+
+
+class SessionMessageParams(TypedDict, total=False):
+ id: Required[str]
+ """Session ID"""
+
+ directory: str
diff --git a/src/opencode_ai/types/session_message_response.py b/src/opencode_ai/types/session_message_response.py
new file mode 100644
index 0000000..a64b088
--- /dev/null
+++ b/src/opencode_ai/types/session_message_response.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .part import Part
+from .message import Message
+from .._models import BaseModel
+
+__all__ = ["SessionMessageResponse"]
+
+
+class SessionMessageResponse(BaseModel):
+ info: Message
+
+ parts: List[Part]
diff --git a/src/opencode_ai/types/session_messages_params.py b/src/opencode_ai/types/session_messages_params.py
new file mode 100644
index 0000000..6831706
--- /dev/null
+++ b/src/opencode_ai/types/session_messages_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionMessagesParams"]
+
+
+class SessionMessagesParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_chat_params.py b/src/opencode_ai/types/session_prompt_params.py
similarity index 67%
rename from src/opencode_ai/types/session_chat_params.py
rename to src/opencode_ai/types/session_prompt_params.py
index e827282..78fad20 100644
--- a/src/opencode_ai/types/session_chat_params.py
+++ b/src/opencode_ai/types/session_prompt_params.py
@@ -8,24 +8,31 @@
from .._utils import PropertyInfo
from .file_part_input_param import FilePartInputParam
from .text_part_input_param import TextPartInputParam
+from .agent_part_input_param import AgentPartInputParam
-__all__ = ["SessionChatParams", "Part"]
+__all__ = ["SessionPromptParams", "Part", "Model"]
-class SessionChatParams(TypedDict, total=False):
- model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]]
-
+class SessionPromptParams(TypedDict, total=False):
parts: Required[Iterable[Part]]
- provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]]
+ directory: str
+
+ agent: str
message_id: Annotated[str, PropertyInfo(alias="messageID")]
- mode: str
+ model: Model
system: str
tools: Dict[str, bool]
-Part: TypeAlias = Union[TextPartInputParam, FilePartInputParam]
+Part: TypeAlias = Union[TextPartInputParam, FilePartInputParam, AgentPartInputParam]
+
+
+class Model(TypedDict, total=False):
+ model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]]
+
+ provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]]
diff --git a/src/opencode_ai/types/session_prompt_response.py b/src/opencode_ai/types/session_prompt_response.py
new file mode 100644
index 0000000..2502def
--- /dev/null
+++ b/src/opencode_ai/types/session_prompt_response.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .part import Part
+from .._models import BaseModel
+from .assistant_message import AssistantMessage
+
+__all__ = ["SessionPromptResponse"]
+
+
+class SessionPromptResponse(BaseModel):
+ info: AssistantMessage
+
+ parts: List[Part]
diff --git a/src/opencode_ai/types/session_revert_params.py b/src/opencode_ai/types/session_revert_params.py
index e5361ee..b56a5b4 100644
--- a/src/opencode_ai/types/session_revert_params.py
+++ b/src/opencode_ai/types/session_revert_params.py
@@ -12,4 +12,6 @@
class SessionRevertParams(TypedDict, total=False):
message_id: Required[Annotated[str, PropertyInfo(alias="messageID")]]
+ directory: str
+
part_id: Annotated[str, PropertyInfo(alias="partID")]
diff --git a/src/opencode_ai/types/session_share_params.py b/src/opencode_ai/types/session_share_params.py
new file mode 100644
index 0000000..9c1dbb6
--- /dev/null
+++ b/src/opencode_ai/types/session_share_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionShareParams"]
+
+
+class SessionShareParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_shell_params.py b/src/opencode_ai/types/session_shell_params.py
new file mode 100644
index 0000000..7e45f59
--- /dev/null
+++ b/src/opencode_ai/types/session_shell_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["SessionShellParams"]
+
+
+class SessionShellParams(TypedDict, total=False):
+ agent: Required[str]
+
+ command: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/session_summarize_params.py b/src/opencode_ai/types/session_summarize_params.py
index 46e3aa2..f976e9f 100644
--- a/src/opencode_ai/types/session_summarize_params.py
+++ b/src/opencode_ai/types/session_summarize_params.py
@@ -13,3 +13,5 @@ class SessionSummarizeParams(TypedDict, total=False):
model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]]
provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]]
+
+ directory: str
diff --git a/src/opencode_ai/types/session_unrevert_params.py b/src/opencode_ai/types/session_unrevert_params.py
new file mode 100644
index 0000000..24cbedd
--- /dev/null
+++ b/src/opencode_ai/types/session_unrevert_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionUnrevertParams"]
+
+
+class SessionUnrevertParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_unshare_params.py b/src/opencode_ai/types/session_unshare_params.py
new file mode 100644
index 0000000..bbbd1be
--- /dev/null
+++ b/src/opencode_ai/types/session_unshare_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionUnshareParams"]
+
+
+class SessionUnshareParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/session_update_params.py b/src/opencode_ai/types/session_update_params.py
new file mode 100644
index 0000000..c1169af
--- /dev/null
+++ b/src/opencode_ai/types/session_update_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SessionUpdateParams"]
+
+
+class SessionUpdateParams(TypedDict, total=False):
+ directory: str
+
+ title: str
diff --git a/src/opencode_ai/types/tool_state_error.py b/src/opencode_ai/types/tool_state_error.py
index 141a4cd..8f6bf5a 100644
--- a/src/opencode_ai/types/tool_state_error.py
+++ b/src/opencode_ai/types/tool_state_error.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Dict
+from typing import Dict, Optional
from typing_extensions import Literal
from .._models import BaseModel
@@ -22,3 +22,5 @@ class ToolStateError(BaseModel):
status: Literal["error"]
time: Time
+
+ metadata: Optional[Dict[str, object]] = None
diff --git a/src/opencode_ai/types/tui_append_prompt_params.py b/src/opencode_ai/types/tui_append_prompt_params.py
index 431f731..48a0e32 100644
--- a/src/opencode_ai/types/tui_append_prompt_params.py
+++ b/src/opencode_ai/types/tui_append_prompt_params.py
@@ -9,3 +9,5 @@
class TuiAppendPromptParams(TypedDict, total=False):
text: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/tui_clear_prompt_params.py b/src/opencode_ai/types/tui_clear_prompt_params.py
new file mode 100644
index 0000000..5f1a7f3
--- /dev/null
+++ b/src/opencode_ai/types/tui_clear_prompt_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["TuiClearPromptParams"]
+
+
+class TuiClearPromptParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/tui_clear_prompt_response.py b/src/opencode_ai/types/tui_clear_prompt_response.py
new file mode 100644
index 0000000..3b96c7a
--- /dev/null
+++ b/src/opencode_ai/types/tui_clear_prompt_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["TuiClearPromptResponse"]
+
+TuiClearPromptResponse: TypeAlias = bool
diff --git a/src/opencode_ai/types/tui_execute_command_params.py b/src/opencode_ai/types/tui_execute_command_params.py
new file mode 100644
index 0000000..a73953f
--- /dev/null
+++ b/src/opencode_ai/types/tui_execute_command_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["TuiExecuteCommandParams"]
+
+
+class TuiExecuteCommandParams(TypedDict, total=False):
+ command: Required[str]
+
+ directory: str
diff --git a/src/opencode_ai/types/tui_execute_command_response.py b/src/opencode_ai/types/tui_execute_command_response.py
new file mode 100644
index 0000000..5ceb1dd
--- /dev/null
+++ b/src/opencode_ai/types/tui_execute_command_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["TuiExecuteCommandResponse"]
+
+TuiExecuteCommandResponse: TypeAlias = bool
diff --git a/src/opencode_ai/types/tui_open_help_params.py b/src/opencode_ai/types/tui_open_help_params.py
new file mode 100644
index 0000000..7d613af
--- /dev/null
+++ b/src/opencode_ai/types/tui_open_help_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["TuiOpenHelpParams"]
+
+
+class TuiOpenHelpParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/tui_open_models_params.py b/src/opencode_ai/types/tui_open_models_params.py
new file mode 100644
index 0000000..f514e95
--- /dev/null
+++ b/src/opencode_ai/types/tui_open_models_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["TuiOpenModelsParams"]
+
+
+class TuiOpenModelsParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/tui_open_models_response.py b/src/opencode_ai/types/tui_open_models_response.py
new file mode 100644
index 0000000..073a388
--- /dev/null
+++ b/src/opencode_ai/types/tui_open_models_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["TuiOpenModelsResponse"]
+
+TuiOpenModelsResponse: TypeAlias = bool
diff --git a/src/opencode_ai/types/tui_open_sessions_params.py b/src/opencode_ai/types/tui_open_sessions_params.py
new file mode 100644
index 0000000..284cd70
--- /dev/null
+++ b/src/opencode_ai/types/tui_open_sessions_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["TuiOpenSessionsParams"]
+
+
+class TuiOpenSessionsParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/tui_open_sessions_response.py b/src/opencode_ai/types/tui_open_sessions_response.py
new file mode 100644
index 0000000..eab352a
--- /dev/null
+++ b/src/opencode_ai/types/tui_open_sessions_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["TuiOpenSessionsResponse"]
+
+TuiOpenSessionsResponse: TypeAlias = bool
diff --git a/src/opencode_ai/types/tui_open_themes_params.py b/src/opencode_ai/types/tui_open_themes_params.py
new file mode 100644
index 0000000..8adc30d
--- /dev/null
+++ b/src/opencode_ai/types/tui_open_themes_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["TuiOpenThemesParams"]
+
+
+class TuiOpenThemesParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/tui_open_themes_response.py b/src/opencode_ai/types/tui_open_themes_response.py
new file mode 100644
index 0000000..8623270
--- /dev/null
+++ b/src/opencode_ai/types/tui_open_themes_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["TuiOpenThemesResponse"]
+
+TuiOpenThemesResponse: TypeAlias = bool
diff --git a/src/opencode_ai/types/tui_show_toast_params.py b/src/opencode_ai/types/tui_show_toast_params.py
new file mode 100644
index 0000000..bbc7e15
--- /dev/null
+++ b/src/opencode_ai/types/tui_show_toast_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["TuiShowToastParams"]
+
+
+class TuiShowToastParams(TypedDict, total=False):
+ message: Required[str]
+
+ variant: Required[Literal["info", "success", "warning", "error"]]
+
+ directory: str
+
+ title: str
diff --git a/src/opencode_ai/types/app_init_response.py b/src/opencode_ai/types/tui_show_toast_response.py
similarity index 63%
rename from src/opencode_ai/types/app_init_response.py
rename to src/opencode_ai/types/tui_show_toast_response.py
index 08e3d67..e66d1de 100644
--- a/src/opencode_ai/types/app_init_response.py
+++ b/src/opencode_ai/types/tui_show_toast_response.py
@@ -2,6 +2,6 @@
from typing_extensions import TypeAlias
-__all__ = ["AppInitResponse"]
+__all__ = ["TuiShowToastResponse"]
-AppInitResponse: TypeAlias = bool
+TuiShowToastResponse: TypeAlias = bool
diff --git a/src/opencode_ai/types/tui_submit_prompt_params.py b/src/opencode_ai/types/tui_submit_prompt_params.py
new file mode 100644
index 0000000..4331fda
--- /dev/null
+++ b/src/opencode_ai/types/tui_submit_prompt_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["TuiSubmitPromptParams"]
+
+
+class TuiSubmitPromptParams(TypedDict, total=False):
+ directory: str
diff --git a/src/opencode_ai/types/tui_submit_prompt_response.py b/src/opencode_ai/types/tui_submit_prompt_response.py
new file mode 100644
index 0000000..55cf124
--- /dev/null
+++ b/src/opencode_ai/types/tui_submit_prompt_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["TuiSubmitPromptResponse"]
+
+TuiSubmitPromptResponse: TypeAlias = bool
diff --git a/tests/api_resources/session/__init__.py b/tests/api_resources/session/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/session/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/session/test_permissions.py b/tests/api_resources/session/test_permissions.py
new file mode 100644
index 0000000..97a1c50
--- /dev/null
+++ b/tests/api_resources/session/test_permissions.py
@@ -0,0 +1,160 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from opencode_ai import Opencode, AsyncOpencode
+from tests.utils import assert_matches_type
+from opencode_ai.types.session import PermissionRespondResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestPermissions:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_respond(self, client: Opencode) -> None:
+ permission = client.session.permissions.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ )
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_respond_with_all_params(self, client: Opencode) -> None:
+ permission = client.session.permissions.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ directory="directory",
+ )
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_respond(self, client: Opencode) -> None:
+ response = client.session.permissions.with_raw_response.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ permission = response.parse()
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_respond(self, client: Opencode) -> None:
+ with client.session.permissions.with_streaming_response.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ permission = response.parse()
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_respond(self, client: Opencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.session.permissions.with_raw_response.respond(
+ permission_id="permissionID",
+ id="",
+ response="once",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `permission_id` but received ''"):
+ client.session.permissions.with_raw_response.respond(
+ permission_id="",
+ id="id",
+ response="once",
+ )
+
+
+class TestAsyncPermissions:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_respond(self, async_client: AsyncOpencode) -> None:
+ permission = await async_client.session.permissions.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ )
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_respond_with_all_params(self, async_client: AsyncOpencode) -> None:
+ permission = await async_client.session.permissions.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ directory="directory",
+ )
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_respond(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.permissions.with_raw_response.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ permission = await response.parse()
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_respond(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.permissions.with_streaming_response.respond(
+ permission_id="permissionID",
+ id="id",
+ response="once",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ permission = await response.parse()
+ assert_matches_type(PermissionRespondResponse, permission, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_respond(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.permissions.with_raw_response.respond(
+ permission_id="permissionID",
+ id="",
+ response="once",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `permission_id` but received ''"):
+ await async_client.session.permissions.with_raw_response.respond(
+ permission_id="",
+ id="id",
+ response="once",
+ )
diff --git a/tests/api_resources/test_agent.py b/tests/api_resources/test_agent.py
new file mode 100644
index 0000000..0cafe73
--- /dev/null
+++ b/tests/api_resources/test_agent.py
@@ -0,0 +1,96 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from opencode_ai import Opencode, AsyncOpencode
+from tests.utils import assert_matches_type
+from opencode_ai.types import AgentListResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAgent:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Opencode) -> None:
+ agent = client.agent.list()
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Opencode) -> None:
+ agent = client.agent.list(
+ directory="directory",
+ )
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Opencode) -> None:
+ response = client.agent.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Opencode) -> None:
+ with client.agent.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncAgent:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpencode) -> None:
+ agent = await async_client.agent.list()
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None:
+ agent = await async_client.agent.list(
+ directory="directory",
+ )
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.agent.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None:
+ async with async_client.agent.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentListResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_app.py b/tests/api_resources/test_app.py
index e0f0670..8a9bf1e 100644
--- a/tests/api_resources/test_app.py
+++ b/tests/api_resources/test_app.py
@@ -9,13 +9,7 @@
from opencode_ai import Opencode, AsyncOpencode
from tests.utils import assert_matches_type
-from opencode_ai.types import (
- App,
- AppLogResponse,
- AppInitResponse,
- AppModesResponse,
- AppProvidersResponse,
-)
+from opencode_ai.types import AppLogResponse, AppProvidersResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -23,62 +17,6 @@
class TestApp:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_method_get(self, client: Opencode) -> None:
- app = client.app.get()
- assert_matches_type(App, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_raw_response_get(self, client: Opencode) -> None:
- response = client.app.with_raw_response.get()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- app = response.parse()
- assert_matches_type(App, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_streaming_response_get(self, client: Opencode) -> None:
- with client.app.with_streaming_response.get() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- app = response.parse()
- assert_matches_type(App, app, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_method_init(self, client: Opencode) -> None:
- app = client.app.init()
- assert_matches_type(AppInitResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_raw_response_init(self, client: Opencode) -> None:
- response = client.app.with_raw_response.init()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- app = response.parse()
- assert_matches_type(AppInitResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_streaming_response_init(self, client: Opencode) -> None:
- with client.app.with_streaming_response.init() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- app = response.parse()
- assert_matches_type(AppInitResponse, app, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_log(self, client: Opencode) -> None:
@@ -96,6 +34,7 @@ def test_method_log_with_all_params(self, client: Opencode) -> None:
level="debug",
message="message",
service="service",
+ directory="directory",
extra={"foo": "bar"},
)
assert_matches_type(AppLogResponse, app, path=["response"])
@@ -132,36 +71,16 @@ def test_streaming_response_log(self, client: Opencode) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_modes(self, client: Opencode) -> None:
- app = client.app.modes()
- assert_matches_type(AppModesResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_raw_response_modes(self, client: Opencode) -> None:
- response = client.app.with_raw_response.modes()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- app = response.parse()
- assert_matches_type(AppModesResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_streaming_response_modes(self, client: Opencode) -> None:
- with client.app.with_streaming_response.modes() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- app = response.parse()
- assert_matches_type(AppModesResponse, app, path=["response"])
-
- assert cast(Any, response.is_closed) is True
+ def test_method_providers(self, client: Opencode) -> None:
+ app = client.app.providers()
+ assert_matches_type(AppProvidersResponse, app, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_providers(self, client: Opencode) -> None:
- app = client.app.providers()
+ def test_method_providers_with_all_params(self, client: Opencode) -> None:
+ app = client.app.providers(
+ directory="directory",
+ )
assert_matches_type(AppProvidersResponse, app, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@@ -192,62 +111,6 @@ class TestAsyncApp:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_method_get(self, async_client: AsyncOpencode) -> None:
- app = await async_client.app.get()
- assert_matches_type(App, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_raw_response_get(self, async_client: AsyncOpencode) -> None:
- response = await async_client.app.with_raw_response.get()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- app = await response.parse()
- assert_matches_type(App, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_streaming_response_get(self, async_client: AsyncOpencode) -> None:
- async with async_client.app.with_streaming_response.get() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- app = await response.parse()
- assert_matches_type(App, app, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_method_init(self, async_client: AsyncOpencode) -> None:
- app = await async_client.app.init()
- assert_matches_type(AppInitResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_raw_response_init(self, async_client: AsyncOpencode) -> None:
- response = await async_client.app.with_raw_response.init()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- app = await response.parse()
- assert_matches_type(AppInitResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_streaming_response_init(self, async_client: AsyncOpencode) -> None:
- async with async_client.app.with_streaming_response.init() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- app = await response.parse()
- assert_matches_type(AppInitResponse, app, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_log(self, async_client: AsyncOpencode) -> None:
@@ -265,6 +128,7 @@ async def test_method_log_with_all_params(self, async_client: AsyncOpencode) ->
level="debug",
message="message",
service="service",
+ directory="directory",
extra={"foo": "bar"},
)
assert_matches_type(AppLogResponse, app, path=["response"])
@@ -301,36 +165,16 @@ async def test_streaming_response_log(self, async_client: AsyncOpencode) -> None
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_modes(self, async_client: AsyncOpencode) -> None:
- app = await async_client.app.modes()
- assert_matches_type(AppModesResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_raw_response_modes(self, async_client: AsyncOpencode) -> None:
- response = await async_client.app.with_raw_response.modes()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- app = await response.parse()
- assert_matches_type(AppModesResponse, app, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_streaming_response_modes(self, async_client: AsyncOpencode) -> None:
- async with async_client.app.with_streaming_response.modes() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- app = await response.parse()
- assert_matches_type(AppModesResponse, app, path=["response"])
-
- assert cast(Any, response.is_closed) is True
+ async def test_method_providers(self, async_client: AsyncOpencode) -> None:
+ app = await async_client.app.providers()
+ assert_matches_type(AppProvidersResponse, app, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_providers(self, async_client: AsyncOpencode) -> None:
- app = await async_client.app.providers()
+ async def test_method_providers_with_all_params(self, async_client: AsyncOpencode) -> None:
+ app = await async_client.app.providers(
+ directory="directory",
+ )
assert_matches_type(AppProvidersResponse, app, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
diff --git a/tests/api_resources/test_command.py b/tests/api_resources/test_command.py
new file mode 100644
index 0000000..c966fef
--- /dev/null
+++ b/tests/api_resources/test_command.py
@@ -0,0 +1,96 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from opencode_ai import Opencode, AsyncOpencode
+from tests.utils import assert_matches_type
+from opencode_ai.types import CommandListResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestCommand:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Opencode) -> None:
+ command = client.command.list()
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Opencode) -> None:
+ command = client.command.list(
+ directory="directory",
+ )
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Opencode) -> None:
+ response = client.command.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ command = response.parse()
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Opencode) -> None:
+ with client.command.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ command = response.parse()
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncCommand:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpencode) -> None:
+ command = await async_client.command.list()
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None:
+ command = await async_client.command.list(
+ directory="directory",
+ )
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.command.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ command = await response.parse()
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None:
+ async with async_client.command.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ command = await response.parse()
+ assert_matches_type(CommandListResponse, command, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_config.py b/tests/api_resources/test_config.py
index 86e4c8f..6c6f053 100644
--- a/tests/api_resources/test_config.py
+++ b/tests/api_resources/test_config.py
@@ -23,6 +23,14 @@ def test_method_get(self, client: Opencode) -> None:
config = client.config.get()
assert_matches_type(Config, config, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_get_with_all_params(self, client: Opencode) -> None:
+ config = client.config.get(
+ directory="directory",
+ )
+ assert_matches_type(Config, config, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_get(self, client: Opencode) -> None:
@@ -57,6 +65,14 @@ async def test_method_get(self, async_client: AsyncOpencode) -> None:
config = await async_client.config.get()
assert_matches_type(Config, config, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_get_with_all_params(self, async_client: AsyncOpencode) -> None:
+ config = await async_client.config.get(
+ directory="directory",
+ )
+ assert_matches_type(Config, config, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_get(self, async_client: AsyncOpencode) -> None:
diff --git a/tests/api_resources/test_event.py b/tests/api_resources/test_event.py
index 95de55b..6955fbe 100644
--- a/tests/api_resources/test_event.py
+++ b/tests/api_resources/test_event.py
@@ -15,13 +15,21 @@
class TestEvent:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
- @pytest.mark.skip(reason="Prism tests are disabled")
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
@parametrize
def test_method_list(self, client: Opencode) -> None:
event_stream = client.event.list()
event_stream.response.close()
- @pytest.mark.skip(reason="Prism tests are disabled")
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Opencode) -> None:
+ event_stream = client.event.list(
+ directory="directory",
+ )
+ event_stream.response.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
@parametrize
def test_raw_response_list(self, client: Opencode) -> None:
response = client.event.with_raw_response.list()
@@ -30,7 +38,7 @@ def test_raw_response_list(self, client: Opencode) -> None:
stream = response.parse()
stream.close()
- @pytest.mark.skip(reason="Prism tests are disabled")
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
@parametrize
def test_streaming_response_list(self, client: Opencode) -> None:
with client.event.with_streaming_response.list() as response:
@@ -48,13 +56,21 @@ class TestAsyncEvent:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
- @pytest.mark.skip(reason="Prism tests are disabled")
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
@parametrize
async def test_method_list(self, async_client: AsyncOpencode) -> None:
event_stream = await async_client.event.list()
await event_stream.response.aclose()
- @pytest.mark.skip(reason="Prism tests are disabled")
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None:
+ event_stream = await async_client.event.list(
+ directory="directory",
+ )
+ await event_stream.response.aclose()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
@parametrize
async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
response = await async_client.event.with_raw_response.list()
@@ -63,7 +79,7 @@ async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
stream = await response.parse()
await stream.close()
- @pytest.mark.skip(reason="Prism tests are disabled")
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
@parametrize
async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None:
async with async_client.event.with_streaming_response.list() as response:
diff --git a/tests/api_resources/test_file.py b/tests/api_resources/test_file.py
index cd09854..5641905 100644
--- a/tests/api_resources/test_file.py
+++ b/tests/api_resources/test_file.py
@@ -9,7 +9,11 @@
from opencode_ai import Opencode, AsyncOpencode
from tests.utils import assert_matches_type
-from opencode_ai.types import FileReadResponse, FileStatusResponse
+from opencode_ai.types import (
+ FileListResponse,
+ FileReadResponse,
+ FileStatusResponse,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -17,6 +21,49 @@
class TestFile:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Opencode) -> None:
+ file = client.file.list(
+ path="path",
+ )
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Opencode) -> None:
+ file = client.file.list(
+ path="path",
+ directory="directory",
+ )
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Opencode) -> None:
+ response = client.file.with_raw_response.list(
+ path="path",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Opencode) -> None:
+ with client.file.with_streaming_response.list(
+ path="path",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = response.parse()
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_read(self, client: Opencode) -> None:
@@ -25,6 +72,15 @@ def test_method_read(self, client: Opencode) -> None:
)
assert_matches_type(FileReadResponse, file, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_read_with_all_params(self, client: Opencode) -> None:
+ file = client.file.read(
+ path="path",
+ directory="directory",
+ )
+ assert_matches_type(FileReadResponse, file, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_read(self, client: Opencode) -> None:
@@ -57,6 +113,14 @@ def test_method_status(self, client: Opencode) -> None:
file = client.file.status()
assert_matches_type(FileStatusResponse, file, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_status_with_all_params(self, client: Opencode) -> None:
+ file = client.file.status(
+ directory="directory",
+ )
+ assert_matches_type(FileStatusResponse, file, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_status(self, client: Opencode) -> None:
@@ -85,6 +149,49 @@ class TestAsyncFile:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpencode) -> None:
+ file = await async_client.file.list(
+ path="path",
+ )
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None:
+ file = await async_client.file.list(
+ path="path",
+ directory="directory",
+ )
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.file.with_raw_response.list(
+ path="path",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = await response.parse()
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None:
+ async with async_client.file.with_streaming_response.list(
+ path="path",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = await response.parse()
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_read(self, async_client: AsyncOpencode) -> None:
@@ -93,6 +200,15 @@ async def test_method_read(self, async_client: AsyncOpencode) -> None:
)
assert_matches_type(FileReadResponse, file, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_read_with_all_params(self, async_client: AsyncOpencode) -> None:
+ file = await async_client.file.read(
+ path="path",
+ directory="directory",
+ )
+ assert_matches_type(FileReadResponse, file, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_read(self, async_client: AsyncOpencode) -> None:
@@ -125,6 +241,14 @@ async def test_method_status(self, async_client: AsyncOpencode) -> None:
file = await async_client.file.status()
assert_matches_type(FileStatusResponse, file, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_status_with_all_params(self, async_client: AsyncOpencode) -> None:
+ file = await async_client.file.status(
+ directory="directory",
+ )
+ assert_matches_type(FileStatusResponse, file, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_status(self, async_client: AsyncOpencode) -> None:
diff --git a/tests/api_resources/test_find.py b/tests/api_resources/test_find.py
index cd54e28..40b1e5d 100644
--- a/tests/api_resources/test_find.py
+++ b/tests/api_resources/test_find.py
@@ -29,6 +29,15 @@ def test_method_files(self, client: Opencode) -> None:
)
assert_matches_type(FindFilesResponse, find, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_files_with_all_params(self, client: Opencode) -> None:
+ find = client.find.files(
+ query="query",
+ directory="directory",
+ )
+ assert_matches_type(FindFilesResponse, find, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_files(self, client: Opencode) -> None:
@@ -63,6 +72,15 @@ def test_method_symbols(self, client: Opencode) -> None:
)
assert_matches_type(FindSymbolsResponse, find, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_symbols_with_all_params(self, client: Opencode) -> None:
+ find = client.find.symbols(
+ query="query",
+ directory="directory",
+ )
+ assert_matches_type(FindSymbolsResponse, find, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_symbols(self, client: Opencode) -> None:
@@ -97,6 +115,15 @@ def test_method_text(self, client: Opencode) -> None:
)
assert_matches_type(FindTextResponse, find, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_text_with_all_params(self, client: Opencode) -> None:
+ find = client.find.text(
+ pattern="pattern",
+ directory="directory",
+ )
+ assert_matches_type(FindTextResponse, find, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_text(self, client: Opencode) -> None:
@@ -137,6 +164,15 @@ async def test_method_files(self, async_client: AsyncOpencode) -> None:
)
assert_matches_type(FindFilesResponse, find, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_files_with_all_params(self, async_client: AsyncOpencode) -> None:
+ find = await async_client.find.files(
+ query="query",
+ directory="directory",
+ )
+ assert_matches_type(FindFilesResponse, find, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_files(self, async_client: AsyncOpencode) -> None:
@@ -171,6 +207,15 @@ async def test_method_symbols(self, async_client: AsyncOpencode) -> None:
)
assert_matches_type(FindSymbolsResponse, find, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_symbols_with_all_params(self, async_client: AsyncOpencode) -> None:
+ find = await async_client.find.symbols(
+ query="query",
+ directory="directory",
+ )
+ assert_matches_type(FindSymbolsResponse, find, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_symbols(self, async_client: AsyncOpencode) -> None:
@@ -205,6 +250,15 @@ async def test_method_text(self, async_client: AsyncOpencode) -> None:
)
assert_matches_type(FindTextResponse, find, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_text_with_all_params(self, async_client: AsyncOpencode) -> None:
+ find = await async_client.find.text(
+ pattern="pattern",
+ directory="directory",
+ )
+ assert_matches_type(FindTextResponse, find, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_text(self, async_client: AsyncOpencode) -> None:
diff --git a/tests/api_resources/test_path.py b/tests/api_resources/test_path.py
new file mode 100644
index 0000000..c612834
--- /dev/null
+++ b/tests/api_resources/test_path.py
@@ -0,0 +1,96 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from opencode_ai import Opencode, AsyncOpencode
+from tests.utils import assert_matches_type
+from opencode_ai.types import Path
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestPath:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_get(self, client: Opencode) -> None:
+ path = client.path.get()
+ assert_matches_type(Path, path, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_get_with_all_params(self, client: Opencode) -> None:
+ path = client.path.get(
+ directory="directory",
+ )
+ assert_matches_type(Path, path, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_get(self, client: Opencode) -> None:
+ response = client.path.with_raw_response.get()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ path = response.parse()
+ assert_matches_type(Path, path, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_get(self, client: Opencode) -> None:
+ with client.path.with_streaming_response.get() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ path = response.parse()
+ assert_matches_type(Path, path, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncPath:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_get(self, async_client: AsyncOpencode) -> None:
+ path = await async_client.path.get()
+ assert_matches_type(Path, path, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_get_with_all_params(self, async_client: AsyncOpencode) -> None:
+ path = await async_client.path.get(
+ directory="directory",
+ )
+ assert_matches_type(Path, path, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.path.with_raw_response.get()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ path = await response.parse()
+ assert_matches_type(Path, path, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncOpencode) -> None:
+ async with async_client.path.with_streaming_response.get() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ path = await response.parse()
+ assert_matches_type(Path, path, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_project.py
new file mode 100644
index 0000000..b0ea3f4
--- /dev/null
+++ b/tests/api_resources/test_project.py
@@ -0,0 +1,168 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from opencode_ai import Opencode, AsyncOpencode
+from tests.utils import assert_matches_type
+from opencode_ai.types import Project, ProjectListResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestProject:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Opencode) -> None:
+ project = client.project.list()
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Opencode) -> None:
+ project = client.project.list(
+ directory="directory",
+ )
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Opencode) -> None:
+ response = client.project.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = response.parse()
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Opencode) -> None:
+ with client.project.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = response.parse()
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_current(self, client: Opencode) -> None:
+ project = client.project.current()
+ assert_matches_type(Project, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_current_with_all_params(self, client: Opencode) -> None:
+ project = client.project.current(
+ directory="directory",
+ )
+ assert_matches_type(Project, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_current(self, client: Opencode) -> None:
+ response = client.project.with_raw_response.current()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = response.parse()
+ assert_matches_type(Project, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_current(self, client: Opencode) -> None:
+ with client.project.with_streaming_response.current() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = response.parse()
+ assert_matches_type(Project, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncProject:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpencode) -> None:
+ project = await async_client.project.list()
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None:
+ project = await async_client.project.list(
+ directory="directory",
+ )
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.project.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = await response.parse()
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None:
+ async with async_client.project.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = await response.parse()
+ assert_matches_type(ProjectListResponse, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_current(self, async_client: AsyncOpencode) -> None:
+ project = await async_client.project.current()
+ assert_matches_type(Project, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_current_with_all_params(self, async_client: AsyncOpencode) -> None:
+ project = await async_client.project.current(
+ directory="directory",
+ )
+ assert_matches_type(Project, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_current(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.project.with_raw_response.current()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ project = await response.parse()
+ assert_matches_type(Project, project, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_current(self, async_client: AsyncOpencode) -> None:
+ async with async_client.project.with_streaming_response.current() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ project = await response.parse()
+ assert_matches_type(Project, project, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_session.py b/tests/api_resources/test_session.py
index 15b39d7..555bb75 100644
--- a/tests/api_resources/test_session.py
+++ b/tests/api_resources/test_session.py
@@ -10,15 +10,19 @@
from opencode_ai import Opencode, AsyncOpencode
from tests.utils import assert_matches_type
from opencode_ai.types import (
- Session,
AssistantMessage,
SessionInitResponse,
SessionListResponse,
SessionAbortResponse,
SessionDeleteResponse,
+ SessionPromptResponse,
+ SessionCommandResponse,
+ SessionMessageResponse,
+ SessionChildrenResponse,
SessionMessagesResponse,
SessionSummarizeResponse,
)
+from opencode_ai.types.session import Session
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -32,6 +36,16 @@ def test_method_create(self, client: Opencode) -> None:
session = client.session.create()
assert_matches_type(Session, session, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Opencode) -> None:
+ session = client.session.create(
+ directory="directory",
+ parent_id="parentID",
+ title="title",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_create(self, client: Opencode) -> None:
@@ -54,12 +68,72 @@ def test_streaming_response_create(self, client: Opencode) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_update(self, client: Opencode) -> None:
+ session = client.session.update(
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_update_with_all_params(self, client: Opencode) -> None:
+ session = client.session.update(
+ id="id",
+ directory="directory",
+ title="title",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_update(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_update(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_update(self, client: Opencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.session.with_raw_response.update(
+ id="",
+ )
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_list(self, client: Opencode) -> None:
session = client.session.list()
assert_matches_type(SessionListResponse, session, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Opencode) -> None:
+ session = client.session.list(
+ directory="directory",
+ )
+ assert_matches_type(SessionListResponse, session, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_list(self, client: Opencode) -> None:
@@ -86,7 +160,16 @@ def test_streaming_response_list(self, client: Opencode) -> None:
@parametrize
def test_method_delete(self, client: Opencode) -> None:
session = client.session.delete(
- "id",
+ id="id",
+ )
+ assert_matches_type(SessionDeleteResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_delete_with_all_params(self, client: Opencode) -> None:
+ session = client.session.delete(
+ id="id",
+ directory="directory",
)
assert_matches_type(SessionDeleteResponse, session, path=["response"])
@@ -94,7 +177,7 @@ def test_method_delete(self, client: Opencode) -> None:
@parametrize
def test_raw_response_delete(self, client: Opencode) -> None:
response = client.session.with_raw_response.delete(
- "id",
+ id="id",
)
assert response.is_closed is True
@@ -106,7 +189,7 @@ def test_raw_response_delete(self, client: Opencode) -> None:
@parametrize
def test_streaming_response_delete(self, client: Opencode) -> None:
with client.session.with_streaming_response.delete(
- "id",
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -121,14 +204,23 @@ def test_streaming_response_delete(self, client: Opencode) -> None:
def test_path_params_delete(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
client.session.with_raw_response.delete(
- "",
+ id="",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_abort(self, client: Opencode) -> None:
session = client.session.abort(
- "id",
+ id="id",
+ )
+ assert_matches_type(SessionAbortResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_abort_with_all_params(self, client: Opencode) -> None:
+ session = client.session.abort(
+ id="id",
+ directory="directory",
)
assert_matches_type(SessionAbortResponse, session, path=["response"])
@@ -136,7 +228,7 @@ def test_method_abort(self, client: Opencode) -> None:
@parametrize
def test_raw_response_abort(self, client: Opencode) -> None:
response = client.session.with_raw_response.abort(
- "id",
+ id="id",
)
assert response.is_closed is True
@@ -148,7 +240,7 @@ def test_raw_response_abort(self, client: Opencode) -> None:
@parametrize
def test_streaming_response_abort(self, client: Opencode) -> None:
with client.session.with_streaming_response.abort(
- "id",
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -163,408 +255,475 @@ def test_streaming_response_abort(self, client: Opencode) -> None:
def test_path_params_abort(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
client.session.with_raw_response.abort(
- "",
+ id="",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_chat(self, client: Opencode) -> None:
- session = client.session.chat(
+ def test_method_children(self, client: Opencode) -> None:
+ session = client.session.children(
id="id",
- model_id="modelID",
- parts=[
- {
- "text": "text",
- "type": "text",
- }
- ],
- provider_id="providerID",
)
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_chat_with_all_params(self, client: Opencode) -> None:
- session = client.session.chat(
+ def test_method_children_with_all_params(self, client: Opencode) -> None:
+ session = client.session.children(
id="id",
- model_id="modelID",
- parts=[
- {
- "text": "text",
- "type": "text",
- "id": "id",
- "synthetic": True,
- "time": {
- "start": 0,
- "end": 0,
- },
- }
- ],
- provider_id="providerID",
- message_id="msg",
- mode="mode",
- system="system",
- tools={"foo": True},
+ directory="directory",
)
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_chat(self, client: Opencode) -> None:
- response = client.session.with_raw_response.chat(
+ def test_raw_response_children(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.children(
id="id",
- model_id="modelID",
- parts=[
- {
- "text": "text",
- "type": "text",
- }
- ],
- provider_id="providerID",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_chat(self, client: Opencode) -> None:
- with client.session.with_streaming_response.chat(
+ def test_streaming_response_children(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.children(
id="id",
- model_id="modelID",
- parts=[
- {
- "text": "text",
- "type": "text",
- }
- ],
- provider_id="providerID",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_chat(self, client: Opencode) -> None:
+ def test_path_params_children(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.chat(
+ client.session.with_raw_response.children(
id="",
- model_id="modelID",
- parts=[
- {
- "text": "text",
- "type": "text",
- }
- ],
- provider_id="providerID",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_init(self, client: Opencode) -> None:
- session = client.session.init(
+ def test_method_command(self, client: Opencode) -> None:
+ session = client.session.command(
id="id",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
+ arguments="arguments",
+ command="command",
)
- assert_matches_type(SessionInitResponse, session, path=["response"])
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_init(self, client: Opencode) -> None:
- response = client.session.with_raw_response.init(
+ def test_method_command_with_all_params(self, client: Opencode) -> None:
+ session = client.session.command(
id="id",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
+ arguments="arguments",
+ command="command",
+ directory="directory",
+ agent="agent",
+ message_id="msg",
+ model="model",
+ )
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_command(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.command(
+ id="id",
+ arguments="arguments",
+ command="command",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(SessionInitResponse, session, path=["response"])
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_init(self, client: Opencode) -> None:
- with client.session.with_streaming_response.init(
+ def test_streaming_response_command(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.command(
id="id",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
+ arguments="arguments",
+ command="command",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(SessionInitResponse, session, path=["response"])
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_init(self, client: Opencode) -> None:
+ def test_path_params_command(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.init(
+ client.session.with_raw_response.command(
id="",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
+ arguments="arguments",
+ command="command",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_messages(self, client: Opencode) -> None:
- session = client.session.messages(
- "id",
+ def test_method_get(self, client: Opencode) -> None:
+ session = client.session.get(
+ id="id",
)
- assert_matches_type(SessionMessagesResponse, session, path=["response"])
+ assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_messages(self, client: Opencode) -> None:
- response = client.session.with_raw_response.messages(
- "id",
+ def test_method_get_with_all_params(self, client: Opencode) -> None:
+ session = client.session.get(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_get(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.get(
+ id="id",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(SessionMessagesResponse, session, path=["response"])
+ assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_messages(self, client: Opencode) -> None:
- with client.session.with_streaming_response.messages(
- "id",
+ def test_streaming_response_get(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.get(
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(SessionMessagesResponse, session, path=["response"])
+ assert_matches_type(Session, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_messages(self, client: Opencode) -> None:
+ def test_path_params_get(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.messages(
- "",
+ client.session.with_raw_response.get(
+ id="",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_revert(self, client: Opencode) -> None:
- session = client.session.revert(
+ def test_method_init(self, client: Opencode) -> None:
+ session = client.session.init(
id="id",
- message_id="msg",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
)
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionInitResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_revert_with_all_params(self, client: Opencode) -> None:
- session = client.session.revert(
+ def test_method_init_with_all_params(self, client: Opencode) -> None:
+ session = client.session.init(
id="id",
- message_id="msg",
- part_id="prt",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
+ directory="directory",
)
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionInitResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_revert(self, client: Opencode) -> None:
- response = client.session.with_raw_response.revert(
+ def test_raw_response_init(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.init(
id="id",
- message_id="msg",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionInitResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_revert(self, client: Opencode) -> None:
- with client.session.with_streaming_response.revert(
+ def test_streaming_response_init(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.init(
id="id",
- message_id="msg",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionInitResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_revert(self, client: Opencode) -> None:
+ def test_path_params_init(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.revert(
+ client.session.with_raw_response.init(
id="",
- message_id="msg",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_share(self, client: Opencode) -> None:
- session = client.session.share(
- "id",
+ def test_method_message(self, client: Opencode) -> None:
+ session = client.session.message(
+ message_id="messageID",
+ id="id",
)
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_share(self, client: Opencode) -> None:
- response = client.session.with_raw_response.share(
- "id",
+ def test_method_message_with_all_params(self, client: Opencode) -> None:
+ session = client.session.message(
+ message_id="messageID",
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_message(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.message(
+ message_id="messageID",
+ id="id",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_share(self, client: Opencode) -> None:
- with client.session.with_streaming_response.share(
- "id",
+ def test_streaming_response_message(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.message(
+ message_id="messageID",
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_share(self, client: Opencode) -> None:
+ def test_path_params_message(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.share(
- "",
+ client.session.with_raw_response.message(
+ message_id="messageID",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"):
+ client.session.with_raw_response.message(
+ message_id="",
+ id="id",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_summarize(self, client: Opencode) -> None:
- session = client.session.summarize(
- id="id",
- model_id="modelID",
- provider_id="providerID",
+ def test_method_messages(self, client: Opencode) -> None:
+ session = client.session.messages(
+ id="id",
)
- assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_summarize(self, client: Opencode) -> None:
- response = client.session.with_raw_response.summarize(
+ def test_method_messages_with_all_params(self, client: Opencode) -> None:
+ session = client.session.messages(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_messages(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.messages(
id="id",
- model_id="modelID",
- provider_id="providerID",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_summarize(self, client: Opencode) -> None:
- with client.session.with_streaming_response.summarize(
+ def test_streaming_response_messages(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.messages(
id="id",
- model_id="modelID",
- provider_id="providerID",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_summarize(self, client: Opencode) -> None:
+ def test_path_params_messages(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.summarize(
+ client.session.with_raw_response.messages(
id="",
- model_id="modelID",
- provider_id="providerID",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_unrevert(self, client: Opencode) -> None:
- session = client.session.unrevert(
- "id",
+ def test_method_prompt(self, client: Opencode) -> None:
+ session = client.session.prompt(
+ id="id",
+ parts=[
+ {
+ "text": "text",
+ "type": "text",
+ }
+ ],
)
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_unrevert(self, client: Opencode) -> None:
- response = client.session.with_raw_response.unrevert(
- "id",
+ def test_method_prompt_with_all_params(self, client: Opencode) -> None:
+ session = client.session.prompt(
+ id="id",
+ parts=[
+ {
+ "text": "text",
+ "type": "text",
+ "id": "id",
+ "synthetic": True,
+ "time": {
+ "start": 0,
+ "end": 0,
+ },
+ }
+ ],
+ directory="directory",
+ agent="agent",
+ message_id="msg",
+ model={
+ "model_id": "modelID",
+ "provider_id": "providerID",
+ },
+ system="system",
+ tools={"foo": True},
+ )
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_prompt(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.prompt(
+ id="id",
+ parts=[
+ {
+ "text": "text",
+ "type": "text",
+ }
+ ],
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_unrevert(self, client: Opencode) -> None:
- with client.session.with_streaming_response.unrevert(
- "id",
+ def test_streaming_response_prompt(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.prompt(
+ id="id",
+ parts=[
+ {
+ "text": "text",
+ "type": "text",
+ }
+ ],
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_unrevert(self, client: Opencode) -> None:
+ def test_path_params_prompt(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.unrevert(
- "",
+ client.session.with_raw_response.prompt(
+ id="",
+ parts=[
+ {
+ "text": "text",
+ "type": "text",
+ }
+ ],
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_unshare(self, client: Opencode) -> None:
- session = client.session.unshare(
- "id",
+ def test_method_revert(self, client: Opencode) -> None:
+ session = client.session.revert(
+ id="id",
+ message_id="msg",
)
assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_unshare(self, client: Opencode) -> None:
- response = client.session.with_raw_response.unshare(
- "id",
+ def test_method_revert_with_all_params(self, client: Opencode) -> None:
+ session = client.session.revert(
+ id="id",
+ message_id="msg",
+ directory="directory",
+ part_id="prt",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_revert(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.revert(
+ id="id",
+ message_id="msg",
)
assert response.is_closed is True
@@ -574,9 +733,10 @@ def test_raw_response_unshare(self, client: Opencode) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_unshare(self, client: Opencode) -> None:
- with client.session.with_streaming_response.unshare(
- "id",
+ def test_streaming_response_revert(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.revert(
+ id="id",
+ message_id="msg",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -588,180 +748,886 @@ def test_streaming_response_unshare(self, client: Opencode) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_path_params_unshare(self, client: Opencode) -> None:
+ def test_path_params_revert(self, client: Opencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.session.with_raw_response.unshare(
- "",
+ client.session.with_raw_response.revert(
+ id="",
+ message_id="msg",
)
-
-class TestAsyncSession:
- parametrize = pytest.mark.parametrize(
- "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
- )
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_share(self, client: Opencode) -> None:
+ session = client.session.share(
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_create(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.create()
+ def test_method_share_with_all_params(self, client: Opencode) -> None:
+ session = client.session.share(
+ id="id",
+ directory="directory",
+ )
assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_create(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.create()
+ def test_raw_response_share(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.share(
+ id="id",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- session = await response.parse()
+ session = response.parse()
assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_create(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.create() as response:
+ def test_streaming_response_share(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.share(
+ id="id",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- session = await response.parse()
+ session = response.parse()
assert_matches_type(Session, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_list(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.list()
- assert_matches_type(SessionListResponse, session, path=["response"])
+ def test_path_params_share(self, client: Opencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.session.with_raw_response.share(
+ id="",
+ )
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.list()
+ def test_method_shell(self, client: Opencode) -> None:
+ session = client.session.shell(
+ id="id",
+ agent="agent",
+ command="command",
+ )
+ assert_matches_type(AssistantMessage, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_shell_with_all_params(self, client: Opencode) -> None:
+ session = client.session.shell(
+ id="id",
+ agent="agent",
+ command="command",
+ directory="directory",
+ )
+ assert_matches_type(AssistantMessage, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_shell(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.shell(
+ id="id",
+ agent="agent",
+ command="command",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- session = await response.parse()
- assert_matches_type(SessionListResponse, session, path=["response"])
+ session = response.parse()
+ assert_matches_type(AssistantMessage, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_shell(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.shell(
+ id="id",
+ agent="agent",
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(AssistantMessage, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_shell(self, client: Opencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.session.with_raw_response.shell(
+ id="",
+ agent="agent",
+ command="command",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_summarize(self, client: Opencode) -> None:
+ session = client.session.summarize(
+ id="id",
+ model_id="modelID",
+ provider_id="providerID",
+ )
+ assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_summarize_with_all_params(self, client: Opencode) -> None:
+ session = client.session.summarize(
+ id="id",
+ model_id="modelID",
+ provider_id="providerID",
+ directory="directory",
+ )
+ assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_summarize(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.summarize(
+ id="id",
+ model_id="modelID",
+ provider_id="providerID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_summarize(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.summarize(
+ id="id",
+ model_id="modelID",
+ provider_id="providerID",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_summarize(self, client: Opencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.session.with_raw_response.summarize(
+ id="",
+ model_id="modelID",
+ provider_id="providerID",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_unrevert(self, client: Opencode) -> None:
+ session = client.session.unrevert(
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_unrevert_with_all_params(self, client: Opencode) -> None:
+ session = client.session.unrevert(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_unrevert(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.unrevert(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_unrevert(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.unrevert(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_unrevert(self, client: Opencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.session.with_raw_response.unrevert(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_unshare(self, client: Opencode) -> None:
+ session = client.session.unshare(
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_unshare_with_all_params(self, client: Opencode) -> None:
+ session = client.session.unshare(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_unshare(self, client: Opencode) -> None:
+ response = client.session.with_raw_response.unshare(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_unshare(self, client: Opencode) -> None:
+ with client.session.with_streaming_response.unshare(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_unshare(self, client: Opencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.session.with_raw_response.unshare(
+ id="",
+ )
+
+
+class TestAsyncSession:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.create()
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.create(
+ directory="directory",
+ parent_id="parentID",
+ title="title",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.update(
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.update(
+ id="id",
+ directory="directory",
+ title="title",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.with_raw_response.update(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.list()
+ assert_matches_type(SessionListResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.list(
+ directory="directory",
+ )
+ assert_matches_type(SessionListResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(SessionListResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(SessionListResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.delete(
+ id="id",
+ )
+ assert_matches_type(SessionDeleteResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_delete_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.delete(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(SessionDeleteResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.delete(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(SessionDeleteResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.delete(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(SessionDeleteResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.with_raw_response.delete(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_abort(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.abort(
+ id="id",
+ )
+ assert_matches_type(SessionAbortResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_abort_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.abort(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(SessionAbortResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_abort(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.abort(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(SessionAbortResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_abort(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.abort(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(SessionAbortResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_abort(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.with_raw_response.abort(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_children(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.children(
+ id="id",
+ )
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_children_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.children(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_children(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.children(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_children(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.children(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(SessionChildrenResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_children(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.with_raw_response.children(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_command(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.command(
+ id="id",
+ arguments="arguments",
+ command="command",
+ )
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_command_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.command(
+ id="id",
+ arguments="arguments",
+ command="command",
+ directory="directory",
+ agent="agent",
+ message_id="msg",
+ model="model",
+ )
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_command(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.command(
+ id="id",
+ arguments="arguments",
+ command="command",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_command(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.command(
+ id="id",
+ arguments="arguments",
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(SessionCommandResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_command(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.with_raw_response.command(
+ id="",
+ arguments="arguments",
+ command="command",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_get(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.get(
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_get_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.get(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.get(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.get(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(Session, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.with_raw_response.get(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_init(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.init(
+ id="id",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
+ )
+ assert_matches_type(SessionInitResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_init_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.init(
+ id="id",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
+ directory="directory",
+ )
+ assert_matches_type(SessionInitResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_init(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.init(
+ id="id",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = await response.parse()
+ assert_matches_type(SessionInitResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.list() as response:
+ async def test_streaming_response_init(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.init(
+ id="id",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(SessionListResponse, session, path=["response"])
+ assert_matches_type(SessionInitResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_delete(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.delete(
- "id",
+ async def test_path_params_init(self, async_client: AsyncOpencode) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.session.with_raw_response.init(
+ id="",
+ message_id="messageID",
+ model_id="modelID",
+ provider_id="providerID",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_message(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.message(
+ message_id="messageID",
+ id="id",
)
- assert_matches_type(SessionDeleteResponse, session, path=["response"])
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_delete(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.delete(
- "id",
+ async def test_method_message_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.message(
+ message_id="messageID",
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_message(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.message(
+ message_id="messageID",
+ id="id",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(SessionDeleteResponse, session, path=["response"])
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_delete(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.delete(
- "id",
+ async def test_streaming_response_message(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.message(
+ message_id="messageID",
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(SessionDeleteResponse, session, path=["response"])
+ assert_matches_type(SessionMessageResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_path_params_delete(self, async_client: AsyncOpencode) -> None:
+ async def test_path_params_message(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.session.with_raw_response.delete(
- "",
+ await async_client.session.with_raw_response.message(
+ message_id="messageID",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"):
+ await async_client.session.with_raw_response.message(
+ message_id="",
+ id="id",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_abort(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.abort(
- "id",
+ async def test_method_messages(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.messages(
+ id="id",
)
- assert_matches_type(SessionAbortResponse, session, path=["response"])
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_abort(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.abort(
- "id",
+ async def test_method_messages_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.messages(
+ id="id",
+ directory="directory",
+ )
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_messages(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.messages(
+ id="id",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(SessionAbortResponse, session, path=["response"])
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_abort(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.abort(
- "id",
+ async def test_streaming_response_messages(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.messages(
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(SessionAbortResponse, session, path=["response"])
+ assert_matches_type(SessionMessagesResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_path_params_abort(self, async_client: AsyncOpencode) -> None:
+ async def test_path_params_messages(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.session.with_raw_response.abort(
- "",
+ await async_client.session.with_raw_response.messages(
+ id="",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_chat(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.chat(
+ async def test_method_prompt(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.prompt(
id="id",
- model_id="modelID",
parts=[
{
"text": "text",
"type": "text",
}
],
- provider_id="providerID",
)
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_chat_with_all_params(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.chat(
+ async def test_method_prompt_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.prompt(
id="id",
- model_id="modelID",
parts=[
{
"text": "text",
@@ -774,193 +1640,149 @@ async def test_method_chat_with_all_params(self, async_client: AsyncOpencode) ->
},
}
],
- provider_id="providerID",
+ directory="directory",
+ agent="agent",
message_id="msg",
- mode="mode",
+ model={
+ "model_id": "modelID",
+ "provider_id": "providerID",
+ },
system="system",
tools={"foo": True},
)
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_chat(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.chat(
+ async def test_raw_response_prompt(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.prompt(
id="id",
- model_id="modelID",
parts=[
{
"text": "text",
"type": "text",
}
],
- provider_id="providerID",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_chat(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.chat(
+ async def test_streaming_response_prompt(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.prompt(
id="id",
- model_id="modelID",
parts=[
{
"text": "text",
"type": "text",
}
],
- provider_id="providerID",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(AssistantMessage, session, path=["response"])
+ assert_matches_type(SessionPromptResponse, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_path_params_chat(self, async_client: AsyncOpencode) -> None:
+ async def test_path_params_prompt(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.session.with_raw_response.chat(
+ await async_client.session.with_raw_response.prompt(
id="",
- model_id="modelID",
parts=[
{
"text": "text",
"type": "text",
}
],
- provider_id="providerID",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_init(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.init(
+ async def test_method_revert(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.revert(
id="id",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
+ message_id="msg",
)
- assert_matches_type(SessionInitResponse, session, path=["response"])
+ assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_init(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.init(
+ async def test_method_revert_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.revert(
id="id",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
+ message_id="msg",
+ directory="directory",
+ part_id="prt",
)
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- session = await response.parse()
- assert_matches_type(SessionInitResponse, session, path=["response"])
+ assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_init(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.init(
+ async def test_raw_response_revert(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.revert(
id="id",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- session = await response.parse()
- assert_matches_type(SessionInitResponse, session, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_path_params_init(self, async_client: AsyncOpencode) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.session.with_raw_response.init(
- id="",
- message_id="messageID",
- model_id="modelID",
- provider_id="providerID",
- )
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_method_messages(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.messages(
- "id",
- )
- assert_matches_type(SessionMessagesResponse, session, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_raw_response_messages(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.messages(
- "id",
+ message_id="msg",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(SessionMessagesResponse, session, path=["response"])
+ assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_messages(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.messages(
- "id",
+ async def test_streaming_response_revert(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.revert(
+ id="id",
+ message_id="msg",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(SessionMessagesResponse, session, path=["response"])
+ assert_matches_type(Session, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_path_params_messages(self, async_client: AsyncOpencode) -> None:
+ async def test_path_params_revert(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.session.with_raw_response.messages(
- "",
+ await async_client.session.with_raw_response.revert(
+ id="",
+ message_id="msg",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_revert(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.revert(
+ async def test_method_share(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.share(
id="id",
- message_id="msg",
)
assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_revert_with_all_params(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.revert(
+ async def test_method_share_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.share(
id="id",
- message_id="msg",
- part_id="prt",
+ directory="directory",
)
assert_matches_type(Session, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_revert(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.revert(
+ async def test_raw_response_share(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.share(
id="id",
- message_id="msg",
)
assert response.is_closed is True
@@ -970,10 +1792,9 @@ async def test_raw_response_revert(self, async_client: AsyncOpencode) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_revert(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.revert(
+ async def test_streaming_response_share(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.share(
id="id",
- message_id="msg",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -985,53 +1806,71 @@ async def test_streaming_response_revert(self, async_client: AsyncOpencode) -> N
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_path_params_revert(self, async_client: AsyncOpencode) -> None:
+ async def test_path_params_share(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.session.with_raw_response.revert(
+ await async_client.session.with_raw_response.share(
id="",
- message_id="msg",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_share(self, async_client: AsyncOpencode) -> None:
- session = await async_client.session.share(
- "id",
+ async def test_method_shell(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.shell(
+ id="id",
+ agent="agent",
+ command="command",
)
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(AssistantMessage, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_share(self, async_client: AsyncOpencode) -> None:
- response = await async_client.session.with_raw_response.share(
- "id",
+ async def test_method_shell_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.shell(
+ id="id",
+ agent="agent",
+ command="command",
+ directory="directory",
+ )
+ assert_matches_type(AssistantMessage, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_shell(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.session.with_raw_response.shell(
+ id="id",
+ agent="agent",
+ command="command",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(AssistantMessage, session, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_share(self, async_client: AsyncOpencode) -> None:
- async with async_client.session.with_streaming_response.share(
- "id",
+ async def test_streaming_response_shell(self, async_client: AsyncOpencode) -> None:
+ async with async_client.session.with_streaming_response.shell(
+ id="id",
+ agent="agent",
+ command="command",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
session = await response.parse()
- assert_matches_type(Session, session, path=["response"])
+ assert_matches_type(AssistantMessage, session, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_path_params_share(self, async_client: AsyncOpencode) -> None:
+ async def test_path_params_shell(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.session.with_raw_response.share(
- "",
+ await async_client.session.with_raw_response.shell(
+ id="",
+ agent="agent",
+ command="command",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@@ -1044,6 +1883,17 @@ async def test_method_summarize(self, async_client: AsyncOpencode) -> None:
)
assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_summarize_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.summarize(
+ id="id",
+ model_id="modelID",
+ provider_id="providerID",
+ directory="directory",
+ )
+ assert_matches_type(SessionSummarizeResponse, session, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_summarize(self, async_client: AsyncOpencode) -> None:
@@ -1088,7 +1938,16 @@ async def test_path_params_summarize(self, async_client: AsyncOpencode) -> None:
@parametrize
async def test_method_unrevert(self, async_client: AsyncOpencode) -> None:
session = await async_client.session.unrevert(
- "id",
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_unrevert_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.unrevert(
+ id="id",
+ directory="directory",
)
assert_matches_type(Session, session, path=["response"])
@@ -1096,7 +1955,7 @@ async def test_method_unrevert(self, async_client: AsyncOpencode) -> None:
@parametrize
async def test_raw_response_unrevert(self, async_client: AsyncOpencode) -> None:
response = await async_client.session.with_raw_response.unrevert(
- "id",
+ id="id",
)
assert response.is_closed is True
@@ -1108,7 +1967,7 @@ async def test_raw_response_unrevert(self, async_client: AsyncOpencode) -> None:
@parametrize
async def test_streaming_response_unrevert(self, async_client: AsyncOpencode) -> None:
async with async_client.session.with_streaming_response.unrevert(
- "id",
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -1123,14 +1982,23 @@ async def test_streaming_response_unrevert(self, async_client: AsyncOpencode) ->
async def test_path_params_unrevert(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
await async_client.session.with_raw_response.unrevert(
- "",
+ id="",
)
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_unshare(self, async_client: AsyncOpencode) -> None:
session = await async_client.session.unshare(
- "id",
+ id="id",
+ )
+ assert_matches_type(Session, session, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_unshare_with_all_params(self, async_client: AsyncOpencode) -> None:
+ session = await async_client.session.unshare(
+ id="id",
+ directory="directory",
)
assert_matches_type(Session, session, path=["response"])
@@ -1138,7 +2006,7 @@ async def test_method_unshare(self, async_client: AsyncOpencode) -> None:
@parametrize
async def test_raw_response_unshare(self, async_client: AsyncOpencode) -> None:
response = await async_client.session.with_raw_response.unshare(
- "id",
+ id="id",
)
assert response.is_closed is True
@@ -1150,7 +2018,7 @@ async def test_raw_response_unshare(self, async_client: AsyncOpencode) -> None:
@parametrize
async def test_streaming_response_unshare(self, async_client: AsyncOpencode) -> None:
async with async_client.session.with_streaming_response.unshare(
- "id",
+ id="id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -1165,5 +2033,5 @@ async def test_streaming_response_unshare(self, async_client: AsyncOpencode) ->
async def test_path_params_unshare(self, async_client: AsyncOpencode) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
await async_client.session.with_raw_response.unshare(
- "",
+ id="",
)
diff --git a/tests/api_resources/test_tui.py b/tests/api_resources/test_tui.py
index 06145a5..20ee299 100644
--- a/tests/api_resources/test_tui.py
+++ b/tests/api_resources/test_tui.py
@@ -9,7 +9,17 @@
from opencode_ai import Opencode, AsyncOpencode
from tests.utils import assert_matches_type
-from opencode_ai.types import TuiOpenHelpResponse, TuiAppendPromptResponse
+from opencode_ai.types import (
+ TuiOpenHelpResponse,
+ TuiShowToastResponse,
+ TuiOpenModelsResponse,
+ TuiOpenThemesResponse,
+ TuiClearPromptResponse,
+ TuiAppendPromptResponse,
+ TuiOpenSessionsResponse,
+ TuiSubmitPromptResponse,
+ TuiExecuteCommandResponse,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -25,6 +35,15 @@ def test_method_append_prompt(self, client: Opencode) -> None:
)
assert_matches_type(TuiAppendPromptResponse, tui, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_append_prompt_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.append_prompt(
+ text="text",
+ directory="directory",
+ )
+ assert_matches_type(TuiAppendPromptResponse, tui, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_append_prompt(self, client: Opencode) -> None:
@@ -51,12 +70,99 @@ def test_streaming_response_append_prompt(self, client: Opencode) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_clear_prompt(self, client: Opencode) -> None:
+ tui = client.tui.clear_prompt()
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_clear_prompt_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.clear_prompt(
+ directory="directory",
+ )
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_clear_prompt(self, client: Opencode) -> None:
+ response = client.tui.with_raw_response.clear_prompt()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = response.parse()
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_clear_prompt(self, client: Opencode) -> None:
+ with client.tui.with_streaming_response.clear_prompt() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = response.parse()
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_execute_command(self, client: Opencode) -> None:
+ tui = client.tui.execute_command(
+ command="command",
+ )
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_execute_command_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.execute_command(
+ command="command",
+ directory="directory",
+ )
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_execute_command(self, client: Opencode) -> None:
+ response = client.tui.with_raw_response.execute_command(
+ command="command",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = response.parse()
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_execute_command(self, client: Opencode) -> None:
+ with client.tui.with_streaming_response.execute_command(
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = response.parse()
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_open_help(self, client: Opencode) -> None:
tui = client.tui.open_help()
assert_matches_type(TuiOpenHelpResponse, tui, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_open_help_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.open_help(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenHelpResponse, tui, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_open_help(self, client: Opencode) -> None:
@@ -79,6 +185,198 @@ def test_streaming_response_open_help(self, client: Opencode) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_open_models(self, client: Opencode) -> None:
+ tui = client.tui.open_models()
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_open_models_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.open_models(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_open_models(self, client: Opencode) -> None:
+ response = client.tui.with_raw_response.open_models()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = response.parse()
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_open_models(self, client: Opencode) -> None:
+ with client.tui.with_streaming_response.open_models() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = response.parse()
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_open_sessions(self, client: Opencode) -> None:
+ tui = client.tui.open_sessions()
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_open_sessions_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.open_sessions(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_open_sessions(self, client: Opencode) -> None:
+ response = client.tui.with_raw_response.open_sessions()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = response.parse()
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_open_sessions(self, client: Opencode) -> None:
+ with client.tui.with_streaming_response.open_sessions() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = response.parse()
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_open_themes(self, client: Opencode) -> None:
+ tui = client.tui.open_themes()
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_open_themes_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.open_themes(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_open_themes(self, client: Opencode) -> None:
+ response = client.tui.with_raw_response.open_themes()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = response.parse()
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_open_themes(self, client: Opencode) -> None:
+ with client.tui.with_streaming_response.open_themes() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = response.parse()
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_show_toast(self, client: Opencode) -> None:
+ tui = client.tui.show_toast(
+ message="message",
+ variant="info",
+ )
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_show_toast_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.show_toast(
+ message="message",
+ variant="info",
+ directory="directory",
+ title="title",
+ )
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_show_toast(self, client: Opencode) -> None:
+ response = client.tui.with_raw_response.show_toast(
+ message="message",
+ variant="info",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = response.parse()
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_show_toast(self, client: Opencode) -> None:
+ with client.tui.with_streaming_response.show_toast(
+ message="message",
+ variant="info",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = response.parse()
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_submit_prompt(self, client: Opencode) -> None:
+ tui = client.tui.submit_prompt()
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_submit_prompt_with_all_params(self, client: Opencode) -> None:
+ tui = client.tui.submit_prompt(
+ directory="directory",
+ )
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_submit_prompt(self, client: Opencode) -> None:
+ response = client.tui.with_raw_response.submit_prompt()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = response.parse()
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_submit_prompt(self, client: Opencode) -> None:
+ with client.tui.with_streaming_response.submit_prompt() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = response.parse()
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
class TestAsyncTui:
parametrize = pytest.mark.parametrize(
@@ -93,6 +391,15 @@ async def test_method_append_prompt(self, async_client: AsyncOpencode) -> None:
)
assert_matches_type(TuiAppendPromptResponse, tui, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_append_prompt_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.append_prompt(
+ text="text",
+ directory="directory",
+ )
+ assert_matches_type(TuiAppendPromptResponse, tui, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_append_prompt(self, async_client: AsyncOpencode) -> None:
@@ -119,12 +426,99 @@ async def test_streaming_response_append_prompt(self, async_client: AsyncOpencod
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_clear_prompt(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.clear_prompt()
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_clear_prompt_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.clear_prompt(
+ directory="directory",
+ )
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_clear_prompt(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.tui.with_raw_response.clear_prompt()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = await response.parse()
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_clear_prompt(self, async_client: AsyncOpencode) -> None:
+ async with async_client.tui.with_streaming_response.clear_prompt() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = await response.parse()
+ assert_matches_type(TuiClearPromptResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_execute_command(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.execute_command(
+ command="command",
+ )
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_execute_command_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.execute_command(
+ command="command",
+ directory="directory",
+ )
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_execute_command(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.tui.with_raw_response.execute_command(
+ command="command",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = await response.parse()
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_execute_command(self, async_client: AsyncOpencode) -> None:
+ async with async_client.tui.with_streaming_response.execute_command(
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = await response.parse()
+ assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_open_help(self, async_client: AsyncOpencode) -> None:
tui = await async_client.tui.open_help()
assert_matches_type(TuiOpenHelpResponse, tui, path=["response"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_open_help_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.open_help(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenHelpResponse, tui, path=["response"])
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_open_help(self, async_client: AsyncOpencode) -> None:
@@ -146,3 +540,195 @@ async def test_streaming_response_open_help(self, async_client: AsyncOpencode) -
assert_matches_type(TuiOpenHelpResponse, tui, path=["response"])
assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_open_models(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.open_models()
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_open_models_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.open_models(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_open_models(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.tui.with_raw_response.open_models()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = await response.parse()
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_open_models(self, async_client: AsyncOpencode) -> None:
+ async with async_client.tui.with_streaming_response.open_models() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = await response.parse()
+ assert_matches_type(TuiOpenModelsResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_open_sessions(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.open_sessions()
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_open_sessions_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.open_sessions(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_open_sessions(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.tui.with_raw_response.open_sessions()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = await response.parse()
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_open_sessions(self, async_client: AsyncOpencode) -> None:
+ async with async_client.tui.with_streaming_response.open_sessions() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = await response.parse()
+ assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_open_themes(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.open_themes()
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_open_themes_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.open_themes(
+ directory="directory",
+ )
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_open_themes(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.tui.with_raw_response.open_themes()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = await response.parse()
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_open_themes(self, async_client: AsyncOpencode) -> None:
+ async with async_client.tui.with_streaming_response.open_themes() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = await response.parse()
+ assert_matches_type(TuiOpenThemesResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_show_toast(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.show_toast(
+ message="message",
+ variant="info",
+ )
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_show_toast_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.show_toast(
+ message="message",
+ variant="info",
+ directory="directory",
+ title="title",
+ )
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_show_toast(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.tui.with_raw_response.show_toast(
+ message="message",
+ variant="info",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = await response.parse()
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_show_toast(self, async_client: AsyncOpencode) -> None:
+ async with async_client.tui.with_streaming_response.show_toast(
+ message="message",
+ variant="info",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = await response.parse()
+ assert_matches_type(TuiShowToastResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_submit_prompt(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.submit_prompt()
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_submit_prompt_with_all_params(self, async_client: AsyncOpencode) -> None:
+ tui = await async_client.tui.submit_prompt(
+ directory="directory",
+ )
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_submit_prompt(self, async_client: AsyncOpencode) -> None:
+ response = await async_client.tui.with_raw_response.submit_prompt()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tui = await response.parse()
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_submit_prompt(self, async_client: AsyncOpencode) -> None:
+ async with async_client.tui.with_streaming_response.submit_prompt() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tui = await response.parse()
+ assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/test_client.py b/tests/test_client.py
index f8225c7..7f7092c 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -6,13 +6,10 @@
import os
import sys
import json
-import time
import asyncio
import inspect
-import subprocess
import tracemalloc
from typing import Any, Union, cast
-from textwrap import dedent
from unittest import mock
from typing_extensions import Literal
@@ -23,6 +20,7 @@
from opencode_ai import Opencode, AsyncOpencode, APIResponseValidationError
from opencode_ai._types import Omit
+from opencode_ai._utils import asyncify
from opencode_ai._models import BaseModel, FinalRequestOptions
from opencode_ai._streaming import Stream, AsyncStream
from opencode_ai._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError
@@ -30,8 +28,10 @@
DEFAULT_TIMEOUT,
HTTPX_DEFAULT_TIMEOUT,
BaseClient,
+ OtherPlatform,
DefaultHttpxClient,
DefaultAsyncHttpxClient,
+ get_platform,
make_request_options,
)
@@ -59,47 +59,45 @@ def _get_open_connections(client: Opencode | AsyncOpencode) -> int:
class TestOpencode:
- client = Opencode(base_url=base_url, _strict_response_validation=True)
-
@pytest.mark.respx(base_url=base_url)
- def test_raw_response(self, respx_mock: MockRouter) -> None:
+ def test_raw_response(self, respx_mock: MockRouter, client: Opencode) -> None:
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = self.client.post("/foo", cast_to=httpx.Response)
+ response = client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
@pytest.mark.respx(base_url=base_url)
- def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None:
+ def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Opencode) -> None:
respx_mock.post("/foo").mock(
return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
- response = self.client.post("/foo", cast_to=httpx.Response)
+ response = client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
- def test_copy(self) -> None:
- copied = self.client.copy()
- assert id(copied) != id(self.client)
+ def test_copy(self, client: Opencode) -> None:
+ copied = client.copy()
+ assert id(copied) != id(client)
- def test_copy_default_options(self) -> None:
+ def test_copy_default_options(self, client: Opencode) -> None:
# options that have a default are overridden correctly
- copied = self.client.copy(max_retries=7)
+ copied = client.copy(max_retries=7)
assert copied.max_retries == 7
- assert self.client.max_retries == 2
+ assert client.max_retries == 2
copied2 = copied.copy(max_retries=6)
assert copied2.max_retries == 6
assert copied.max_retries == 7
# timeout
- assert isinstance(self.client.timeout, httpx.Timeout)
- copied = self.client.copy(timeout=None)
+ assert isinstance(client.timeout, httpx.Timeout)
+ copied = client.copy(timeout=None)
assert copied.timeout is None
- assert isinstance(self.client.timeout, httpx.Timeout)
+ assert isinstance(client.timeout, httpx.Timeout)
def test_copy_default_headers(self) -> None:
client = Opencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"})
@@ -132,6 +130,7 @@ def test_copy_default_headers(self) -> None:
match="`default_headers` and `set_default_headers` arguments are mutually exclusive",
):
client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"})
+ client.close()
def test_copy_default_query(self) -> None:
client = Opencode(base_url=base_url, _strict_response_validation=True, default_query={"foo": "bar"})
@@ -167,13 +166,15 @@ def test_copy_default_query(self) -> None:
):
client.copy(set_default_query={}, default_query={"foo": "Bar"})
- def test_copy_signature(self) -> None:
+ client.close()
+
+ def test_copy_signature(self, client: Opencode) -> None:
# ensure the same parameters that can be passed to the client are defined in the `.copy()` method
init_signature = inspect.signature(
# mypy doesn't like that we access the `__init__` property.
- self.client.__init__, # type: ignore[misc]
+ client.__init__, # type: ignore[misc]
)
- copy_signature = inspect.signature(self.client.copy)
+ copy_signature = inspect.signature(client.copy)
exclude_params = {"transport", "proxies", "_strict_response_validation"}
for name in init_signature.parameters.keys():
@@ -184,12 +185,12 @@ def test_copy_signature(self) -> None:
assert copy_param is not None, f"copy() signature is missing the {name} param"
@pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12")
- def test_copy_build_request(self) -> None:
+ def test_copy_build_request(self, client: Opencode) -> None:
options = FinalRequestOptions(method="get", url="/foo")
def build_request(options: FinalRequestOptions) -> None:
- client = self.client.copy()
- client._build_request(options)
+ client_copy = client.copy()
+ client_copy._build_request(options)
# ensure that the machinery is warmed up before tracing starts.
build_request(options)
@@ -246,14 +247,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
print(frame)
raise AssertionError()
- def test_request_timeout(self) -> None:
- request = self.client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ def test_request_timeout(self, client: Opencode) -> None:
+ request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
- request = self.client._build_request(
- FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))
- )
+ request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(100.0)
@@ -264,6 +263,8 @@ def test_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(0)
+ client.close()
+
def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
with httpx.Client(timeout=None) as http_client:
@@ -273,6 +274,8 @@ def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(None)
+ client.close()
+
# no timeout given to the httpx client should not use the httpx default
with httpx.Client() as http_client:
client = Opencode(base_url=base_url, _strict_response_validation=True, http_client=http_client)
@@ -281,6 +284,8 @@ def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
+ client.close()
+
# explicitly passing the default timeout currently results in it being ignored
with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
client = Opencode(base_url=base_url, _strict_response_validation=True, http_client=http_client)
@@ -289,18 +294,20 @@ def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT # our default
+ client.close()
+
async def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
async with httpx.AsyncClient() as http_client:
Opencode(base_url=base_url, _strict_response_validation=True, http_client=cast(Any, http_client))
def test_default_headers_option(self) -> None:
- client = Opencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"})
- request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ test_client = Opencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"})
+ request = test_client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "bar"
assert request.headers.get("x-stainless-lang") == "python"
- client2 = Opencode(
+ test_client2 = Opencode(
base_url=base_url,
_strict_response_validation=True,
default_headers={
@@ -308,10 +315,13 @@ def test_default_headers_option(self) -> None:
"X-Stainless-Lang": "my-overriding-header",
},
)
- request = client2._build_request(FinalRequestOptions(method="get", url="/foo"))
+ request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "stainless"
assert request.headers.get("x-stainless-lang") == "my-overriding-header"
+ test_client.close()
+ test_client2.close()
+
def test_default_query_option(self) -> None:
client = Opencode(base_url=base_url, _strict_response_validation=True, default_query={"query_param": "bar"})
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
@@ -328,8 +338,10 @@ def test_default_query_option(self) -> None:
url = httpx.URL(request.url)
assert dict(url.params) == {"foo": "baz", "query_param": "overridden"}
- def test_request_extra_json(self) -> None:
- request = self.client._build_request(
+ client.close()
+
+ def test_request_extra_json(self, client: Opencode) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -340,7 +352,7 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": False}
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -351,7 +363,7 @@ def test_request_extra_json(self) -> None:
assert data == {"baz": False}
# `extra_json` takes priority over `json_data` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -362,8 +374,8 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": None}
- def test_request_extra_headers(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_headers(self, client: Opencode) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -373,7 +385,7 @@ def test_request_extra_headers(self) -> None:
assert request.headers.get("X-Foo") == "Foo"
# `extra_headers` takes priority over `default_headers` when keys clash
- request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request(
+ request = client.with_options(default_headers={"X-Bar": "true"})._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -384,8 +396,8 @@ def test_request_extra_headers(self) -> None:
)
assert request.headers.get("X-Bar") == "false"
- def test_request_extra_query(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_query(self, client: Opencode) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -398,7 +410,7 @@ def test_request_extra_query(self) -> None:
assert params == {"my_query_param": "Foo"}
# if both `query` and `extra_query` are given, they are merged
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -412,7 +424,7 @@ def test_request_extra_query(self) -> None:
assert params == {"bar": "1", "foo": "2"}
# `extra_query` takes priority over `query` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -455,7 +467,7 @@ def test_multipart_repeating_array(self, client: Opencode) -> None:
]
@pytest.mark.respx(base_url=base_url)
- def test_basic_union_response(self, respx_mock: MockRouter) -> None:
+ def test_basic_union_response(self, respx_mock: MockRouter, client: Opencode) -> None:
class Model1(BaseModel):
name: str
@@ -464,12 +476,12 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
@pytest.mark.respx(base_url=base_url)
- def test_union_response_different_types(self, respx_mock: MockRouter) -> None:
+ def test_union_response_different_types(self, respx_mock: MockRouter, client: Opencode) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -480,18 +492,18 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1}))
- response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model1)
assert response.foo == 1
@pytest.mark.respx(base_url=base_url)
- def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None:
+ def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Opencode) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
"""
@@ -507,7 +519,7 @@ class Model(BaseModel):
)
)
- response = self.client.get("/foo", cast_to=Model)
+ response = client.get("/foo", cast_to=Model)
assert isinstance(response, Model)
assert response.foo == 2
@@ -519,6 +531,8 @@ def test_base_url_setter(self) -> None:
assert client.base_url == "https://example.com/from_setter/"
+ client.close()
+
def test_base_url_env(self) -> None:
with update_env(OPENCODE_BASE_URL="http://localhost:5000/from/env"):
client = Opencode(_strict_response_validation=True)
@@ -545,6 +559,7 @@ def test_base_url_trailing_slash(self, client: Opencode) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ client.close()
@pytest.mark.parametrize(
"client",
@@ -567,6 +582,7 @@ def test_base_url_no_trailing_slash(self, client: Opencode) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ client.close()
@pytest.mark.parametrize(
"client",
@@ -589,35 +605,36 @@ def test_absolute_request_url(self, client: Opencode) -> None:
),
)
assert request.url == "https://myapi.com/foo"
+ client.close()
def test_copied_client_does_not_close_http(self) -> None:
- client = Opencode(base_url=base_url, _strict_response_validation=True)
- assert not client.is_closed()
+ test_client = Opencode(base_url=base_url, _strict_response_validation=True)
+ assert not test_client.is_closed()
- copied = client.copy()
- assert copied is not client
+ copied = test_client.copy()
+ assert copied is not test_client
del copied
- assert not client.is_closed()
+ assert not test_client.is_closed()
def test_client_context_manager(self) -> None:
- client = Opencode(base_url=base_url, _strict_response_validation=True)
- with client as c2:
- assert c2 is client
+ test_client = Opencode(base_url=base_url, _strict_response_validation=True)
+ with test_client as c2:
+ assert c2 is test_client
assert not c2.is_closed()
- assert not client.is_closed()
- assert client.is_closed()
+ assert not test_client.is_closed()
+ assert test_client.is_closed()
@pytest.mark.respx(base_url=base_url)
- def test_client_response_validation_error(self, respx_mock: MockRouter) -> None:
+ def test_client_response_validation_error(self, respx_mock: MockRouter, client: Opencode) -> None:
class Model(BaseModel):
foo: str
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}}))
with pytest.raises(APIResponseValidationError) as exc:
- self.client.get("/foo", cast_to=Model)
+ client.get("/foo", cast_to=Model)
assert isinstance(exc.value.__cause__, ValidationError)
@@ -626,13 +643,13 @@ def test_client_max_retries_validation(self) -> None:
Opencode(base_url=base_url, _strict_response_validation=True, max_retries=cast(Any, None))
@pytest.mark.respx(base_url=base_url)
- def test_default_stream_cls(self, respx_mock: MockRouter) -> None:
+ def test_default_stream_cls(self, respx_mock: MockRouter, client: Opencode) -> None:
class Model(BaseModel):
name: str
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- stream = self.client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model])
+ stream = client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model])
assert isinstance(stream, Stream)
stream.response.close()
@@ -648,11 +665,14 @@ class Model(BaseModel):
with pytest.raises(APIResponseValidationError):
strict_client.get("/foo", cast_to=Model)
- client = Opencode(base_url=base_url, _strict_response_validation=False)
+ non_strict_client = Opencode(base_url=base_url, _strict_response_validation=False)
- response = client.get("/foo", cast_to=Model)
+ response = non_strict_client.get("/foo", cast_to=Model)
assert isinstance(response, str) # type: ignore[unreachable]
+ strict_client.close()
+ non_strict_client.close()
+
@pytest.mark.parametrize(
"remaining_retries,retry_after,timeout",
[
@@ -675,9 +695,9 @@ class Model(BaseModel):
],
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
- def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None:
- client = Opencode(base_url=base_url, _strict_response_validation=True)
-
+ def test_parse_retry_after_header(
+ self, remaining_retries: int, retry_after: str, timeout: float, client: Opencode
+ ) -> None:
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
@@ -691,7 +711,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien
with pytest.raises(APITimeoutError):
client.session.with_streaming_response.list().__enter__()
- assert _get_open_connections(self.client) == 0
+ assert _get_open_connections(client) == 0
@mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
@@ -700,7 +720,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client
with pytest.raises(APIStatusError):
client.session.with_streaming_response.list().__enter__()
- assert _get_open_connections(self.client) == 0
+ assert _get_open_connections(client) == 0
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@@ -802,79 +822,73 @@ def test_default_client_creation(self) -> None:
)
@pytest.mark.respx(base_url=base_url)
- def test_follow_redirects(self, respx_mock: MockRouter) -> None:
+ def test_follow_redirects(self, respx_mock: MockRouter, client: Opencode) -> None:
# Test that the default follow_redirects=True allows following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
)
respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
- response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
assert response.status_code == 200
assert response.json() == {"status": "ok"}
@pytest.mark.respx(base_url=base_url)
- def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
+ def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Opencode) -> None:
# Test that follow_redirects=False prevents following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
)
with pytest.raises(APIStatusError) as exc_info:
- self.client.post(
- "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
- )
+ client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response)
assert exc_info.value.response.status_code == 302
assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
class TestAsyncOpencode:
- client = AsyncOpencode(base_url=base_url, _strict_response_validation=True)
-
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
- async def test_raw_response(self, respx_mock: MockRouter) -> None:
+ async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = await self.client.post("/foo", cast_to=httpx.Response)
+ response = await async_client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
- async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None:
+ async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
respx_mock.post("/foo").mock(
return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
- response = await self.client.post("/foo", cast_to=httpx.Response)
+ response = await async_client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
- def test_copy(self) -> None:
- copied = self.client.copy()
- assert id(copied) != id(self.client)
+ def test_copy(self, async_client: AsyncOpencode) -> None:
+ copied = async_client.copy()
+ assert id(copied) != id(async_client)
- def test_copy_default_options(self) -> None:
+ def test_copy_default_options(self, async_client: AsyncOpencode) -> None:
# options that have a default are overridden correctly
- copied = self.client.copy(max_retries=7)
+ copied = async_client.copy(max_retries=7)
assert copied.max_retries == 7
- assert self.client.max_retries == 2
+ assert async_client.max_retries == 2
copied2 = copied.copy(max_retries=6)
assert copied2.max_retries == 6
assert copied.max_retries == 7
# timeout
- assert isinstance(self.client.timeout, httpx.Timeout)
- copied = self.client.copy(timeout=None)
+ assert isinstance(async_client.timeout, httpx.Timeout)
+ copied = async_client.copy(timeout=None)
assert copied.timeout is None
- assert isinstance(self.client.timeout, httpx.Timeout)
+ assert isinstance(async_client.timeout, httpx.Timeout)
- def test_copy_default_headers(self) -> None:
+ async def test_copy_default_headers(self) -> None:
client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"})
assert client.default_headers["X-Foo"] == "bar"
@@ -905,8 +919,9 @@ def test_copy_default_headers(self) -> None:
match="`default_headers` and `set_default_headers` arguments are mutually exclusive",
):
client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"})
+ await client.close()
- def test_copy_default_query(self) -> None:
+ async def test_copy_default_query(self) -> None:
client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, default_query={"foo": "bar"})
assert _get_params(client)["foo"] == "bar"
@@ -940,13 +955,15 @@ def test_copy_default_query(self) -> None:
):
client.copy(set_default_query={}, default_query={"foo": "Bar"})
- def test_copy_signature(self) -> None:
+ await client.close()
+
+ def test_copy_signature(self, async_client: AsyncOpencode) -> None:
# ensure the same parameters that can be passed to the client are defined in the `.copy()` method
init_signature = inspect.signature(
# mypy doesn't like that we access the `__init__` property.
- self.client.__init__, # type: ignore[misc]
+ async_client.__init__, # type: ignore[misc]
)
- copy_signature = inspect.signature(self.client.copy)
+ copy_signature = inspect.signature(async_client.copy)
exclude_params = {"transport", "proxies", "_strict_response_validation"}
for name in init_signature.parameters.keys():
@@ -957,12 +974,12 @@ def test_copy_signature(self) -> None:
assert copy_param is not None, f"copy() signature is missing the {name} param"
@pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12")
- def test_copy_build_request(self) -> None:
+ def test_copy_build_request(self, async_client: AsyncOpencode) -> None:
options = FinalRequestOptions(method="get", url="/foo")
def build_request(options: FinalRequestOptions) -> None:
- client = self.client.copy()
- client._build_request(options)
+ client_copy = async_client.copy()
+ client_copy._build_request(options)
# ensure that the machinery is warmed up before tracing starts.
build_request(options)
@@ -1019,12 +1036,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
print(frame)
raise AssertionError()
- async def test_request_timeout(self) -> None:
- request = self.client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ async def test_request_timeout(self, async_client: AsyncOpencode) -> None:
+ request = async_client._build_request(FinalRequestOptions(method="get", url="/foo"))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
- request = self.client._build_request(
+ request = async_client._build_request(
FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))
)
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
@@ -1037,6 +1054,8 @@ async def test_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(0)
+ await client.close()
+
async def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
async with httpx.AsyncClient(timeout=None) as http_client:
@@ -1046,6 +1065,8 @@ async def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(None)
+ await client.close()
+
# no timeout given to the httpx client should not use the httpx default
async with httpx.AsyncClient() as http_client:
client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, http_client=http_client)
@@ -1054,6 +1075,8 @@ async def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
+ await client.close()
+
# explicitly passing the default timeout currently results in it being ignored
async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, http_client=http_client)
@@ -1062,18 +1085,22 @@ async def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT # our default
+ await client.close()
+
def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
with httpx.Client() as http_client:
AsyncOpencode(base_url=base_url, _strict_response_validation=True, http_client=cast(Any, http_client))
- def test_default_headers_option(self) -> None:
- client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"})
- request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ async def test_default_headers_option(self) -> None:
+ test_client = AsyncOpencode(
+ base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
+ )
+ request = test_client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "bar"
assert request.headers.get("x-stainless-lang") == "python"
- client2 = AsyncOpencode(
+ test_client2 = AsyncOpencode(
base_url=base_url,
_strict_response_validation=True,
default_headers={
@@ -1081,11 +1108,14 @@ def test_default_headers_option(self) -> None:
"X-Stainless-Lang": "my-overriding-header",
},
)
- request = client2._build_request(FinalRequestOptions(method="get", url="/foo"))
+ request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "stainless"
assert request.headers.get("x-stainless-lang") == "my-overriding-header"
- def test_default_query_option(self) -> None:
+ await test_client.close()
+ await test_client2.close()
+
+ async def test_default_query_option(self) -> None:
client = AsyncOpencode(
base_url=base_url, _strict_response_validation=True, default_query={"query_param": "bar"}
)
@@ -1103,8 +1133,10 @@ def test_default_query_option(self) -> None:
url = httpx.URL(request.url)
assert dict(url.params) == {"foo": "baz", "query_param": "overridden"}
- def test_request_extra_json(self) -> None:
- request = self.client._build_request(
+ await client.close()
+
+ def test_request_extra_json(self, client: Opencode) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1115,7 +1147,7 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": False}
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1126,7 +1158,7 @@ def test_request_extra_json(self) -> None:
assert data == {"baz": False}
# `extra_json` takes priority over `json_data` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1137,8 +1169,8 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": None}
- def test_request_extra_headers(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_headers(self, client: Opencode) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1148,7 +1180,7 @@ def test_request_extra_headers(self) -> None:
assert request.headers.get("X-Foo") == "Foo"
# `extra_headers` takes priority over `default_headers` when keys clash
- request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request(
+ request = client.with_options(default_headers={"X-Bar": "true"})._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1159,8 +1191,8 @@ def test_request_extra_headers(self) -> None:
)
assert request.headers.get("X-Bar") == "false"
- def test_request_extra_query(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_query(self, client: Opencode) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1173,7 +1205,7 @@ def test_request_extra_query(self) -> None:
assert params == {"my_query_param": "Foo"}
# if both `query` and `extra_query` are given, they are merged
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1187,7 +1219,7 @@ def test_request_extra_query(self) -> None:
assert params == {"bar": "1", "foo": "2"}
# `extra_query` takes priority over `query` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1230,7 +1262,7 @@ def test_multipart_repeating_array(self, async_client: AsyncOpencode) -> None:
]
@pytest.mark.respx(base_url=base_url)
- async def test_basic_union_response(self, respx_mock: MockRouter) -> None:
+ async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
class Model1(BaseModel):
name: str
@@ -1239,12 +1271,12 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
@pytest.mark.respx(base_url=base_url)
- async def test_union_response_different_types(self, respx_mock: MockRouter) -> None:
+ async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -1255,18 +1287,20 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1}))
- response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model1)
assert response.foo == 1
@pytest.mark.respx(base_url=base_url)
- async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None:
+ async def test_non_application_json_content_type_for_json_data(
+ self, respx_mock: MockRouter, async_client: AsyncOpencode
+ ) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
"""
@@ -1282,11 +1316,11 @@ class Model(BaseModel):
)
)
- response = await self.client.get("/foo", cast_to=Model)
+ response = await async_client.get("/foo", cast_to=Model)
assert isinstance(response, Model)
assert response.foo == 2
- def test_base_url_setter(self) -> None:
+ async def test_base_url_setter(self) -> None:
client = AsyncOpencode(base_url="https://example.com/from_init", _strict_response_validation=True)
assert client.base_url == "https://example.com/from_init/"
@@ -1294,7 +1328,9 @@ def test_base_url_setter(self) -> None:
assert client.base_url == "https://example.com/from_setter/"
- def test_base_url_env(self) -> None:
+ await client.close()
+
+ async def test_base_url_env(self) -> None:
with update_env(OPENCODE_BASE_URL="http://localhost:5000/from/env"):
client = AsyncOpencode(_strict_response_validation=True)
assert client.base_url == "http://localhost:5000/from/env/"
@@ -1311,7 +1347,7 @@ def test_base_url_env(self) -> None:
],
ids=["standard", "custom http client"],
)
- def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None:
+ async def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1320,6 +1356,7 @@ def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ await client.close()
@pytest.mark.parametrize(
"client",
@@ -1333,7 +1370,7 @@ def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None:
],
ids=["standard", "custom http client"],
)
- def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None:
+ async def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1342,6 +1379,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ await client.close()
@pytest.mark.parametrize(
"client",
@@ -1355,7 +1393,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None:
],
ids=["standard", "custom http client"],
)
- def test_absolute_request_url(self, client: AsyncOpencode) -> None:
+ async def test_absolute_request_url(self, client: AsyncOpencode) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1364,37 +1402,37 @@ def test_absolute_request_url(self, client: AsyncOpencode) -> None:
),
)
assert request.url == "https://myapi.com/foo"
+ await client.close()
async def test_copied_client_does_not_close_http(self) -> None:
- client = AsyncOpencode(base_url=base_url, _strict_response_validation=True)
- assert not client.is_closed()
+ test_client = AsyncOpencode(base_url=base_url, _strict_response_validation=True)
+ assert not test_client.is_closed()
- copied = client.copy()
- assert copied is not client
+ copied = test_client.copy()
+ assert copied is not test_client
del copied
await asyncio.sleep(0.2)
- assert not client.is_closed()
+ assert not test_client.is_closed()
async def test_client_context_manager(self) -> None:
- client = AsyncOpencode(base_url=base_url, _strict_response_validation=True)
- async with client as c2:
- assert c2 is client
+ test_client = AsyncOpencode(base_url=base_url, _strict_response_validation=True)
+ async with test_client as c2:
+ assert c2 is test_client
assert not c2.is_closed()
- assert not client.is_closed()
- assert client.is_closed()
+ assert not test_client.is_closed()
+ assert test_client.is_closed()
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
- async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None:
+ async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
class Model(BaseModel):
foo: str
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}}))
with pytest.raises(APIResponseValidationError) as exc:
- await self.client.get("/foo", cast_to=Model)
+ await async_client.get("/foo", cast_to=Model)
assert isinstance(exc.value.__cause__, ValidationError)
@@ -1403,19 +1441,17 @@ async def test_client_max_retries_validation(self) -> None:
AsyncOpencode(base_url=base_url, _strict_response_validation=True, max_retries=cast(Any, None))
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
- async def test_default_stream_cls(self, respx_mock: MockRouter) -> None:
+ async def test_default_stream_cls(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
class Model(BaseModel):
name: str
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- stream = await self.client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model])
+ stream = await async_client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model])
assert isinstance(stream, AsyncStream)
await stream.response.aclose()
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
class Model(BaseModel):
name: str
@@ -1427,11 +1463,14 @@ class Model(BaseModel):
with pytest.raises(APIResponseValidationError):
await strict_client.get("/foo", cast_to=Model)
- client = AsyncOpencode(base_url=base_url, _strict_response_validation=False)
+ non_strict_client = AsyncOpencode(base_url=base_url, _strict_response_validation=False)
- response = await client.get("/foo", cast_to=Model)
+ response = await non_strict_client.get("/foo", cast_to=Model)
assert isinstance(response, str) # type: ignore[unreachable]
+ await strict_client.close()
+ await non_strict_client.close()
+
@pytest.mark.parametrize(
"remaining_retries,retry_after,timeout",
[
@@ -1454,13 +1493,12 @@ class Model(BaseModel):
],
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
- @pytest.mark.asyncio
- async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None:
- client = AsyncOpencode(base_url=base_url, _strict_response_validation=True)
-
+ async def test_parse_retry_after_header(
+ self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncOpencode
+ ) -> None:
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
- calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
+ calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers)
assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
@mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@@ -1473,7 +1511,7 @@ async def test_retrying_timeout_errors_doesnt_leak(
with pytest.raises(APITimeoutError):
await async_client.session.with_streaming_response.list().__aenter__()
- assert _get_open_connections(self.client) == 0
+ assert _get_open_connections(async_client) == 0
@mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
@@ -1484,12 +1522,11 @@ async def test_retrying_status_errors_doesnt_leak(
with pytest.raises(APIStatusError):
await async_client.session.with_streaming_response.list().__aenter__()
- assert _get_open_connections(self.client) == 0
+ assert _get_open_connections(async_client) == 0
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
async def test_retries_taken(
self,
@@ -1521,7 +1558,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
async def test_omit_retry_count_header(
self, async_client: AsyncOpencode, failures_before_success: int, respx_mock: MockRouter
) -> None:
@@ -1545,7 +1581,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
async def test_overwrite_retry_count_header(
self, async_client: AsyncOpencode, failures_before_success: int, respx_mock: MockRouter
) -> None:
@@ -1566,50 +1601,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
- def test_get_platform(self) -> None:
- # A previous implementation of asyncify could leave threads unterminated when
- # used with nest_asyncio.
- #
- # Since nest_asyncio.apply() is global and cannot be un-applied, this
- # test is run in a separate process to avoid affecting other tests.
- test_code = dedent("""
- import asyncio
- import nest_asyncio
- import threading
-
- from opencode_ai._utils import asyncify
- from opencode_ai._base_client import get_platform
-
- async def test_main() -> None:
- result = await asyncify(get_platform)()
- print(result)
- for thread in threading.enumerate():
- print(thread.name)
-
- nest_asyncio.apply()
- asyncio.run(test_main())
- """)
- with subprocess.Popen(
- [sys.executable, "-c", test_code],
- text=True,
- ) as process:
- timeout = 10 # seconds
-
- start_time = time.monotonic()
- while True:
- return_code = process.poll()
- if return_code is not None:
- if return_code != 0:
- raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code")
-
- # success
- break
-
- if time.monotonic() - start_time > timeout:
- process.kill()
- raise AssertionError("calling get_platform using asyncify resulted in a hung process")
-
- time.sleep(0.1)
+ async def test_get_platform(self) -> None:
+ platform = await asyncify(get_platform)()
+ assert isinstance(platform, (str, OtherPlatform))
async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
# Test that the proxy environment variables are set correctly
@@ -1634,26 +1628,26 @@ async def test_default_client_creation(self) -> None:
)
@pytest.mark.respx(base_url=base_url)
- async def test_follow_redirects(self, respx_mock: MockRouter) -> None:
+ async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
# Test that the default follow_redirects=True allows following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
)
respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
- response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
assert response.status_code == 200
assert response.json() == {"status": "ok"}
@pytest.mark.respx(base_url=base_url)
- async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
+ async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None:
# Test that follow_redirects=False prevents following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
)
with pytest.raises(APIStatusError) as exc_info:
- await self.client.post(
+ await async_client.post(
"/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
)
diff --git a/tests/test_models.py b/tests/test_models.py
index 94fa22d..f5c177f 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -8,8 +8,8 @@
from pydantic import Field
from opencode_ai._utils import PropertyInfo
-from opencode_ai._compat import PYDANTIC_V2, parse_obj, model_dump, model_json
-from opencode_ai._models import BaseModel, construct_type
+from opencode_ai._compat import PYDANTIC_V1, parse_obj, model_dump, model_json
+from opencode_ai._models import DISCRIMINATOR_CACHE, BaseModel, construct_type
class BasicModel(BaseModel):
@@ -294,12 +294,12 @@ class Model(BaseModel):
assert cast(bool, m.foo) is True
m = Model.construct(foo={"name": 3})
- if PYDANTIC_V2:
- assert isinstance(m.foo, Submodel1)
- assert m.foo.name == 3 # type: ignore
- else:
+ if PYDANTIC_V1:
assert isinstance(m.foo, Submodel2)
assert m.foo.name == "3"
+ else:
+ assert isinstance(m.foo, Submodel1)
+ assert m.foo.name == 3 # type: ignore
def test_list_of_unions() -> None:
@@ -426,10 +426,10 @@ class Model(BaseModel):
expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc)
- if PYDANTIC_V2:
- expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}'
- else:
+ if PYDANTIC_V1:
expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}'
+ else:
+ expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}'
model = Model.construct(created_at="2019-12-27T18:11:19.117Z")
assert model.created_at == expected
@@ -531,7 +531,7 @@ class Model2(BaseModel):
assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)}
assert m4.to_dict(mode="json") == {"created_at": time_str}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"):
m.to_dict(warnings=False)
@@ -556,7 +556,7 @@ class Model(BaseModel):
assert m3.model_dump() == {"foo": None}
assert m3.model_dump(exclude_none=True) == {}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"):
m.model_dump(round_trip=True)
@@ -580,10 +580,10 @@ class Model(BaseModel):
assert json.loads(m.to_json()) == {"FOO": "hello"}
assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"}
- if PYDANTIC_V2:
- assert m.to_json(indent=None) == '{"FOO":"hello"}'
- else:
+ if PYDANTIC_V1:
assert m.to_json(indent=None) == '{"FOO": "hello"}'
+ else:
+ assert m.to_json(indent=None) == '{"FOO":"hello"}'
m2 = Model()
assert json.loads(m2.to_json()) == {}
@@ -595,7 +595,7 @@ class Model(BaseModel):
assert json.loads(m3.to_json()) == {"FOO": None}
assert json.loads(m3.to_json(exclude_none=True)) == {}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"):
m.to_json(warnings=False)
@@ -622,7 +622,7 @@ class Model(BaseModel):
assert json.loads(m3.model_dump_json()) == {"foo": None}
assert json.loads(m3.model_dump_json(exclude_none=True)) == {}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"):
m.model_dump_json(round_trip=True)
@@ -679,12 +679,12 @@ class B(BaseModel):
)
assert isinstance(m, A)
assert m.type == "a"
- if PYDANTIC_V2:
- assert m.data == 100 # type: ignore[comparison-overlap]
- else:
+ if PYDANTIC_V1:
# pydantic v1 automatically converts inputs to strings
# if the expected type is a str
assert m.data == "100"
+ else:
+ assert m.data == 100 # type: ignore[comparison-overlap]
def test_discriminated_unions_unknown_variant() -> None:
@@ -768,12 +768,12 @@ class B(BaseModel):
)
assert isinstance(m, A)
assert m.foo_type == "a"
- if PYDANTIC_V2:
- assert m.data == 100 # type: ignore[comparison-overlap]
- else:
+ if PYDANTIC_V1:
# pydantic v1 automatically converts inputs to strings
# if the expected type is a str
assert m.data == "100"
+ else:
+ assert m.data == 100 # type: ignore[comparison-overlap]
def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None:
@@ -809,7 +809,7 @@ class B(BaseModel):
UnionType = cast(Any, Union[A, B])
- assert not hasattr(UnionType, "__discriminator__")
+ assert not DISCRIMINATOR_CACHE.get(UnionType)
m = construct_type(
value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")])
@@ -818,7 +818,7 @@ class B(BaseModel):
assert m.type == "b"
assert m.data == "foo" # type: ignore[comparison-overlap]
- discriminator = UnionType.__discriminator__
+ discriminator = DISCRIMINATOR_CACHE.get(UnionType)
assert discriminator is not None
m = construct_type(
@@ -830,10 +830,10 @@ class B(BaseModel):
# if the discriminator details object stays the same between invocations then
# we hit the cache
- assert UnionType.__discriminator__ is discriminator
+ assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator
-@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1")
+@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1")
def test_type_alias_type() -> None:
Alias = TypeAliasType("Alias", str) # pyright: ignore
@@ -849,7 +849,7 @@ class Model(BaseModel):
assert m.union == "bar"
-@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1")
+@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1")
def test_field_named_cls() -> None:
class Model(BaseModel):
cls: str
@@ -936,7 +936,7 @@ class Type2(BaseModel):
assert isinstance(model.value, InnerType2)
-@pytest.mark.skipif(not PYDANTIC_V2, reason="this is only supported in pydantic v2 for now")
+@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now")
def test_extra_properties() -> None:
class Item(BaseModel):
prop: int
diff --git a/tests/test_transform.py b/tests/test_transform.py
index 14a6ae1..5adf5f4 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -8,14 +8,14 @@
import pytest
-from opencode_ai._types import NOT_GIVEN, Base64FileInput
+from opencode_ai._types import Base64FileInput, omit, not_given
from opencode_ai._utils import (
PropertyInfo,
transform as _transform,
parse_datetime,
async_transform as _async_transform,
)
-from opencode_ai._compat import PYDANTIC_V2
+from opencode_ai._compat import PYDANTIC_V1
from opencode_ai._models import BaseModel
_T = TypeVar("_T")
@@ -189,7 +189,7 @@ class DateModel(BaseModel):
@pytest.mark.asyncio
async def test_iso8601_format(use_async: bool) -> None:
dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
- tz = "Z" if PYDANTIC_V2 else "+00:00"
+ tz = "+00:00" if PYDANTIC_V1 else "Z"
assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap]
@@ -297,11 +297,11 @@ async def test_pydantic_unknown_field(use_async: bool) -> None:
@pytest.mark.asyncio
async def test_pydantic_mismatched_types(use_async: bool) -> None:
model = MyModel.construct(foo=True)
- if PYDANTIC_V2:
+ if PYDANTIC_V1:
+ params = await transform(model, Any, use_async)
+ else:
with pytest.warns(UserWarning):
params = await transform(model, Any, use_async)
- else:
- params = await transform(model, Any, use_async)
assert cast(Any, params) == {"foo": True}
@@ -309,11 +309,11 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None:
@pytest.mark.asyncio
async def test_pydantic_mismatched_object_type(use_async: bool) -> None:
model = MyModel.construct(foo=MyModel.construct(hello="world"))
- if PYDANTIC_V2:
+ if PYDANTIC_V1:
+ params = await transform(model, Any, use_async)
+ else:
with pytest.warns(UserWarning):
params = await transform(model, Any, use_async)
- else:
- params = await transform(model, Any, use_async)
assert cast(Any, params) == {"foo": {"hello": "world"}}
@@ -450,4 +450,11 @@ async def test_transform_skipping(use_async: bool) -> None:
@pytest.mark.asyncio
async def test_strips_notgiven(use_async: bool) -> None:
assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"}
- assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {}
+ assert await transform({"foo_bar": not_given}, Foo1, use_async) == {}
+
+
+@parametrize
+@pytest.mark.asyncio
+async def test_strips_omit(use_async: bool) -> None:
+ assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"}
+ assert await transform({"foo_bar": omit}, Foo1, use_async) == {}
diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py
new file mode 100644
index 0000000..7363e95
--- /dev/null
+++ b/tests/test_utils/test_datetime_parse.py
@@ -0,0 +1,110 @@
+"""
+Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py
+with modifications so it works without pydantic v1 imports.
+"""
+
+from typing import Type, Union
+from datetime import date, datetime, timezone, timedelta
+
+import pytest
+
+from opencode_ai._utils import parse_date, parse_datetime
+
+
+def create_tz(minutes: int) -> timezone:
+ return timezone(timedelta(minutes=minutes))
+
+
+@pytest.mark.parametrize(
+ "value,result",
+ [
+ # Valid inputs
+ ("1494012444.883309", date(2017, 5, 5)),
+ (b"1494012444.883309", date(2017, 5, 5)),
+ (1_494_012_444.883_309, date(2017, 5, 5)),
+ ("1494012444", date(2017, 5, 5)),
+ (1_494_012_444, date(2017, 5, 5)),
+ (0, date(1970, 1, 1)),
+ ("2012-04-23", date(2012, 4, 23)),
+ (b"2012-04-23", date(2012, 4, 23)),
+ ("2012-4-9", date(2012, 4, 9)),
+ (date(2012, 4, 9), date(2012, 4, 9)),
+ (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)),
+ # Invalid inputs
+ ("x20120423", ValueError),
+ ("2012-04-56", ValueError),
+ (19_999_999_999, date(2603, 10, 11)), # just before watershed
+ (20_000_000_001, date(1970, 8, 20)), # just after watershed
+ (1_549_316_052, date(2019, 2, 4)), # nowish in s
+ (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms
+ (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs
+ (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns
+ ("infinity", date(9999, 12, 31)),
+ ("inf", date(9999, 12, 31)),
+ (float("inf"), date(9999, 12, 31)),
+ ("infinity ", date(9999, 12, 31)),
+ (int("1" + "0" * 100), date(9999, 12, 31)),
+ (1e1000, date(9999, 12, 31)),
+ ("-infinity", date(1, 1, 1)),
+ ("-inf", date(1, 1, 1)),
+ ("nan", ValueError),
+ ],
+)
+def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None:
+ if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance]
+ with pytest.raises(result):
+ parse_date(value)
+ else:
+ assert parse_date(value) == result
+
+
+@pytest.mark.parametrize(
+ "value,result",
+ [
+ # Valid inputs
+ # values in seconds
+ ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)),
+ (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)),
+ ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ # values in ms
+ ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)),
+ ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)),
+ (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)),
+ ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)),
+ ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)),
+ ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))),
+ ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))),
+ ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))),
+ ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))),
+ (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))),
+ (datetime(2017, 5, 5), datetime(2017, 5, 5)),
+ (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)),
+ # Invalid inputs
+ ("x20120423091500", ValueError),
+ ("2012-04-56T09:15:90", ValueError),
+ ("2012-04-23T11:05:00-25:00", ValueError),
+ (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed
+ (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed
+ (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s
+ (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms
+ (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs
+ (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns
+ ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ ("-infinity", datetime(1, 1, 1, 0, 0)),
+ ("-inf", datetime(1, 1, 1, 0, 0)),
+ ("nan", ValueError),
+ ],
+)
+def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None:
+ if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance]
+ with pytest.raises(result):
+ parse_datetime(value)
+ else:
+ assert parse_datetime(value) == result
diff --git a/tests/utils.py b/tests/utils.py
index 5842949..bf5bda6 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -4,7 +4,7 @@
import inspect
import traceback
import contextlib
-from typing import Any, TypeVar, Iterator, cast
+from typing import Any, TypeVar, Iterator, Sequence, cast
from datetime import date, datetime
from typing_extensions import Literal, get_args, get_origin, assert_type
@@ -15,10 +15,11 @@
is_list_type,
is_union_type,
extract_type_arg,
+ is_sequence_type,
is_annotated_type,
is_type_alias_type,
)
-from opencode_ai._compat import PYDANTIC_V2, field_outer_type, get_model_fields
+from opencode_ai._compat import PYDANTIC_V1, field_outer_type, get_model_fields
from opencode_ai._models import BaseModel
BaseModelT = TypeVar("BaseModelT", bound=BaseModel)
@@ -27,12 +28,12 @@
def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool:
for name, field in get_model_fields(model).items():
field_value = getattr(value, name)
- if PYDANTIC_V2:
- allow_none = False
- else:
+ if PYDANTIC_V1:
# in v1 nullability was structured differently
# https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields
allow_none = getattr(field, "allow_none", False)
+ else:
+ allow_none = False
assert_matches_type(
field_outer_type(field),
@@ -71,6 +72,13 @@ def assert_matches_type(
if is_list_type(type_):
return _assert_list_type(type_, value)
+ if is_sequence_type(type_):
+ assert isinstance(value, Sequence)
+ inner_type = get_args(type_)[0]
+ for entry in value: # type: ignore
+ assert_type(inner_type, entry) # type: ignore
+ return
+
if origin == str:
assert isinstance(value, str)
elif origin == int: