From 255c18cf777f5c76084f7cb2abf706763d5d4d5a Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Wed, 4 Nov 2026 22:49:50 -0500 Subject: [PATCH 01/14] feat(desktop-gpui): native desktop client that starts and connects to the server A native client lets us replace Electron without shipping Chromium or a Node runtime: the app launches the server with the user's Node, the same bootstrap handshake Electron uses, so no pairing is needed. Signed-off-by: Yordis Prieto --- .gitignore | 1 + apps/desktop-gpui/Cargo.lock | 6755 +++++++++++++++++++++++++++ apps/desktop-gpui/Cargo.toml | 36 + apps/desktop-gpui/README.md | 47 + apps/desktop-gpui/package.json | 10 + apps/desktop-gpui/src/auth.rs | 183 + apps/desktop-gpui/src/config.rs | 86 + apps/desktop-gpui/src/connection.rs | 170 + apps/desktop-gpui/src/main.rs | 93 + apps/desktop-gpui/src/server.rs | 404 ++ apps/desktop-gpui/src/status.rs | 30 + package.json | 1 + scripts/dev-runner.test.ts | 51 + scripts/dev-runner.ts | 64 +- 14 files changed, 7921 insertions(+), 10 deletions(-) create mode 100644 apps/desktop-gpui/Cargo.lock create mode 100644 apps/desktop-gpui/Cargo.toml create mode 100644 apps/desktop-gpui/README.md create mode 100644 apps/desktop-gpui/package.json create mode 100644 apps/desktop-gpui/src/auth.rs create mode 100644 apps/desktop-gpui/src/config.rs create mode 100644 apps/desktop-gpui/src/connection.rs create mode 100644 apps/desktop-gpui/src/main.rs create mode 100644 apps/desktop-gpui/src/server.rs create mode 100644 apps/desktop-gpui/src/status.rs diff --git a/.gitignore b/.gitignore index 05ac43c32f1e4..dc9af11ed7a14 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ artifacts/mobile-e2e/ artifacts/mobile-behavior/ .github/pr-assets/ native/**/target/ +apps/desktop-gpui/target node_modules/ .alchemy/ *.log diff --git a/apps/desktop-gpui/Cargo.lock b/apps/desktop-gpui/Cargo.lock new file mode 100644 index 0000000000000..7691fef2bbe94 --- /dev/null +++ b/apps/desktop-gpui/Cargo.lock @@ -0,0 +1,6755 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "accesskit" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320" +dependencies = [ + "enumn", + "uuid", +] + +[[package]] +name = "accesskit_consumer" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_macos" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "objc2 0.5.3", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "accesskit_windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106c2b961215864d1c2e703ee63269c25c4e80a577ffb2c1017b9c17dcdf83a1" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "static_assertions", + "windows 0.62.2", + "windows-core 0.62.2", +] + +[[package]] +name = "addr2line" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e567177890eb1617b1f774005b66b26b2377afd138a2ca37aae7d8f0c81429d4" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", + "zeroize", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arbitrary" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "ashpd" +version = "0.13.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8421aaa9644a5faf26735f258b669b15f063313ef8f8e2bdb28912a1a6f111" +dependencies = [ + "enumflags2", + "futures-util", + "getrandom 0.4.3", + "serde", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f426a31126c735126297bc5ea7a521263c9faff4f63ca415267ee625651c4f37" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0876700b0d007ad25d5508865a4259c3af9af6d296bf663a0d586d0bc3369f6f" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8abc12baad266b1c8cec146854c195b5864b4221d4b2ca7296a7ae82d9e451" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey 0.1.1", + "rayon", + "thiserror", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "aws-lc-rs" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048006790404103c488e6c5616b3e7e702a71292aadda08e8f7698a9e0d2eb53" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bfbe32ac36b3c428696ce315464181bc4029d689fd58e3f2a4e58c252242f52" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "backtrace" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f4220217f3288537b83684130f42fad43320b1c3b805f1910d683d52ded24f" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide 0.9.1", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "bit-set" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d87354e4229f54a44f7bf2435906a4656dba36026ab6eaca629a2c436a691c" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5727b15fa97d4f4fee0a3b7c3d550ed0269f54329207b86388de918604e31269" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.3", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2 0.6.5", +] + +[[package]] +name = "blocking" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "built" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "calloop" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e" +dependencies = [ + "bitflags 2.13.2", + "polling", + "rustix", + "tracing", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cbindgen" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" +dependencies = [ + "heck 0.4.1", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.119", + "tempfile", + "toml 0.8.23", +] + +[[package]] +name = "cc" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375989decec1a0fa424442bed520fa12ede43a48475ce8c7417d2c69b32b5f6b" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core 0.10.1", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link 0.2.1", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", + "zeroize", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "collections" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "gpui_util", + "indexmap", + "rustc-hash 2.1.3", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + +[[package]] +name = "cosmic-text" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" +dependencies = [ + "bitflags 2.13.2", + "fontdb", + "harfrust", + "linebender_resource_handle", + "log", + "rangemap", + "rustc-hash 2.1.3", + "self_cell", + "skrifa 0.40.0", + "smol_str", + "swash", + "sys-locale", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f97d3416d5337d15c4b0d78c8c91df922ca143f57ec20e2d1c188333047ef04" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d" +dependencies = [ + "link-section", + "linktime-proc-macro", +] + +[[package]] +name = "ctutils" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261af4dc7a06ea8a527a8717d8cbba3417530d6ea14e1edb3de6b67806be474f" +dependencies = [ + "cmov", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "deranged" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + +[[package]] +name = "derive_refineable" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.5", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dwrote" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + +[[package]] +name = "embed-resource" +version = "3.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6168487b5e7eb2673e6efe1200b1848016e436a6ca0b8031bf04bd9d493a249f" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.2.0+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.74.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711fe42c9964295e01ee3fba3f9fe0e1d24b98886950d68efe81b1c76e21adf3" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide 0.8.9", + "num-complex", + "pulp", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide 0.9.1", + "zlib-rs", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.9", +] + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "font-types" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "font-types" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835ae9d781464a79026cde387f9cbc650c9a4909d7cf7e658b127e3c35d2f35d" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1033caf0b349c518623b5396bfb2cf0bddf44f0306d543a250e5743297aafd10" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "263218b0ba2b0715c3370fb04674f5f8aa331594b521c94ff0a8e4a8472d1386" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror", + "windows 0.62.2", +] + +[[package]] +name = "gpui" +version = "0.2.2" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "accesskit", + "anyhow", + "async-channel", + "async-task", + "backtrace", + "bitflags 2.13.2", + "chrono", + "collections", + "derive_more", + "embed-resource", + "etagere", + "futures", + "getrandom 0.3.4", + "gpui_macros", + "gpui_shared_string", + "gpui_util", + "heapless", + "http_client", + "icu_segmenter", + "image", + "inventory", + "itertools", + "libc", + "log", + "lyon", + "num_cpus", + "objc2-core-foundation", + "objc2-core-video", + "parking", + "parking_lot", + "pin-project", + "pollster", + "profiling", + "rand 0.9.5", + "raw-window-handle", + "refineable", + "regex", + "resvg", + "scheduler", + "schemars", + "seahash", + "serde", + "serde_json", + "slotmap", + "smallvec", + "spin 0.10.1", + "strum", + "sum_tree", + "taffy", + "thiserror", + "ttf-parser", + "unicode-segmentation", + "url", + "usvg", + "uuid", + "waker-fn", + "windows 0.62.2", + "zed-scap", +] + +[[package]] +name = "gpui_apple" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "anyhow", + "async-task", + "block2 0.6.2", + "cbindgen", + "collections", + "core-foundation", + "core-foundation-sys", + "core-graphics", + "core-text", + "dispatch2", + "etagere", + "futures", + "gpui", + "gpui_util", + "image", + "libc", + "log", + "mach2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-text", + "objc2-core-video", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", + "objc2-quartz-core 0.3.2", + "objc2-security", + "parking_lot", + "pathfinder_geometry", + "smallvec", + "thiserror", + "zed-font-kit", +] + +[[package]] +name = "gpui_ios" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "anyhow", + "block2 0.6.2", + "futures", + "gpui", + "gpui_apple", + "log", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", + "objc2-ui-kit", + "parking_lot", + "raw-window-handle", + "uuid", +] + +[[package]] +name = "gpui_linux" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "accesskit", + "anyhow", + "bytemuck", + "calloop", + "collections", + "futures", + "gpui", + "gpui_util", + "http_client", + "libc", + "log", + "notify-rust", + "oo7", + "parking_lot", + "raw-window-handle", + "smallvec", + "smol", + "strum", + "url", + "uuid", +] + +[[package]] +name = "gpui_macos" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "accesskit", + "accesskit_macos", + "anyhow", + "block2 0.6.2", + "collections", + "core-foundation", + "core-foundation-sys", + "core-graphics", + "ctor", + "dispatch2", + "foreign-types", + "futures", + "gpui", + "gpui_apple", + "gpui_util", + "image", + "itertools", + "log", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-core-graphics", + "objc2-core-video", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", + "parking_lot", + "raw-window-handle", + "scheduler", + "semver", + "smallvec", + "strum", + "uuid", +] + +[[package]] +name = "gpui_macros" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpui_platform" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "anyhow", + "console_error_panic_hook", + "gpui", + "gpui_ios", + "gpui_linux", + "gpui_macos", + "gpui_web", + "gpui_windows", +] + +[[package]] +name = "gpui_shared_string" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "schemars", + "serde", + "smol_str", +] + +[[package]] +name = "gpui_util" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "anyhow", + "log", + "which", +] + +[[package]] +name = "gpui_web" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "anyhow", + "async-channel", + "console_error_panic_hook", + "futures", + "gpui", + "gpui_wgpu", + "http_client", + "js-sys", + "log", + "parking_lot", + "raw-window-handle", + "scheduler", + "smallvec", + "unicode-properties", + "unicode-script", + "unicode-segmentation", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm_thread", + "web-sys", + "web-time", +] + +[[package]] +name = "gpui_wgpu" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "anyhow", + "bytemuck", + "collections", + "cosmic-text", + "etagere", + "gpui", + "gpui_util", + "itertools", + "log", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "swash", + "unicode-bidi", + "unicode-segmentation", + "web-sys", + "wgpu", +] + +[[package]] +name = "gpui_windows" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "accesskit", + "accesskit_windows", + "anyhow", + "collections", + "dunce", + "etagere", + "futures", + "gpui", + "gpui_util", + "image", + "itertools", + "log", + "parking_lot", + "rand 0.9.5", + "raw-window-handle", + "scheduler", + "smallvec", + "uuid", + "windows 0.62.2", + "windows-core 0.62.2", + "windows-numerics 0.3.1", + "windows-registry", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "harfrust" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "core_maths", + "read-fonts 0.37.0", + "smallvec", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http_client" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "anyhow", + "bytes", + "futures", + "http", + "http-body", + "parking_lot", + "serde", + "serde_json", + "serde_urlencoded", + "url", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" +dependencies = [ + "atomic-waker", + "bytes", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" +dependencies = [ + "displaydoc", + "litemap", + "serde", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locale_fallback" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406e9ae5b95d8aa37db6293405931b766a6172d1848daee22d70eb44aa78a43c" +dependencies = [ + "icu_locale_core", + "icu_locale_fallback_data", + "icu_provider", + "potential_utf", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locale_fallback_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3632ebbd0c5c3637ba1a40bdf8288f2cb9d2268e73441a69d5fc73d7cfbaa87c" + +[[package]] +name = "icu_normalizer" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" + +[[package]] +name = "icu_properties" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" + +[[package]] +name = "icu_provider" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" +dependencies = [ + "displaydoc", + "icu_locale_core", + "serde", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_segmenter" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b195dc605f81aa181bec9b3bd14e26cc71b903e4644f6af578ca9772c7a710" +dependencies = [ + "icu_collections", + "icu_locale_fallback", + "icu_provider", + "icu_segmenter_data", + "potential_utf", + "smallvec", + "utf8_iter", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_segmenter_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913a7d294ad056474749152107309fdee3ec17d2482d1ab70fdc61a81c64b9a3" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png 0.18.1", + "qoi", + "ravif", + "rayon", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" + +[[package]] +name = "imgref" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e44b0a4eaa4c82f441d50a963f2d5f05a787240aeee097597033e72accfd22f" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "inventory" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6928282826c822ad91bf1c9a1cb90a30ba1c26770749929b4656cd6be829cd7c" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kurbo" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" +dependencies = [ + "arrayvec", + "euclid", + "polycool", + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb" + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libc" +version = "0.2.191" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" +dependencies = [ + "libc", +] + +[[package]] +name = "linebender_resource_handle" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" + +[[package]] +name = "link-section" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c29a617ce3df32c08497bdc1ab6e2376e0b17948ac166a2fbe5977c5954cd9" + +[[package]] +name = "linktime-proc-macro" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e57c38c1e860fd37c604281cdfb1dd2216977fd76a50f85ba2f388ef3219616" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "litrs" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + +[[package]] +name = "lyon" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0578bdecb7d6d88987b8b2b1e3a4e2f81df9d0ece1078623324a567904e7b7" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a6b9d4e8b48acd531fbf1241ad9b270df6e60b6b92bdea1469f4f40899d805" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82dd26aa4dc0b5b70b9115de59824842bcb43cf4a28e1b1e6d0ad148c93f93e" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c463f9c428b7fc5ec885dcd39ce4aa61e29111d0e33483f6f98c74e89d8621e" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd4872650e70dc907dd4f7ebc449d7d657a0d2658e423b2e724970870bbbf75b" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + +[[package]] +name = "mac-notification-sys" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca" +dependencies = [ + "cc", + "log", + "objc2 0.6.5", + "objc2-foundation 0.3.2", + "time", + "uuid", +] + +[[package]] +name = "mach2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + +[[package]] +name = "macro_rules_attribute" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ae8f6d608c795738406608304d30a2dfbdc8e58e44f7ba43236da5208ded3c" +dependencies = [ + "macro_rules_attribute-proc_macro", + "pastey 0.2.3", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc04a4c58212d57930a24bf47d3fa87485264a3a054e9c10e042eb373573ad3c" + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memmap2" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0ca1155a9ecc25aa12e8313a119f32edf0255fea2f44839c4ac0a12b2ac157" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "naga" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.13.2", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.17.1", + "indexmap", + "libm", + "log", + "naga-types", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "naga-types" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "hashbrown 0.17.1", + "indexmap", + "rustc-hash 1.1.0", + "thiserror", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "notify-rust" +version = "4.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4587364a9a0074333429b3df75a30a205340c56a536ca3eb6ca0e59b87bbf8af" +dependencies = [ + "futures-lite", + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f9a86e097b0d187ad0e65667c2f58b9254671e86e7dbb78036b16692eae099" +dependencies = [ + "libm", + "num-integer", + "num-iter", + "num-traits", + "once_cell", + "rand 0.9.5", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "bytemuck", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d5490aaf8f1d7cf7688dfa9b0ce07900e168852c45cd2c03f534dfd27cfd0b" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.3", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-uniform-type-identifiers", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "dispatch2", + "libc", + "objc2 0.6.5", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.2", + "dispatch2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", + "objc2-metal 0.3.2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.3", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "libc", + "objc2 0.6.5", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "dispatch2", + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", +] + +[[package]] +name = "objc2-security" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" +dependencies = [ + "objc2-core-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7902ac02859fc1f7045f8b598c63f1ae0cc7efeaa06a9bc9f3d9a3c955974fa4" +dependencies = [ + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "object" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd229a0361b9d0d4396176e02d65897f487eebeab7caa6d443855ee152ca0b9c" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oo7" +version = "0.6.0" +source = "git+https://github.com/zed-industries/oo7?rev=731c1215edb6905876c6137ab300428c7b8d7c13#731c1215edb6905876c6137ab300428c7b8d7c13" +dependencies = [ + "aes", + "ashpd", + "async-fs", + "async-io", + "async-lock", + "blocking", + "cbc", + "cipher", + "digest 0.10.7", + "endi", + "futures-lite", + "futures-util", + "getrandom 0.4.3", + "hkdf", + "hmac 0.12.1", + "md-5", + "num", + "num-bigint-dig", + "pbkdf2", + "serde", + "serde_bytes", + "sha2 0.10.9", + "subtle", + "zbus", + "zbus_macros", + "zeroize", + "zvariant", +] + +[[package]] +name = "openssl-probe" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e036b411754132fd6b74e94b74e9a13c5cf606fa93d1885c35860d3060820b1" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "5.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f59006ce1e204c30956638535b74d317bca99477b9356dfaec54c37f6f600ab" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "polycool" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "serde_core", + "writeable", + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.18+spec-1.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pulp" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046aa45b989642ec2e4717c8e72d677b13edd831a4d3b6cf37d9a3e54912496a" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "paste", + "pulp-wasm-simd-flag", + "raw-cpuid", + "reborrow", + "version_check", +] + +[[package]] +name = "pulp-wasm-simd-flag" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8f70e07b9c3962945a74e59ca1c511bba65b6419468acc217c457d93f3c740" + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.3", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.3", + "rand_pcg", + "ring", + "rustc-hash 2.1.3", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + +[[package]] +name = "rangemap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a611d15b50743feb4c76b7d03edcb0e64f399c26961e4efe6975bc398be6aa3d" + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.5", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" +dependencies = [ + "bytemuck", + "core_maths", + "font-types 0.11.3", +] + +[[package]] +name = "read-fonts" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" +dependencies = [ + "bytemuck", + "font-types 0.12.7", + "once_cell", +] + +[[package]] +name = "reborrow" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "redox_users" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" +dependencies = [ + "libredox", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "refineable" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "derive_refineable", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "reqwest" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "resvg" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" +dependencies = [ + "gif", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", + "zune-jpeg", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "roxmltree" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da7f59328b09405b181fee1018925fad50f157afb43c0c0f5c522c256dd5907" +dependencies = [ + "memchr", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa" +dependencies = [ + "bitflags 2.13.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a073f5dc7469f984c52ad2752b63b0807745133b6de880b7b64c1ac4c48aec4" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + +[[package]] +name = "rustls-webpki" +version = "0.103.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "rustybuzz" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb23c424d8ee841bb4d813677ca237e2a610067d594fec130f87d6093495ab5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scheduler" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "async-task", + "backtrace", + "chrono", + "flume", + "futures", + "parking_lot", + "rand 0.9.5", + "wasm_thread", + "web-time", +] + +[[package]] +name = "schemars" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab508826f74a77ca9d5aba6ff19b522583ee3eaf28a19384ff3d0e5835fadf6e" +dependencies = [ + "dyn-clone", + "indexmap", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c3a92094fa7d61aa124645844facb6b554dfc797136d0f5fd1f890e2bffc69" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 3.0.9", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_derive_internals" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "foldhash", + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" + +[[package]] +name = "skrifa" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" +dependencies = [ + "bytemuck", + "read-fonts 0.37.0", +] + +[[package]] +name = "skrifa" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" +dependencies = [ + "bytemuck", + "read-fonts 0.41.0", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" + +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + +[[package]] +name = "smol_str" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb7109d23f319619d4cc0de5464613c8b7b1a09db2774a58101459cf6be2f10" +dependencies = [ + "serde_core", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spin" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spirv" +version = "0.4.0+sdk-1.4.341.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "sum_tree" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "heapless", + "rayon", + "tracing", + "ztracing", +] + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f12b12995e3638c70618c1facb1ce65302f6a6f706b675f07c58a5d7391120e" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "swash" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" +dependencies = [ + "skrifa 0.44.0", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "sysinfo" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + +[[package]] +name = "t3-desktop-gpui" +version = "0.1.0" +dependencies = [ + "async-channel", + "futures-util", + "getrandom 0.4.3", + "gpui", + "gpui_platform", + "hex", + "hmac 0.13.0", + "libc", + "reqwest", + "serde", + "serde_json", + "sha2 0.11.1", + "tokio", + "tokio-tungstenite", +] + +[[package]] +name = "taffy" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8480887e8803986d96a7173729565520d4bbb721ad6b7ad6941a22897207aa" +dependencies = [ + "serde", + "slotmap", + "smallvec", +] + +[[package]] +name = "tauri-winrt-notification" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37a6c354fd28fc9e322ed9bd47e3959576dad28c9d58ea1cf888cce1c7ccb36" +dependencies = [ + "thiserror", + "windows 0.62.2", + "windows-version", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png 0.17.16", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinystr" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17a073bfed563fa236697a068031408a93cd9522e08abf9933ead3e73411bd71" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "1.2.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ffcdf8f50436dbbfd675c1fe1b8e0854f42b7defea8584bdebe1aee900e561" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.2", + "toml_datetime 1.1.2+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.5", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + +[[package]] +name = "toml_edit" +version = "0.25.18+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec6e78bd70db9420d8d212fa9dc97f116448a1ceff5d00eabb991d407e21396" +dependencies = [ + "indexmap", + "toml_datetime 1.1.2+spec-1.1.0", + "toml_parser", + "winnow 1.0.5", +] + +[[package]] +name = "toml_parser" +version = "1.1.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" +dependencies = [ + "winnow 1.0.5", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.2.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009dbb7700f11c55ef83aebbe4c0e9cf1f1ac0844bd8d6f00dc38f302026e13a" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.2", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + +[[package]] +name = "tungstenite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48ac77174b19c110a50ab2128b24215ac9cb40e0e12e093fb602d175c569d22" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.10.3", + "sha1", + "thiserror", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" + +[[package]] +name = "unicode-ccc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa04c955db05429d464c74d495c1ee677e1866e783a4c3747f9c0cef7756cabf" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] +name = "unicode-width" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca718471876992059f251e33aafc334346ada08042b7661dd1fe313a9bced557" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "usvg" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.21.2", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "utf8_iter" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3bd9d3e571c26fcce9d033b87ea5ab061b8d2ffe9b7a5675d810d68f03edf0" +dependencies = [ + "utf_types", +] + +[[package]] +name = "utf_types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3b425286980a367278453f3b6d28cdcb134b574e5a35824a6ff5eebad50bb6" + +[[package]] +name = "uuid" +version = "1.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "sha1_smol", + "wasm-bindgen", +] + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c" +dependencies = [ + "cfg-if", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e14e0327cb5368ad60ccb22d04a9827a693c95a926d8d125e9fa39e0baec501" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm_thread" +version = "0.3.3" +source = "git+https://github.com/zed-industries/wasm_thread?rev=0cf96c7708dfb97ccf3da50347e25edcf75d6937#0cf96c7708dfb97ccf3da50347e25edcf75d6937" +dependencies = [ + "futures", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d179596ff5ec0f6de4db64d54011bde6289cc49603d3a6e4ce3f329207ab88" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "arrayvec", + "bitflags 2.13.2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.17.1", + "js-sys", + "log", + "naga", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-sync", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags 2.13.2", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.17.1", + "indexmap", + "log", + "naga", + "naga-types", + "num-traits", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "scopeguard", + "smallvec", + "thiserror", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-linux-android-bsd", + "wgpu-core-deps-wasm", + "wgpu-core-deps-windows", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-sync", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-linux-android-bsd" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-wasm" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.13.2", + "block2 0.6.2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "hashbrown 0.17.1", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "naga", + "naga-types", + "ndk-sys", + "objc2 0.6.5", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", + "objc2-quartz-core 0.3.2", + "ordered-float", + "profiling", + "range-alloc", + "raw-window-handle", + "raw-window-metal", + "renderdoc-sys", + "smallvec", + "static_assertions", + "thiserror", + "wasm-bindgen", + "wayland-sys", + "web-sys", + "wgpu-naga-bridge", + "wgpu-sync", + "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", + "windows-result 0.4.1", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "naga", + "wgpu-types", +] + +[[package]] +name = "wgpu-sync" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "cfg-if", + "lock_api", + "once_cell", + "parking_lot", + "portable-atomic", + "portable-atomic-util", +] + +[[package]] +name = "wgpu-types" +version = "30.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "js-sys", + "log", + "macro_rules_attribute", + "naga-types", + "raw-window-handle", + "static_assertions", + "web-sys", +] + +[[package]] +name = "which" +version = "8.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae2f2b2b816647a1cab1acc91f5bd20812d53cb344382635ec2181940c8034f" +dependencies = [ + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-capture" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" +dependencies = [ + "parking_lot", + "rayon", + "thiserror", + "windows 0.61.3", + "windows-future 0.2.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3b93199df0ab874cdf0c6a94f0a0dc3423fe41f1cafb62b8bd7b1deb277aef" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "xcb" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c2ad15e0e922856ee89afe862b8992334bbe7953adad56cd1199358cb30566" +dependencies = [ + "bitflags 2.13.2", + "libc", + "quick-xml", + "x11", +] + +[[package]] +name = "xml-rs" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + +[[package]] +name = "yazi" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac366ea532fa8ee6a992cec9dea525b1dc284e69702f36c2ab5fbebadc81ec86" + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8b8abf912b9a29ff112e1671c97c33636903d13a69712037190e6805af4f76" +dependencies = [ + "dlib", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.5", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 3.0.9", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" +dependencies = [ + "serde", + "winnow 1.0.5", + "zvariant", +] + +[[package]] +name = "zcheapstr" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939f186d9ecebe044315a5cb6d6f78568bbc21152adc954d5a0c2f58bdbc9920" +dependencies = [ + "serde", +] + +[[package]] +name = "zed-font-kit" +version = "0.14.1-zed" +source = "git+https://github.com/zed-industries/font-kit?rev=94b0f28166665e8fd2f53ff6d268a14955c82269#94b0f28166665e8fd2f53ff6d268a14955c82269" +dependencies = [ + "bitflags 2.13.2", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "zed-scap" +version = "0.0.8-zed" +source = "git+https://github.com/zed-industries/scap?rev=b5c86ec58dd4b6b7732bf6ac8be03d6b1f190a82#b5c86ec58dd4b6b7732bf6ac8be03d6b1f190a82" +dependencies = [ + "anyhow", + "log", + "rand 0.8.8", + "sysinfo", + "windows 0.61.3", + "windows-capture", + "x11", + "xcb", +] + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + +[[package]] +name = "zerocopy" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "zerovec" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" +dependencies = [ + "serde", + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "ztracing" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +dependencies = [ + "tracing", + "ztracing_macro", +] + +[[package]] +name = "ztracing_macro" +version = "0.1.0" +source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" + +[[package]] +name = "zune-core" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zvariant" +version = "5.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" +dependencies = [ + "endi", + "enumflags2", + "serde", + "serde_bytes", + "winnow 1.0.5", + "zcheapstr", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 3.0.9", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.9", + "winnow 1.0.5", +] diff --git a/apps/desktop-gpui/Cargo.toml b/apps/desktop-gpui/Cargo.toml new file mode 100644 index 0000000000000..cb23e47de817d --- /dev/null +++ b/apps/desktop-gpui/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "t3-desktop-gpui" +version = "0.1.0" +edition = "2024" +license = "MIT" +publish = false + +[dependencies] +async-channel = "2.5.0" +futures-util = "0.3.34" +getrandom = "0.4.3" +gpui = { git = "https://github.com/zed-industries/zed", rev = "c7bea343838f42b4c14151393e41f0bbe033c95e" } +gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "c7bea343838f42b4c14151393e41f0bbe033c95e", features = [ + "font-kit", +] } +hex = "0.4.3" +hmac = "0.13.0" +libc = "0.2.191" +reqwest = { version = "0.13.5", default-features = false, features = ["form", "json", "rustls"] } +serde = { version = "1.0.229", features = ["derive"] } +serde_json = "1.0.154" +sha2 = "0.11.1" +tokio = { version = "1.54.1", features = [ + "rt-multi-thread", + "process", + "time", + "sync", + "io-util", + "macros", +] } +tokio-tungstenite = "0.30.0" + +[profile.release] +codegen-units = 1 +lto = "thin" +strip = true diff --git a/apps/desktop-gpui/README.md b/apps/desktop-gpui/README.md new file mode 100644 index 0000000000000..f8d890596ecdf --- /dev/null +++ b/apps/desktop-gpui/README.md @@ -0,0 +1,47 @@ +# T3 Code desktop (GPUI) + +Native desktop client written in Rust with [GPUI](https://github.com/zed-industries/zed/tree/main/crates/gpui). It is meant to replace `apps/desktop` (Electron) over time and never embeds Node or Chromium. It starts the T3 server as a child process, authenticates without pairing, and opens the RPC WebSocket. + +Status: connection shell. The window shows the connection state ("Starting server...", "Connecting...", "Connected to ", or the error). There are no RPC methods or screens yet. + +## Run + +From the repo root: + +```sh +vp run dev:desktop-gpui +``` + +This works like `vp run dev:desktop`: one command. The dev runner derives the server port and the per-worktree `.t3` home (never `~/.t3`), then runs `cargo run` here. No Vite or web server starts. Pass `--home-dir` to use another T3 home. + +The app reads these variables, which the dev runner sets. Without them it shows "Not connected" and starts nothing. + +| Variable | Meaning | +| ---------------------------- | ------------------------------------------------------------------- | +| `T3CODE_GPUI_SERVER_PORT` | Loopback port for the server. | +| `T3CODE_GPUI_HOME` | T3 home passed to the server. | +| `T3CODE_GPUI_SERVER_COMMAND` | Program that starts the server (`node` in dev, `t3` once packaged). | +| `T3CODE_GPUI_SERVER_ENTRY` | Optional first argument, the server entry script in dev. | + +Other scripts: + +```sh +vp run --filter @t3tools/desktop-gpui build # cargo build --release +vp run --filter @t3tools/desktop-gpui test # cargo test +``` + +## How it connects + +1. Spawns ` [entry] --bootstrap-fd 3` and writes one JSON bootstrap line (`packages/contracts/src/desktopBootstrap.ts`) to a pipe dup'd onto fd 3, as Electron does. The secret is random per launch. +2. Polls `GET /.well-known/t3/environment` until the server is ready. +3. Exchanges the HMAC bootstrap token for a bearer session, mints a WebSocket ticket, and opens `/ws`. +4. Sends an RPC `Ping` every 5 seconds and reconnects with backoff after 15 seconds of silence. + +On quit the app sends SIGTERM to the server it spawned and SIGKILL after 5 seconds. If the app is killed, the server notices its parent changed within about 2 seconds and shuts itself down. + +## Notes + +- `gpui` and `gpui_platform` are pinned to one `zed-industries/zed` commit in `Cargo.toml`. Bump both together. +- macOS needs the Xcode Metal toolchain (`xcodebuild -downloadComponent MetalToolchain`) and the `font-kit` feature, which is enabled. +- Server spawning uses a unix pipe for fd 3, so it is macOS and Linux only for now. +- CI does not build this crate yet. GPUI needs a macOS runner or Linux system libraries (wayland, x11, xkbcommon, vulkan). diff --git a/apps/desktop-gpui/package.json b/apps/desktop-gpui/package.json new file mode 100644 index 0000000000000..eb7b4dec7dee4 --- /dev/null +++ b/apps/desktop-gpui/package.json @@ -0,0 +1,10 @@ +{ + "name": "@t3tools/desktop-gpui", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "cargo run --locked", + "build": "cargo build --locked --release", + "test": "cargo test --locked" + } +} diff --git a/apps/desktop-gpui/src/auth.rs b/apps/desktop-gpui/src/auth.rs new file mode 100644 index 0000000000000..618ab7ffca6f8 --- /dev/null +++ b/apps/desktop-gpui/src/auth.rs @@ -0,0 +1,183 @@ +//! Desktop bootstrap credential and the bearer/WebSocket-ticket exchange. +//! Mirrors packages/shared/src/desktopBootstrapToken.ts and +//! packages/client-runtime/src/authorization/remote.ts. + +use hmac::{Hmac, KeyInit, Mac}; +use serde::Deserialize; +use sha2::Sha256; +use std::fmt::Write; +use std::time::{SystemTime, UNIX_EPOCH}; + +const TOKEN_WINDOW_MS: u64 = 12 * 60 * 60 * 1000; +const GRANT_TYPE: &str = "urn:ietf:params:oauth:grant-type:token-exchange"; +const ACCESS_TOKEN_TYPE: &str = "urn:ietf:params:oauth:token-type:access_token"; +const BOOTSTRAP_TOKEN_TYPE: &str = "urn:t3:params:oauth:token-type:environment-bootstrap"; +const CLIENT_LABEL: &str = "T3 Code Desktop (GPUI)"; + +pub fn now_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|elapsed| elapsed.as_millis() as u64) + .unwrap_or(0) +} + +/// 256 random bits, hex encoded. Only this process and its server ever see it. +pub fn generate_secret() -> Result { + let mut bytes = [0u8; 32]; + getrandom::fill(&mut bytes).map_err(|error| format!("could not read random bytes: {error}"))?; + Ok(hex::encode(bytes)) +} + +/// HMAC-SHA256 keyed by the secret over `t3-desktop-bootstrap:`, hex encoded. +pub fn bootstrap_token(secret: &str, now_ms: u64) -> String { + let window = now_ms / TOKEN_WINDOW_MS; + let mut mac = + Hmac::::new_from_slice(secret.as_bytes()).expect("HMAC takes any key length"); + let mut message = String::new(); + write!(message, "t3-desktop-bootstrap:{window}").expect("writing to a String cannot fail"); + mac.update(message.as_bytes()); + hex::encode(mac.finalize().into_bytes()) +} + +/// Loopback only: an ambient HTTP(S)_PROXY must never capture traffic to the local server. +pub fn http_client() -> reqwest::Client { + reqwest::Client::builder() + .no_proxy() + .build() + .expect("the HTTP client configuration is static") +} + +#[derive(Debug)] +pub enum AuthError { + /// The server refused the credential, so a retry needs a fresh one. + Rejected(u16), + Failed(String), +} + +impl std::fmt::Display for AuthError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Rejected(status) => write!(f, "server rejected the credential (HTTP {status})"), + Self::Failed(message) => f.write_str(message), + } + } +} + +impl From for AuthError { + fn from(error: reqwest::Error) -> Self { + Self::Failed(error.without_url().to_string()) + } +} + +#[derive(Deserialize)] +struct AccessTokenResult { + access_token: String, +} + +#[derive(Deserialize)] +struct TicketResult { + ticket: String, +} + +/// RFC 8693 exchange of the desktop bootstrap token for a bearer session. +pub async fn exchange_bearer( + client: &reqwest::Client, + http_base: &str, + secret: &str, +) -> Result { + let credential = bootstrap_token(secret, now_ms()); + let response = client + .post(format!("{http_base}/oauth/token")) + .form(&[ + ("grant_type", GRANT_TYPE), + ("subject_token", credential.as_str()), + ("subject_token_type", BOOTSTRAP_TOKEN_TYPE), + ("requested_token_type", ACCESS_TOKEN_TYPE), + ("client_label", CLIENT_LABEL), + ("client_device_type", "desktop"), + ]) + .send() + .await?; + let status = response.status(); + if status.is_client_error() { + return Err(AuthError::Rejected(status.as_u16())); + } + let result: AccessTokenResult = response.error_for_status()?.json().await?; + Ok(result.access_token) +} + +/// Mints an RPC WebSocket ticket. A server without audience routes answers 404 +/// for `/:use`, and its bare-path tickets are valid for RPC. +pub async fn issue_ws_ticket( + client: &reqwest::Client, + http_base: &str, + bearer: &str, +) -> Result { + let mint = |path: &'static str| async move { + client + .post(format!("{http_base}{path}")) + .bearer_auth(bearer) + .send() + .await + }; + let mut response = mint("/api/auth/websocket-ticket/rpc").await?; + if response.status() == reqwest::StatusCode::NOT_FOUND { + response = mint("/api/auth/websocket-ticket").await?; + } + let status = response.status(); + if status == reqwest::StatusCode::UNAUTHORIZED || status == reqwest::StatusCode::FORBIDDEN { + return Err(AuthError::Rejected(status.as_u16())); + } + let result: TicketResult = response.error_for_status()?.json().await?; + Ok(result.ticket) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Computed with currentDesktopBootstrapToken from packages/shared. + #[test] + fn matches_the_typescript_token() { + let vectors = [ + ( + "test-secret", + 0, + "29cdd8a2256df92036e83f50c98bafcefa22fbf04e12d040cd53bd1680f62e4c", + ), + ( + "test-secret", + 1_700_000_000_000, + "fdcc803bed0e5fcbde01221220f9abcd496feaa9b06965a0761d6927d9b5c38e", + ), + ( + "another-secret-value", + 86_400_000, + "7e1f8f7f6a6ecc013ea23398711d2b7c762684f88e9f688f8bf4e5155da281ae", + ), + ]; + for (secret, now, expected) in vectors { + assert_eq!(bootstrap_token(secret, now), expected); + } + } + + #[test] + fn token_is_stable_inside_a_window_and_changes_across_it() { + let start = 5 * TOKEN_WINDOW_MS; + assert_eq!( + bootstrap_token("s", start), + bootstrap_token("s", start + TOKEN_WINDOW_MS - 1) + ); + assert_ne!( + bootstrap_token("s", start), + bootstrap_token("s", start + TOKEN_WINDOW_MS) + ); + } + + #[test] + fn secrets_are_random_hex() { + let first = generate_secret().expect("secret"); + assert_eq!(first.len(), 64); + assert_ne!(first, generate_secret().expect("secret")); + } +} diff --git a/apps/desktop-gpui/src/config.rs b/apps/desktop-gpui/src/config.rs new file mode 100644 index 0000000000000..1ff8afe8ce68a --- /dev/null +++ b/apps/desktop-gpui/src/config.rs @@ -0,0 +1,86 @@ +//! Launch settings handed over by the dev runner (or, later, a packaged launcher). + +pub const ENV_PORT: &str = "T3CODE_GPUI_SERVER_PORT"; +pub const ENV_HOME: &str = "T3CODE_GPUI_HOME"; +pub const ENV_COMMAND: &str = "T3CODE_GPUI_SERVER_COMMAND"; +pub const ENV_ENTRY: &str = "T3CODE_GPUI_SERVER_ENTRY"; + +/// How to start the server. The command is data: dev runs `node `, a +/// packaged build runs `t3` from PATH with no entry. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ServerCommand { + pub program: String, + pub args: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Config { + pub port: u16, + pub t3_home: String, + pub command: ServerCommand, +} + +impl Config { + pub fn http_base(&self) -> String { + format!("http://127.0.0.1:{}", self.port) + } + + /// `None` when the launcher did not ask this app to manage a server. + pub fn from_env() -> Option { + Self::from_lookup(|name| std::env::var(name).ok()) + } + + fn from_lookup(get: impl Fn(&str) -> Option) -> Option { + let non_empty = |name| get(name).filter(|value| !value.trim().is_empty()); + let port = non_empty(ENV_PORT)?.trim().parse().ok()?; + let t3_home = non_empty(ENV_HOME)?; + let program = non_empty(ENV_COMMAND)?; + let args = non_empty(ENV_ENTRY).into_iter().collect(); + Some(Self { + port, + t3_home, + command: ServerCommand { program, args }, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn lookup(pairs: &'static [(&'static str, &'static str)]) -> impl Fn(&str) -> Option { + move |name| { + pairs + .iter() + .find(|(key, _)| *key == name) + .map(|(_, value)| value.to_string()) + } + } + + #[test] + fn reads_all_settings() { + let config = Config::from_lookup(lookup(&[ + (ENV_PORT, "13773"), + (ENV_HOME, "/work/.t3"), + (ENV_COMMAND, "node"), + (ENV_ENTRY, "/work/apps/server/src/bin.ts"), + ])) + .expect("config"); + assert_eq!(config.port, 13773); + assert_eq!(config.command.args, vec!["/work/apps/server/src/bin.ts"]); + assert_eq!(config.http_base(), "http://127.0.0.1:13773"); + } + + #[test] + fn entry_is_optional_and_missing_values_disable_the_server() { + let config = Config::from_lookup(lookup(&[ + (ENV_PORT, "1"), + (ENV_HOME, "/h"), + (ENV_COMMAND, "t3"), + ])) + .expect("config"); + assert!(config.command.args.is_empty()); + assert_eq!(Config::from_lookup(lookup(&[(ENV_PORT, "1")])), None); + assert_eq!(Config::from_lookup(lookup(&[])), None); + } +} diff --git a/apps/desktop-gpui/src/connection.rs b/apps/desktop-gpui/src/connection.rs new file mode 100644 index 0000000000000..3751bb282cb1f --- /dev/null +++ b/apps/desktop-gpui/src/connection.rs @@ -0,0 +1,170 @@ +//! The authenticated RPC WebSocket: connect, keep alive, reconnect. + +use crate::auth::{self, AuthError}; +use crate::config::Config; +use crate::status::{Status, StatusSender, publish}; +use futures_util::{SinkExt, StreamExt}; +use std::time::Duration; +use tokio::time::{Instant, MissedTickBehavior, interval, sleep, sleep_until}; +use tokio_tungstenite::tungstenite::Message; + +/// Effect RPC ping envelope (RpcMessage.constPing) in the JSON serialization. +const PING_FRAME: &str = r#"{"_tag":"Ping"}"#; +const PING_INTERVAL: Duration = Duration::from_secs(5); +// Pings go out every 5 s and a busy server stops answering them, so allow 15 s +// without any frame (packages/client-runtime/src/rpc/protocol.ts PING_TIMEOUT). +const PING_TIMEOUT: Duration = Duration::from_secs(15); +const ORCHESTRATION_PROTOCOL_VERSION: &str = "2"; + +/// Doubling delay between attempts, capped (Electron restarts the backend the same way). +pub struct Backoff { + next: Duration, +} + +impl Backoff { + const INITIAL: Duration = Duration::from_millis(500); + const MAX: Duration = Duration::from_secs(10); + + pub fn new() -> Self { + Self { + next: Self::INITIAL, + } + } + + pub fn reset(&mut self) { + self.next = Self::INITIAL; + } + + pub fn next_delay(&mut self) -> Duration { + let delay = self.next; + self.next = (self.next * 2).min(Self::MAX); + delay + } +} + +fn socket_url(port: u16, ticket: &str) -> Result { + reqwest::Url::parse_with_params( + &format!("ws://127.0.0.1:{port}/ws"), + [ + ("orchestrationProtocol", ORCHESTRATION_PROTOCOL_VERSION), + ("wsTicket", ticket), + ("clientSurface", "desktop"), + ("clientDeviceType", "desktop"), + ("clientAppVersion", env!("CARGO_PKG_VERSION")), + ("connectionMethod", "direct"), + ], + ) + .map_err(|error| error.to_string()) +} + +/// Connects and stays connected until cancelled, reporting through `status`. +pub async fn run(config: &Config, secret: &str, label: &str, status: &StatusSender) { + let client = auth::http_client(); + let http_base = config.http_base(); + let mut bearer: Option = None; + let mut backoff = Backoff::new(); + loop { + publish(status, Status::Connecting); + let failure = match connect(&client, config, &http_base, secret, &mut bearer).await { + Ok(socket) => { + publish(status, Status::Connected(label.to_owned())); + backoff.reset(); + serve(socket).await + } + Err(message) => message, + }; + publish(status, Status::Failed(format!("Reconnecting: {failure}"))); + sleep(backoff.next_delay()).await; + } +} + +type Socket = + tokio_tungstenite::WebSocketStream>; + +async fn connect( + client: &reqwest::Client, + config: &Config, + http_base: &str, + secret: &str, + bearer: &mut Option, +) -> Result { + let token = match bearer { + Some(token) => token.clone(), + None => { + let token = auth::exchange_bearer(client, http_base, secret) + .await + .map_err(|error| error.to_string())?; + *bearer = Some(token.clone()); + token + } + }; + let ticket = match auth::issue_ws_ticket(client, http_base, &token).await { + Ok(ticket) => ticket, + Err(error) => { + if matches!(error, AuthError::Rejected(_)) { + *bearer = None; + } + return Err(error.to_string()); + } + }; + let url = socket_url(config.port, &ticket)?; + let (socket, _) = tokio_tungstenite::connect_async(url.as_str()) + .await + .map_err(|error| error.to_string())?; + Ok(socket) +} + +/// Pings until the socket closes or goes quiet; returns why it ended. +async fn serve(socket: Socket) -> String { + let (mut sink, mut stream) = socket.split(); + let mut ping = interval(PING_INTERVAL); + ping.set_missed_tick_behavior(MissedTickBehavior::Delay); + let mut deadline = Instant::now() + PING_TIMEOUT; + loop { + tokio::select! { + _ = ping.tick() => { + if let Err(error) = sink.send(Message::text(PING_FRAME)).await { + return error.to_string(); + } + } + _ = sleep_until(deadline) => { + return format!("server stopped responding for {}s", PING_TIMEOUT.as_secs()); + } + frame = stream.next() => match frame { + None => return "connection closed".to_owned(), + Some(Err(error)) => return error.to_string(), + Some(Ok(Message::Close(frame))) => { + return frame + .map(|frame| format!("connection closed: {}", frame.reason)) + .unwrap_or_else(|| "connection closed".to_owned()); + } + // No RPC methods yet: any frame, Pong included, proves the server is alive. + Some(Ok(_)) => deadline = Instant::now() + PING_TIMEOUT, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn backoff_doubles_to_a_cap_and_resets() { + let mut backoff = Backoff::new(); + let delays: Vec<_> = (0..7).map(|_| backoff.next_delay().as_millis()).collect(); + assert_eq!(delays, vec![500, 1000, 2000, 4000, 8000, 10_000, 10_000]); + backoff.reset(); + assert_eq!(backoff.next_delay().as_millis(), 500); + } + + #[test] + fn socket_url_carries_protocol_and_escaped_ticket() { + let url = socket_url(13773, "a b&c").expect("url"); + assert_eq!(url.scheme(), "ws"); + assert_eq!(url.path(), "/ws"); + let pairs: Vec<_> = url.query_pairs().into_owned().collect(); + assert!(pairs.contains(&("orchestrationProtocol".into(), "2".into()))); + assert!(pairs.contains(&("wsTicket".into(), "a b&c".into()))); + } +} diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs new file mode 100644 index 0000000000000..cce74b27c9780 --- /dev/null +++ b/apps/desktop-gpui/src/main.rs @@ -0,0 +1,93 @@ +mod auth; +mod config; +mod connection; +mod server; +mod status; + +use config::Config; +use gpui::{ + App, Bounds, Context, SharedString, TitlebarOptions, Window, WindowBounds, WindowOptions, div, + prelude::*, px, rgb, size, +}; +use gpui_platform::application; +use server::Supervisor; +use status::Status; +use std::rc::Rc; + +struct StatusView { + status: SharedString, +} + +impl Render for StatusView { + fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { + div() + .flex() + .size_full() + .justify_center() + .items_center() + .bg(rgb(0x1a1a1a)) + .text_color(rgb(0xe6e6e6)) + .text_xl() + .child(self.status.clone()) + } +} + +fn main() { + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .expect("failed to start the async runtime"); + let (status_tx, status_rx) = async_channel::unbounded(); + let supervisor = Config::from_env() + .map(|config| Rc::new(Supervisor::start(runtime.handle(), config, status_tx))); + let initial = if supervisor.is_some() { + Status::Starting + } else { + Status::NotConnected + }; + + application().run(move |cx: &mut App| { + if let Some(supervisor) = supervisor { + // The grace period outlasts gpui's quit timeout, so block here instead of awaiting. + cx.on_app_quit(move |_| { + supervisor.stop(); + async {} + }) + .detach(); + } + let bounds = Bounds::centered(None, size(px(960.), px(640.)), cx); + cx.open_window( + WindowOptions { + window_bounds: Some(WindowBounds::Windowed(bounds)), + titlebar: Some(TitlebarOptions { + title: Some("T3 Code".into()), + ..Default::default() + }), + ..Default::default() + }, + |_, cx| { + cx.new(|cx| { + cx.spawn(async move |this, cx| { + while let Ok(status) = status_rx.recv().await { + let updated = this.update(cx, |view: &mut StatusView, cx| { + view.status = status.to_string().into(); + cx.notify(); + }); + if updated.is_err() { + break; + } + } + }) + .detach(); + StatusView { + status: initial.to_string().into(), + } + }) + }, + ) + .expect("failed to open the main window"); + cx.activate(true); + }); + drop(runtime); +} diff --git a/apps/desktop-gpui/src/server.rs b/apps/desktop-gpui/src/server.rs new file mode 100644 index 0000000000000..3d4873b2c3ba8 --- /dev/null +++ b/apps/desktop-gpui/src/server.rs @@ -0,0 +1,404 @@ +//! Spawns and supervises the T3 server the way apps/desktop does +//! (DesktopBackendConfiguration.ts and DesktopBackendManager.ts): a JSON +//! bootstrap line on fd 3, readiness on /.well-known/t3/environment, SIGTERM +//! then SIGKILL on stop. + +use crate::auth; +use crate::config::{Config, ServerCommand}; +use crate::connection::{self, Backoff}; +use crate::status::{Status, StatusSender, publish}; +use serde::{Deserialize, Serialize}; +use std::io; +use std::process::Stdio; +use std::sync::Mutex; +use std::time::Duration; +use tokio::io::{AsyncBufReadExt, AsyncRead, BufReader}; +use tokio::process::{Child, Command}; +use tokio::sync::watch; +use tokio::time::{Instant, sleep, timeout}; + +const READINESS_PATH: &str = "/.well-known/t3/environment"; +// Large state databases can take over a minute to become ready. +const READINESS_TIMEOUT: Duration = Duration::from_secs(3 * 60); +const READINESS_INTERVAL: Duration = Duration::from_millis(100); +const READINESS_REQUEST_TIMEOUT: Duration = Duration::from_secs(1); +// Matches the service launcher's grace so the server can stop provider sessions. +const TERMINATE_GRACE: Duration = Duration::from_secs(5); +// A run this long counts as healthy, so the next failure restarts quickly again. +const HEALTHY_UPTIME: Duration = Duration::from_secs(30); +const BOOTSTRAP_FD: i32 = 3; +// packages/contracts/src/desktopBootstrap.ts: refusals a restart cannot fix. +const EXIT_MANAGED_POLICY: i32 = 77; +const EXIT_STATE_DIR_OWNED: i32 = 78; + +// The server takes these from the bootstrap payload, never from the launcher's shell. +const ENV_REMOVED_FROM_SERVER: &[&str] = &[ + "T3CODE_PORT", + "T3CODE_MODE", + "T3CODE_NO_BROWSER", + "T3CODE_HOST", + "T3CODE_DESKTOP_WS_URL", + "T3CODE_DESKTOP_LAN_HOST", + "T3CODE_DESKTOP_HTTPS_ENDPOINTS", + "T3CODE_TAILSCALE_SERVE", + "T3CODE_TAILSCALE_SERVE_PORT", +]; + +/// Shape of packages/contracts/src/desktopBootstrap.ts. Optional telemetry and +/// browser descriptors are omitted: this client provides neither. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Bootstrap<'a> { + mode: &'static str, + no_browser: bool, + port: u16, + t3_home: &'a str, + host: &'static str, + desktop_bootstrap_token: String, + desktop_bootstrap_secret: &'a str, + // The schema requires both even with Tailscale Serve off. + tailscale_serve_enabled: bool, + tailscale_serve_port: u16, +} + +fn bootstrap_line(config: &Config, secret: &str) -> String { + let bootstrap = Bootstrap { + mode: "desktop", + no_browser: true, + port: config.port, + t3_home: &config.t3_home, + host: "127.0.0.1", + desktop_bootstrap_token: auth::bootstrap_token(secret, auth::now_ms()), + desktop_bootstrap_secret: secret, + tailscale_serve_enabled: false, + tailscale_serve_port: 443, + }; + let mut line = serde_json::to_string(&bootstrap).expect("bootstrap serializes"); + line.push('\n'); + line +} + +/// Pipe pre-filled with `payload`, write end already closed so the server sees EOF. +#[cfg(unix)] +fn bootstrap_pipe(payload: &[u8]) -> io::Result { + use std::io::Write; + use std::os::fd::{FromRawFd, OwnedFd}; + + let mut fds = [0i32; 2]; + // SAFETY: `fds` holds two ints, as pipe(2) requires. + if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 { + return Err(io::Error::last_os_error()); + } + // SAFETY: both descriptors were just created and are owned by nobody else. + let (read, write) = unsafe { (OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])) }; + for fd in [&read, &write] { + use std::os::fd::AsRawFd; + // Only the descriptor dup'd onto fd 3 may survive exec. + // SAFETY: valid, owned descriptor. + if unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) } != 0 { + return Err(io::Error::last_os_error()); + } + } + // The payload is far below the pipe capacity, so this never blocks. + std::fs::File::from(write).write_all(payload)?; + Ok(read) +} + +#[cfg(unix)] +fn spawn_server(config: &Config, secret: &str) -> io::Result { + use std::os::fd::AsRawFd; + + let ServerCommand { program, args } = &config.command; + let pipe = bootstrap_pipe(bootstrap_line(config, secret).as_bytes())?; + let source = pipe.as_raw_fd(); + let mut command = Command::new(program); + command + .args(args) + .args(["--bootstrap-fd", &BOOTSTRAP_FD.to_string()]) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + // Last resort if this task is dropped; the explicit stop path is `terminate`. + .kill_on_drop(true); + for name in ENV_REMOVED_FROM_SERVER { + command.env_remove(name); + } + // SAFETY: the closure only calls async-signal-safe fcntl/dup2. + unsafe { + command.pre_exec(move || { + if source == BOOTSTRAP_FD { + let flags = libc::fcntl(source, libc::F_GETFD); + if flags < 0 || libc::fcntl(source, libc::F_SETFD, flags & !libc::FD_CLOEXEC) < 0 { + return Err(io::Error::last_os_error()); + } + } else if libc::dup2(source, BOOTSTRAP_FD) < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + }); + } + let child = command.spawn(); + drop(pipe); + child +} + +#[cfg(not(unix))] +fn spawn_server(_config: &Config, _secret: &str) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "starting the server needs a unix fd 3 pipe", + )) +} + +fn forward_output(stream: impl AsyncRead + Unpin + Send + 'static) { + tokio::spawn(async move { + let mut lines = BufReader::new(stream).lines(); + while let Ok(Some(line)) = lines.next_line().await { + eprintln!("[server] {line}"); + } + }); +} + +#[derive(Deserialize)] +struct Descriptor { + label: String, +} + +/// Polls until the descriptor answers, returning the environment label. +async fn wait_until_ready(client: &reqwest::Client, http_base: &str) -> Result { + let url = format!("{http_base}{READINESS_PATH}"); + let deadline = Instant::now() + READINESS_TIMEOUT; + loop { + let attempt = client + .get(&url) + .timeout(READINESS_REQUEST_TIMEOUT) + .send() + .await; + if let Ok(response) = attempt + && response.status().is_success() + && let Ok(descriptor) = response.json::().await + { + return Ok(descriptor.label); + } + if Instant::now() >= deadline { + return Err(format!( + "Server did not become ready within {}s", + READINESS_TIMEOUT.as_secs() + )); + } + sleep(READINESS_INTERVAL).await; + } +} + +/// SIGTERM the child this process spawned, then SIGKILL after the grace period. +async fn terminate(child: &mut Child) { + #[cfg(unix)] + if let Some(pid) = child.id() { + // SAFETY: `pid` is the id of the child we spawned and have not yet reaped. + unsafe { libc::kill(pid as i32, libc::SIGTERM) }; + } + if timeout(TERMINATE_GRACE, child.wait()).await.is_err() { + let _ = child.kill().await; + } +} + +async fn shutdown_requested(shutdown: &mut watch::Receiver) { + let _ = shutdown.wait_for(|requested| *requested).await; +} + +/// Waits out `delay`; true when shutdown arrived first. +async fn sleep_or_shutdown(delay: Duration, shutdown: &mut watch::Receiver) -> bool { + tokio::select! { + _ = sleep(delay) => false, + _ = shutdown_requested(shutdown) => true, + } +} + +async fn session(config: &Config, secret: &str, status: &StatusSender) -> String { + let client = auth::http_client(); + match wait_until_ready(&client, &config.http_base()).await { + Ok(label) => { + connection::run(config, secret, &label, status).await; + unreachable_session() + } + Err(message) => message, + } +} + +// `connection::run` only ends by cancellation. +fn unreachable_session() -> String { + "connection ended".to_owned() +} + +/// Runs the server and its connection until shutdown, restarting after crashes. +pub async fn supervise(config: Config, status: StatusSender, mut shutdown: watch::Receiver) { + let mut backoff = Backoff::new(); + loop { + publish(&status, Status::Starting); + let secret = match auth::generate_secret() { + Ok(secret) => secret, + Err(message) => return publish(&status, Status::Failed(message)), + }; + let started = Instant::now(); + let mut child = match spawn_server(&config, &secret) { + Ok(child) => child, + Err(error) => { + publish( + &status, + Status::Failed(format!("Could not start the server: {error}")), + ); + if sleep_or_shutdown(backoff.next_delay(), &mut shutdown).await { + return; + } + continue; + } + }; + if let Some(stdout) = child.stdout.take() { + forward_output(stdout); + } + if let Some(stderr) = child.stderr.take() { + forward_output(stderr); + } + + let failure = tokio::select! { + _ = shutdown_requested(&mut shutdown) => { + terminate(&mut child).await; + return; + } + exited = child.wait() => { + let code = exited.ok().and_then(|exit| exit.code()); + match code { + Some(EXIT_STATE_DIR_OWNED) => { + publish(&status, Status::Failed( + "Another T3 Code server already owns this T3 home. Stop it or use a different home.".to_owned(), + )); + shutdown_requested(&mut shutdown).await; + return; + } + Some(EXIT_MANAGED_POLICY) => { + publish(&status, Status::Failed("The server was refused by managed policy.".to_owned())); + shutdown_requested(&mut shutdown).await; + return; + } + Some(code) => format!("Server exited with code {code}"), + None => "Server exited unexpectedly".to_owned(), + } + } + message = session(&config, &secret, &status) => { + terminate(&mut child).await; + message + } + }; + if started.elapsed() >= HEALTHY_UPTIME { + backoff.reset(); + } + publish(&status, Status::Failed(format!("{failure}. Restarting..."))); + if sleep_or_shutdown(backoff.next_delay(), &mut shutdown).await { + return; + } + } +} + +/// Handle for stopping the supervisor from the UI thread at quit. +pub struct Supervisor { + shutdown: watch::Sender, + done: Mutex>>, +} + +impl Supervisor { + pub fn start(runtime: &tokio::runtime::Handle, config: Config, status: StatusSender) -> Self { + let (shutdown, shutdown_rx) = watch::channel(false); + let (done_tx, done_rx) = std::sync::mpsc::channel(); + runtime.spawn(async move { + supervise(config, status, shutdown_rx).await; + let _ = done_tx.send(()); + }); + Self { + shutdown, + done: Mutex::new(Some(done_rx)), + } + } + + /// Blocks until the server we spawned is gone (bounded by the grace period). + pub fn stop(&self) { + let _ = self.shutdown.send(true); + let done = self.done.lock().ok().and_then(|mut done| done.take()); + if let Some(done) = done { + let _ = done.recv_timeout(TERMINATE_GRACE + Duration::from_secs(2)); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn config() -> Config { + Config { + port: 13773, + t3_home: "/work/.t3".to_owned(), + command: ServerCommand { + program: "node".to_owned(), + args: vec!["/work/apps/server/src/bin.ts".to_owned()], + }, + } + } + + #[test] + fn bootstrap_line_matches_the_contract() { + let line = bootstrap_line(&config(), "secret"); + assert!(line.ends_with('\n')); + let value: serde_json::Value = serde_json::from_str(line.trim_end()).expect("json"); + assert_eq!(value["mode"], "desktop"); + assert_eq!(value["noBrowser"], true); + assert_eq!(value["port"], 13773); + assert_eq!(value["host"], "127.0.0.1"); + assert_eq!(value["t3Home"], "/work/.t3"); + assert_eq!(value["desktopBootstrapSecret"], "secret"); + assert_eq!(value["tailscaleServeEnabled"], false); + assert_eq!(value["tailscaleServePort"], 443); + assert_eq!( + value["desktopBootstrapToken"].as_str().map(str::len), + Some(64) + ); + for omitted in [ + "desktopTelemetryFd", + "desktopBrowserFd", + "shellEnvironmentPrepared", + ] { + assert!(value.get(omitted).is_none(), "{omitted} must be omitted"); + } + } + + #[cfg(unix)] + #[tokio::test] + async fn child_reads_the_bootstrap_line_from_fd_3() { + let mut config = config(); + config.command = ServerCommand { + program: "/bin/sh".to_owned(), + // `--bootstrap-fd 3` becomes $1 and $2 of the script. + args: vec![ + "-c".to_owned(), + "head -n 1 <&3; exit 7".to_owned(), + "sh".to_owned(), + ], + }; + let mut child = spawn_server(&config, "secret").expect("spawn"); + let output = { + use tokio::io::AsyncReadExt; + let mut text = String::new(); + child + .stdout + .take() + .expect("stdout") + .read_to_string(&mut text) + .await + .expect("read"); + text + }; + let exit = child.wait().await.expect("wait"); + assert_eq!(exit.code(), Some(7)); + let value: serde_json::Value = serde_json::from_str(output.trim()).expect("json line"); + assert_eq!(value["desktopBootstrapSecret"], "secret"); + } +} diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs new file mode 100644 index 0000000000000..9e7cbd99470fb --- /dev/null +++ b/apps/desktop-gpui/src/status.rs @@ -0,0 +1,30 @@ +use std::fmt; + +/// What the window shows about the managed server and its socket. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Status { + NotConnected, + Starting, + Connecting, + Connected(String), + Failed(String), +} + +impl fmt::Display for Status { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NotConnected => f.write_str("Not connected"), + Self::Starting => f.write_str("Starting server..."), + Self::Connecting => f.write_str("Connecting..."), + Self::Connected(label) => write!(f, "Connected to {label}"), + Self::Failed(message) => f.write_str(message), + } + } +} + +pub type StatusSender = async_channel::Sender; + +pub fn publish(sender: &StatusSender, status: Status) { + // Unbounded, so this only fails once the window is gone. + let _ = sender.try_send(status); +} diff --git a/package.json b/package.json index 2824692d78f7b..8a6519a359404 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dev:web": "node scripts/dev-runner.ts dev:web", "dev:marketing": "vp run --filter @t3tools/marketing dev", "dev:desktop": "node scripts/dev-runner.ts dev:desktop", + "dev:desktop-gpui": "node scripts/dev-runner.ts dev:desktop-gpui", "migrate-dev-db": "node apps/server/scripts/migrate-dev-db.ts", "start": "vp run --filter t3 start", "start:desktop": "vp run --filter @t3tools/desktop start", diff --git a/scripts/dev-runner.test.ts b/scripts/dev-runner.test.ts index 34b768319c2fb..f01616da5b0c6 100644 --- a/scripts/dev-runner.test.ts +++ b/scripts/dev-runner.test.ts @@ -93,6 +93,41 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { assert.equal(desktop.T3CODE_DEV_AUTH_TOKEN, undefined); }), ); + it.effect("hands the gpui app its server port, home and entry without web URLs", () => + Effect.gen(function* () { + const path = yield* Path.Path; + const input = { + mode: "dev:desktop-gpui", + baseEnv: { + VITE_HTTP_URL: "http://localhost:1", + VITE_WS_URL: "ws://localhost:1", + T3CODE_PORT: "1", + T3CODE_HOME: "/ambient/home", + }, + serverOffset: 3, + webOffset: 3, + t3Home: "/tmp/worktree/.t3", + browser: undefined, + autoBootstrapProjectFromCwd: undefined, + logWebSocketEvents: undefined, + host: undefined, + port: undefined, + devUrl: undefined, + } as const; + const env = yield* createDevRunnerEnv(input); + const fallback = yield* createDevRunnerEnv({ ...input, t3Home: undefined }); + + assert.equal(env.T3CODE_GPUI_SERVER_PORT, String(13_773 + 3)); + assert.equal(env.T3CODE_GPUI_HOME, path.resolve("/tmp/worktree/.t3")); + assert.isTrue(path.isAbsolute(env.T3CODE_GPUI_SERVER_ENTRY ?? "")); + assert.isTrue(env.T3CODE_GPUI_SERVER_ENTRY?.endsWith(path.join("apps/server/src/bin.ts"))); + assert.equal(env.VITE_HTTP_URL, undefined); + assert.equal(env.VITE_WS_URL, undefined); + assert.equal(env.T3CODE_PORT, undefined); + assert.notEqual(fallback.T3CODE_GPUI_HOME, path.join(NodeOS.homedir(), ".t3")); + }), + ); + it.effect("keeps the desktop profile inside an isolated home only", () => Effect.gen(function* () { const path = yield* Path.Path; @@ -371,6 +406,21 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { }), ); + it.effect("shifts only server offset for dev:desktop-gpui", () => + Effect.gen(function* () { + const taken = new Set([13773]); + const offsets = yield* resolveModePortOffsets({ + mode: "dev:desktop-gpui", + startOffset: 0, + hasExplicitServerPort: false, + hasExplicitDevUrl: false, + checkPortAvailability: (port) => Effect.succeed(!taken.has(port)), + }); + + assert.deepStrictEqual(offsets, { serverOffset: 1, webOffset: 1 }); + }), + ); + it.effect("respects explicit dev-url override for dev:web", () => Effect.gen(function* () { const offsets = yield* resolveModePortOffsets({ @@ -658,6 +708,7 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { { mode: "dev", share: false }, { mode: "dev:web", share: true }, { mode: "dev:desktop", share: true }, + { mode: "dev:desktop-gpui", share: true }, ] as const)("rejects --built for $mode with share=$share", ({ mode, share }) => { const spawnerLayer = Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, diff --git a/scripts/dev-runner.ts b/scripts/dev-runner.ts index 55ff6ac0513dc..a449d73ea7a3a 100644 --- a/scripts/dev-runner.ts +++ b/scripts/dev-runner.ts @@ -76,6 +76,8 @@ const MODE_ARGS = { "dev:server": ["run", "--filter=t3", "dev"], "dev:web": ["run", "--filter=@t3tools/web", "dev"], "dev:desktop": ["run", "--filter=@t3tools/desktop", "--filter=@t3tools/web", "dev"], + // The native client spawns its own server; no Vite or web server runs. + "dev:desktop-gpui": ["run", "--filter=@t3tools/desktop-gpui", "dev"], } as const satisfies Record>; // `--built`: the web package rebuilds into apps/web/dist and the backend serves @@ -146,7 +148,7 @@ export class DevRunnerProcessError extends Schema.TaggedError()( "DevRunnerProcessExitError", { - mode: Schema.Literals(["dev", "dev:server", "dev:web", "dev:desktop"]), + mode: Schema.Literals(["dev", "dev:server", "dev:web", "dev:desktop", "dev:desktop-gpui"]), executable: Schema.Literal("vp"), argumentCount: Schema.Number, shell: Schema.Boolean, @@ -197,7 +199,7 @@ export class DevRunnerHostNotProxiableError extends Schema.TaggedError()( "DevRunnerBuiltNeedsShareError", { - mode: Schema.Literals(["dev", "dev:server", "dev:web", "dev:desktop"]), + mode: Schema.Literals(["dev", "dev:server", "dev:web", "dev:desktop", "dev:desktop-gpui"]), }, ) { override get message(): string { @@ -331,6 +333,7 @@ export const createDevRunnerEnv = Effect.fn("dev-runner.createDevRunnerEnv")(fun const configuredBaseDir = t3Home?.trim() || undefined; const resolvedBaseDir = yield* resolveBaseDir(configuredBaseDir); const isDesktopMode = mode === "dev:desktop"; + const isGpuiMode = mode === "dev:desktop-gpui"; const output: NodeJS.ProcessEnv = { ...baseEnv, @@ -340,7 +343,17 @@ export const createDevRunnerEnv = Effect.fn("dev-runner.createDevRunnerEnv")(fun `http://${isDesktopMode ? DESKTOP_DEV_LOOPBACK_HOST : "localhost"}:${webPort}`, }; - if (configuredBaseDir !== undefined) { + if (isGpuiMode) { + // The app hands this home to its server through the bootstrap payload, so it + // must never fall back to the live ~/.t3. + const path = yield* Path.Path; + const gpuiHome = + configuredBaseDir === undefined + ? path.join(path.dirname(import.meta.dirname), ".t3") + : resolvedBaseDir; + output.T3CODE_HOME = gpuiHome; + output.T3CODE_GPUI_HOME = gpuiHome; + } else if (configuredBaseDir !== undefined) { output.T3CODE_HOME = resolvedBaseDir; } else { delete output.T3CODE_HOME; @@ -355,7 +368,33 @@ export const createDevRunnerEnv = Effect.fn("dev-runner.createDevRunnerEnv")(fun // install of the developer's real accounts. Set it explicitly to opt in. output.T3CODE_TELEMETRY_ENABLED = baseEnv.T3CODE_TELEMETRY_ENABLED ?? "false"; - if (!isDesktopMode) { + if (isGpuiMode) { + const path = yield* Path.Path; + output.T3CODE_GPUI_SERVER_PORT = String(serverPort); + output.T3CODE_GPUI_SERVER_COMMAND = yield* HostProcess.ExecutablePath; + output.T3CODE_GPUI_SERVER_ENTRY = path.join( + path.dirname(import.meta.dirname), + "apps/server/src/bin.ts", + ); + // No web client runs, so nothing may point one at a backend, and the app's + // server takes its settings from the bootstrap payload alone. + for (const name of [ + "PORT", + "HOST", + "VITE_DEV_SERVER_URL", + "VITE_HTTP_URL", + "VITE_WS_URL", + "T3CODE_PORT", + "T3CODE_MODE", + "T3CODE_NO_BROWSER", + "T3CODE_HOST", + "T3CODE_DESKTOP_WS_URL", + "T3CODE_SINGLE_ORIGIN_DEV", + "T3CODE_DEV_AUTH_TOKEN", + ]) { + delete output[name]; + } + } else if (!isDesktopMode) { output.T3CODE_PORT = String(serverPort); // An inherited HOST pins Vite's HMR socket via explicitHost in apps/web/vite.config.ts. // Only desktop mode should set it. @@ -389,11 +428,11 @@ export const createDevRunnerEnv = Effect.fn("dev-runner.createDevRunnerEnv")(fun delete output.T3CODE_DEV_AUTH_TOKEN; } - if (!isDesktopMode && host !== undefined) { + if (!isDesktopMode && !isGpuiMode && host !== undefined) { output.T3CODE_HOST = host; } - if (!isDesktopMode) { + if (!isDesktopMode && !isGpuiMode) { output.T3CODE_NO_BROWSER = browser === true ? "0" : "1"; } @@ -583,7 +622,7 @@ export const resolveModePortOffsets = Effect.fn("dev-runner.resolveModePortOffse return { serverOffset: startOffset, webOffset }; } - if (mode === "dev:server") { + if (mode === "dev:server" || mode === "dev:desktop-gpui") { if (hasExplicitServerPort) { return { serverOffset: startOffset, webOffset: startOffset }; } @@ -686,6 +725,7 @@ const prepareFirstStart = Effect.fn("dev-runner.prepareFirstStart")(function* (i const repoRoot = path.dirname(import.meta.dirname); if ( input.mode !== "dev:server" && + input.mode !== "dev:desktop-gpui" && !(yield* exists(path.join(repoRoot, "apps/web/node_modules/.vite/deps/_metadata.json"))) ) { yield* runRepoScript("apps/web/scripts/warm-dep-cache.ts", [], input.env); @@ -806,7 +846,7 @@ export const runDevRunnerWithInput = Effect.fn("dev-runner.runDevRunnerWithInput const baseDir = env.T3CODE_HOME ?? (yield* DEFAULT_T3_HOME); yield* Effect.logInfo( - `[dev-runner] mode=${input.mode} source=${source}${selectionSuffix} serverPort=${String(env.T3CODE_PORT)} webPort=${String(env.PORT)} baseDir=${baseDir}`, + `[dev-runner] mode=${input.mode} source=${source}${selectionSuffix} serverPort=${String(env.T3CODE_PORT ?? env.T3CODE_GPUI_SERVER_PORT)} webPort=${String(env.PORT)} baseDir=${baseDir}`, ); // Mirrors the server's deriveServerPaths: an explicit home uses userdata. @@ -816,7 +856,7 @@ export const runDevRunnerWithInput = Effect.fn("dev-runner.runDevRunnerWithInput ); if ( !input.dryRun && - (input.mode === "dev" || input.mode === "dev:server") && + (input.mode === "dev" || input.mode === "dev:server" || input.mode === "dev:desktop-gpui") && (yield* isStateDirOwned(stateDir)) ) { return yield* new DevRunnerStateDirOwnedError({ stateDir }); @@ -838,6 +878,10 @@ export const runDevRunnerWithInput = Effect.fn("dev-runner.runDevRunnerWithInput yield* Effect.logWarning( "[dev-runner] --share is not supported for dev:desktop (the renderer is pinned to loopback). Use `dev`, which runs the whole browser stack.", ); + } else if (input.mode === "dev:desktop-gpui") { + yield* Effect.logWarning( + "[dev-runner] --share is not supported for dev:desktop-gpui (the app talks to its own loopback server). Use `dev`, which runs the whole browser stack.", + ); } else { // acquireRelease, not share-then-addFinalizer: the mapping outlives this // process (and reboots), so the cleanup has to be registered atomically From 4199241d43f4ab6abca638e8119c3e944abdf261 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Wed, 4 Nov 2026 23:35:06 -0500 Subject: [PATCH 02/14] feat(desktop-gpui): typed RPC wire types generated from contracts Hand-porting 212 RPC methods to Rust would drift from the TypeScript contracts. Export a JSON Schema from WsRpcGroup, check it for drift in CI, and generate the Rust types from it so the native client decodes the shell subscription with the same shapes the server sends. Signed-off-by: Yordis Prieto --- .github/workflows/ci.yml | 3 + apps/desktop-gpui/Cargo.lock | 168 +- apps/desktop-gpui/Cargo.toml | 6 + apps/desktop-gpui/README.md | 19 +- apps/desktop-gpui/src/connection.rs | 79 +- apps/desktop-gpui/src/main.rs | 3 + apps/desktop-gpui/src/rpc.rs | 285 + apps/desktop-gpui/src/shell.rs | 221 + apps/desktop-gpui/src/status.rs | 71 +- apps/desktop-gpui/src/wire/generated.rs | 4058 ++ apps/desktop-gpui/src/wire/mod.rs | 11 + .../tests/fixtures/shell-project-updated.json | 14 + .../tests/fixtures/shell-snapshot.json | 61 + .../tests/fixtures/shell-thread-updated.json | 44 + apps/desktop-gpui/tests/wire_types.rs | 133 + packages/contracts/package.json | 1 + packages/contracts/schema/rpc.schema.json | 52466 ++++++++++++++++ .../scripts/export-rpc-json-schema.ts | 316 + scripts/check-changed.ts | 5 + vite.config.ts | 2 + 20 files changed, 57950 insertions(+), 16 deletions(-) create mode 100644 apps/desktop-gpui/src/rpc.rs create mode 100644 apps/desktop-gpui/src/shell.rs create mode 100644 apps/desktop-gpui/src/wire/generated.rs create mode 100644 apps/desktop-gpui/src/wire/mod.rs create mode 100644 apps/desktop-gpui/tests/fixtures/shell-project-updated.json create mode 100644 apps/desktop-gpui/tests/fixtures/shell-snapshot.json create mode 100644 apps/desktop-gpui/tests/fixtures/shell-thread-updated.json create mode 100644 apps/desktop-gpui/tests/wire_types.rs create mode 100644 packages/contracts/schema/rpc.schema.json create mode 100644 packages/contracts/scripts/export-rpc-json-schema.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9253bd23eb85..dc54cf1c588df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,6 +109,9 @@ jobs: - name: Check generated Expo GraphQL operations run: vp run --filter t3 generate --check + - name: Check generated RPC JSON Schema + run: vp run --filter @t3tools/contracts generate --check + - name: Check run: vp check diff --git a/apps/desktop-gpui/Cargo.lock b/apps/desktop-gpui/Cargo.lock index 7691fef2bbe94..d2c4c67b10c26 100644 --- a/apps/desktop-gpui/Cargo.lock +++ b/apps/desktop-gpui/Cargo.lock @@ -707,6 +707,19 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" +[[package]] +name = "codespace" +version = "0.0.1-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b10f68d82f8de01589cd0ab1a21cddaf271b2a936d0bb2518c56a3498efe2f" +dependencies = [ + "proc-macro2", + "quote", + "semver", + "syn 3.0.9", + "version-ranges", +] + [[package]] name = "codespan-reporting" version = "0.13.1" @@ -1692,7 +1705,7 @@ dependencies = [ "regex", "resvg", "scheduler", - "schemars", + "schemars 1.2.3", "seahash", "serde", "serde_json", @@ -1871,7 +1884,7 @@ name = "gpui_shared_string" version = "0.1.0" source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" dependencies = [ - "schemars", + "schemars 1.2.3", "serde", "smol_str", ] @@ -3689,6 +3702,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "prettyplease" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" +dependencies = [ + "proc-macro2", + "syn 3.0.9", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -4155,6 +4178,15 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "regress" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32eef8b209c3c1c15dbad02c1f30f9539f00dc7253e0cbcdaae442a50a09d7c1" +dependencies = [ + "memchr", +] + [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -4433,6 +4465,18 @@ dependencies = [ "web-time", ] +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive 0.8.22", + "serde", + "serde_json", +] + [[package]] name = "schemars" version = "1.2.3" @@ -4442,11 +4486,23 @@ dependencies = [ "dyn-clone", "indexmap", "ref-cast", - "schemars_derive", + "schemars_derive 1.2.3", "serde", "serde_json", ] +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals 0.29.1", + "syn 2.0.119", +] + [[package]] name = "schemars_derive" version = "1.2.3" @@ -4455,7 +4511,7 @@ checksum = "e1c3a92094fa7d61aa124645844facb6b554dfc797136d0f5fd1f890e2bffc69" dependencies = [ "proc-macro2", "quote", - "serde_derive_internals", + "serde_derive_internals 0.30.0", "syn 3.0.9", ] @@ -4550,6 +4606,17 @@ dependencies = [ "syn 3.0.9", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "serde_derive_internals" version = "0.30.0" @@ -4605,6 +4672,18 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_tokenstream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db95b8acf6eca5bc1bc771a7d7fb1a0d1485bdef0c46eec2f573bbaf4b4118f8" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.9", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4985,12 +5064,16 @@ dependencies = [ "hex", "hmac 0.13.0", "libc", + "prettyplease", "reqwest", + "schemars 0.8.22", "serde", "serde_json", "sha2 0.11.1", + "syn 3.0.9", "tokio", "tokio-tungstenite", + "typify", ] [[package]] @@ -5388,6 +5471,73 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "typespace" +version = "0.0.1-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6d2e65af738a56413ffeec4a912246a37cbc3e95e7bf5241d971bdce10ae47" +dependencies = [ + "codespace", + "heck 0.5.0", + "log", + "proc-macro2", + "quote", + "schemars 0.8.22", + "serde", + "serde_json", + "strum", + "syn 3.0.9", + "thiserror", +] + +[[package]] +name = "typify" +version = "0.10.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96a0d41e55c21fa2d06a672c7306dac957197c16ab527d0cdf6c33b910126fc" +dependencies = [ + "typify-impl", + "typify-macro", +] + +[[package]] +name = "typify-impl" +version = "0.10.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b487fba77e13eee8caea824dc8c8c94457cb08040ea60ae3e26a649e504a6281" +dependencies = [ + "heck 0.5.0", + "log", + "proc-macro2", + "quote", + "regress", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "syn 3.0.9", + "thiserror", + "typespace", + "unicode-ident", +] + +[[package]] +name = "typify-macro" +version = "0.10.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c697dad0e4ddb1b73a0bf780cd44c6487a986afc8b0826fe2e11a067c81b528" +dependencies = [ + "proc-macro2", + "quote", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "serde_tokenstream", + "syn 3.0.9", + "typify-impl", +] + [[package]] name = "uds_windows" version = "1.2.1" @@ -5543,6 +5693,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "version-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e9bd4e9c9ff6a2a9b5969462ba26216af3e010df0377dad8320ab515262ef8" +dependencies = [ + "semver", + "smallvec", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/apps/desktop-gpui/Cargo.toml b/apps/desktop-gpui/Cargo.toml index cb23e47de817d..a3d580d795e1e 100644 --- a/apps/desktop-gpui/Cargo.toml +++ b/apps/desktop-gpui/Cargo.toml @@ -34,3 +34,9 @@ tokio-tungstenite = "0.30.0" codegen-units = 1 lto = "thin" strip = true + +[dev-dependencies] +prettyplease = "0.3.0" +schemars = "0.8" +syn = "3.0.9" +typify = "0.10.0-alpha.3" diff --git a/apps/desktop-gpui/README.md b/apps/desktop-gpui/README.md index f8d890596ecdf..4262d1e05ad10 100644 --- a/apps/desktop-gpui/README.md +++ b/apps/desktop-gpui/README.md @@ -2,7 +2,7 @@ Native desktop client written in Rust with [GPUI](https://github.com/zed-industries/zed/tree/main/crates/gpui). It is meant to replace `apps/desktop` (Electron) over time and never embeds Node or Chromium. It starts the T3 server as a child process, authenticates without pairing, and opens the RPC WebSocket. -Status: connection shell. The window shows the connection state ("Starting server...", "Connecting...", "Connected to ", or the error). There are no RPC methods or screens yet. +Status: connection shell. The window shows the connection state ("Starting server...", "Connecting...", "Connected to · N projects · M threads", or the error). The counts come from a live `orchestration.subscribeShell` subscription. There are no screens yet. ## Run @@ -35,10 +35,25 @@ vp run --filter @t3tools/desktop-gpui test # cargo test 1. Spawns ` [entry] --bootstrap-fd 3` and writes one JSON bootstrap line (`packages/contracts/src/desktopBootstrap.ts`) to a pipe dup'd onto fd 3, as Electron does. The secret is random per launch. 2. Polls `GET /.well-known/t3/environment` until the server is ready. 3. Exchanges the HMAC bootstrap token for a bearer session, mints a WebSocket ticket, and opens `/ws`. -4. Sends an RPC `Ping` every 5 seconds and reconnects with backoff after 15 seconds of silence. +4. Subscribes to `orchestration.subscribeShell`, acks each stream chunk, and reduces snapshot and delta items into project and thread counts (`src/rpc.rs`, `src/shell.rs`). +5. Sends an RPC `Ping` every 5 seconds and reconnects with backoff after 15 seconds of silence or when the stream ends. On quit the app sends SIGTERM to the server it spawned and SIGKILL after 5 seconds. If the app is killed, the server notices its parent changed within about 2 seconds and shuts itself down. +## Wire types + +`packages/contracts/schema/rpc.schema.json` is generated from the Effect schemas (`vp run --filter @t3tools/contracts generate`, or `generate --check` to verify). It holds the encoded side of every `WsRpcGroup` method as shared `$defs` plus a `methods` index (payload, success, error refs, stream flag). + +`src/wire/generated.rs` is generated from the methods this client calls (`METHODS` in `tests/wire_types.rs`) with [typify](https://github.com/oxidecomputer/typify) and committed, so normal builds need no codegen and diffs stay reviewable. To add a method, add its tag to `METHODS`, then regenerate: + +```sh +T3_UPDATE_GENERATED=1 cargo test --test wire_types +``` + +Without the variable the same test fails when the committed file is stale. Drift in the schema itself is caught by `generate --check` in CI and `vp run check:changed`. + +Known gaps: typify cannot flatten the `projectIcon` union (an `anyOf` with a `{kind: string}` catch-all), so `ProjectsEntry.projectIcon` is kept opaque (`serde_json::Value`) through the `OPAQUE` list. Unions without a discriminator generate as `#[serde(untagged)]` enums. + ## Notes - `gpui` and `gpui_platform` are pinned to one `zed-industries/zed` commit in `Cargo.toml`. Bump both together. diff --git a/apps/desktop-gpui/src/connection.rs b/apps/desktop-gpui/src/connection.rs index 3751bb282cb1f..60f2dbd9eb471 100644 --- a/apps/desktop-gpui/src/connection.rs +++ b/apps/desktop-gpui/src/connection.rs @@ -2,14 +2,15 @@ use crate::auth::{self, AuthError}; use crate::config::Config; -use crate::status::{Status, StatusSender, publish}; +use crate::rpc::{Event, PING_FRAME, Session, SubscribeShell}; +use crate::shell::ShellState; +use crate::status::{Counts, Status, StatusSender, publish}; +use crate::wire::{ShellItem, SubscribeShellPayload}; use futures_util::{SinkExt, StreamExt}; use std::time::Duration; use tokio::time::{Instant, MissedTickBehavior, interval, sleep, sleep_until}; use tokio_tungstenite::tungstenite::Message; -/// Effect RPC ping envelope (RpcMessage.constPing) in the JSON serialization. -const PING_FRAME: &str = r#"{"_tag":"Ping"}"#; const PING_INTERVAL: Duration = Duration::from_secs(5); // Pings go out every 5 s and a busy server stops answering them, so allow 15 s // without any frame (packages/client-runtime/src/rpc/protocol.ts PING_TIMEOUT). @@ -67,9 +68,15 @@ pub async fn run(config: &Config, secret: &str, label: &str, status: &StatusSend publish(status, Status::Connecting); let failure = match connect(&client, config, &http_base, secret, &mut bearer).await { Ok(socket) => { - publish(status, Status::Connected(label.to_owned())); + publish( + status, + Status::Connected { + label: label.to_owned(), + counts: None, + }, + ); backoff.reset(); - serve(socket).await + serve(socket, label, status).await } Err(message) => message, }; @@ -114,9 +121,18 @@ async fn connect( Ok(socket) } -/// Pings until the socket closes or goes quiet; returns why it ended. -async fn serve(socket: Socket) -> String { +/// Subscribes to the shell stream and keeps the socket alive; returns why it ended. +async fn serve(socket: Socket, label: &str, status: &StatusSender) -> String { let (mut sink, mut stream) = socket.split(); + let mut session = Session::new(); + let mut shell = ShellState::default(); + let request = match session.request::(&SubscribeShellPayload::default()) { + Ok((_, frame)) => frame, + Err(error) => return error, + }; + if let Err(error) = sink.send(Message::text(request)).await { + return error.to_string(); + } let mut ping = interval(PING_INTERVAL); ping.set_missed_tick_behavior(MissedTickBehavior::Delay); let mut deadline = Instant::now() + PING_TIMEOUT; @@ -138,13 +154,60 @@ async fn serve(socket: Socket) -> String { .map(|frame| format!("connection closed: {}", frame.reason)) .unwrap_or_else(|| "connection closed".to_owned()); } - // No RPC methods yet: any frame, Pong included, proves the server is alive. + Some(Ok(Message::Text(text))) => { + // Any frame, Pong included, proves the server is alive. + deadline = Instant::now() + PING_TIMEOUT; + let reaction = session.react(text.as_str()); + for reply in reaction.send { + if let Err(error) = sink.send(Message::text(reply)).await { + return error.to_string(); + } + } + for event in reaction.events { + if let Some(reason) = handle(event, &mut shell, label, status) { + return reason; + } + } + } Some(Ok(_)) => deadline = Instant::now() + PING_TIMEOUT, } } } } +/// Applies one event; `Some(reason)` means the subscription is over and the socket should reopen. +fn handle( + event: Event, + shell: &mut ShellState, + label: &str, + status: &StatusSender, +) -> Option { + match event { + Event::Item { value, .. } => match serde_json::from_value::(value) { + Ok(item) => { + if shell.apply(item) && shell.is_seeded() { + publish( + status, + Status::Connected { + label: label.to_owned(), + counts: Some(Counts { + projects: shell.project_count(), + threads: shell.thread_count(), + }), + }, + ); + } + None + } + Err(error) => Some(format!("unreadable shell item: {error}")), + }, + Event::Done { .. } => Some("shell stream ended".to_owned()), + Event::Failed { message, .. } => Some(format!("shell stream failed: {message}")), + Event::Defect(message) => Some(format!("server defect: {message}")), + Event::Pong | Event::Ignored => None, + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs index cce74b27c9780..c3cbc50c93bbb 100644 --- a/apps/desktop-gpui/src/main.rs +++ b/apps/desktop-gpui/src/main.rs @@ -1,8 +1,11 @@ mod auth; mod config; mod connection; +mod rpc; mod server; +mod shell; mod status; +mod wire; use config::Config; use gpui::{ diff --git a/apps/desktop-gpui/src/rpc.rs b/apps/desktop-gpui/src/rpc.rs new file mode 100644 index 0000000000000..dd86ded87283e --- /dev/null +++ b/apps/desktop-gpui/src/rpc.rs @@ -0,0 +1,285 @@ +//! Effect RPC over the WebSocket in its JSON serialization: request, ack, and response frames. +//! Shapes follow effect's `RpcMessage` (`FromClientEncoded`, `FromServerEncoded`). + +use crate::wire; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::{Value, json}; +use std::collections::HashSet; + +/// RpcMessage.constPing in the JSON serialization. +pub const PING_FRAME: &str = r#"{"_tag":"Ping"}"#; + +/// One RPC method: its tag and the generated wire types for its payload and success. +pub trait Method { + const TAG: &'static str; + type Payload: Serialize; + type Success: DeserializeOwned; +} + +/// `orchestration.subscribeShell`, a stream of shell snapshot and delta items. +pub struct SubscribeShell; + +impl Method for SubscribeShell { + const TAG: &'static str = "orchestration.subscribeShell"; + type Payload = wire::SubscribeShellPayload; + type Success = wire::ShellItem; +} + +/// Request ids are bigints on the wire, so they travel as strings. +fn id_text<'de, D: Deserializer<'de>>(deserializer: D) -> Result { + match Value::deserialize(deserializer)? { + Value::String(text) => Ok(text), + Value::Number(number) => Ok(number.to_string()), + other => Err(serde::de::Error::custom(format!("bad request id: {other}"))), + } +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "_tag")] +enum ServerFrame { + Pong, + Chunk { + #[serde(rename = "requestId", deserialize_with = "id_text")] + request_id: String, + values: Vec, + }, + Exit { + #[serde(rename = "requestId", deserialize_with = "id_text")] + request_id: String, + exit: ExitFrame, + }, + Defect { + defect: Value, + }, + #[serde(other)] + Unknown, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "_tag")] +enum ExitFrame { + Success { value: Value }, + Failure { cause: Vec }, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "_tag")] +enum CauseFrame { + Fail { error: Value }, + Die { defect: Value }, + Interrupt, +} + +/// What a server frame means to the caller. +#[derive(Debug, PartialEq)] +pub enum Event { + Pong, + /// One element of a stream. The element is already acknowledged in `Reaction::send`. + Item { + request_id: String, + value: Value, + }, + /// The request finished successfully (a stream ended, or a unary call returned `value`). + Done { + request_id: String, + value: Value, + }, + /// The request failed with a typed error, a defect, or an interrupt. + Failed { + request_id: String, + message: String, + }, + /// A defect not tied to one request. + Defect(String), + /// Frame this client does not understand; ignored but proves the server is alive. + Ignored, +} + +#[derive(Debug, Default, PartialEq)] +pub struct Reaction { + /// Frames to write back (an Ack per Chunk). + pub send: Vec, + pub events: Vec, +} + +/// Request id bookkeeping for one socket. Create a new one per connection. +#[derive(Default)] +pub struct Session { + next_id: u64, + open: HashSet, +} + +impl Session { + pub fn new() -> Self { + Self::default() + } + + /// Returns the request id and the frame to send. + pub fn request(&mut self, payload: &M::Payload) -> Result<(String, String), String> { + let id = self.next_id.to_string(); + self.next_id += 1; + let frame = json!({ + "_tag": "Request", + "id": id, + "tag": M::TAG, + "payload": serde_json::to_value(payload).map_err(|error| error.to_string())?, + "headers": [], + }); + self.open.insert(id.clone()); + Ok((id, frame.to_string())) + } + + pub fn react(&mut self, text: &str) -> Reaction { + let mut reaction = Reaction::default(); + let Ok(frame) = serde_json::from_str::(text) else { + reaction.events.push(Event::Ignored); + return reaction; + }; + match frame { + ServerFrame::Pong => reaction.events.push(Event::Pong), + ServerFrame::Chunk { request_id, values } => { + // The server holds the next chunk until the previous one is acked. + reaction + .send + .push(json!({ "_tag": "Ack", "requestId": request_id }).to_string()); + for value in values { + reaction.events.push(Event::Item { + request_id: request_id.clone(), + value, + }); + } + } + ServerFrame::Exit { request_id, exit } => { + self.open.remove(&request_id); + reaction.events.push(match exit { + ExitFrame::Success { value } => Event::Done { request_id, value }, + ExitFrame::Failure { cause } => Event::Failed { + request_id, + message: describe_cause(&cause), + }, + }); + } + ServerFrame::Defect { defect } => { + reaction.events.push(Event::Defect(defect.to_string())) + } + ServerFrame::Unknown => reaction.events.push(Event::Ignored), + } + reaction + } +} + +fn describe_cause(cause: &[CauseFrame]) -> String { + let parts: Vec = cause + .iter() + .map(|entry| match entry { + CauseFrame::Fail { error } => format!("failed: {error}"), + CauseFrame::Die { defect } => format!("defect: {defect}"), + CauseFrame::Interrupt => "interrupted".to_owned(), + }) + .collect(); + if parts.is_empty() { + "request failed".to_owned() + } else { + parts.join("; ") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn request_frame_matches_the_effect_envelope() { + let mut session = Session::new(); + let payload = wire::SubscribeShellPayload { + request_completion_marker: Some(true), + ..Default::default() + }; + let (id, frame) = session.request::(&payload).expect("frame"); + let value: Value = serde_json::from_str(&frame).expect("json"); + assert_eq!(id, "0"); + assert_eq!( + value, + json!({ + "_tag": "Request", + "id": "0", + "tag": "orchestration.subscribeShell", + "payload": { "requestCompletionMarker": true }, + "headers": [], + }) + ); + let (second, _) = session.request::(&payload).expect("frame"); + assert_eq!(second, "1"); + } + + #[test] + fn chunk_is_acked_once_and_yields_each_value() { + let mut session = Session::new(); + let reaction = + session.react(r#"{"_tag":"Chunk","requestId":"3","values":[{"a":1},{"a":2}]}"#); + assert_eq!( + reaction.send, + vec![r#"{"_tag":"Ack","requestId":"3"}"#.to_owned()] + ); + assert_eq!( + reaction.events, + vec![ + Event::Item { + request_id: "3".into(), + value: json!({"a":1}) + }, + Event::Item { + request_id: "3".into(), + value: json!({"a":2}) + }, + ] + ); + } + + #[test] + fn numeric_request_ids_are_accepted() { + let reaction = Session::new().react(r#"{"_tag":"Chunk","requestId":7,"values":[1]}"#); + assert_eq!( + reaction.send, + vec![r#"{"_tag":"Ack","requestId":"7"}"#.to_owned()] + ); + } + + #[test] + fn exit_success_failure_defect_and_pong() { + let mut session = Session::new(); + assert_eq!( + session + .react(r#"{"_tag":"Exit","requestId":"1","exit":{"_tag":"Success","value":null}}"#) + .events, + vec![Event::Done { + request_id: "1".into(), + value: Value::Null + }] + ); + let failed = session.react( + r#"{"_tag":"Exit","requestId":"2","exit":{"_tag":"Failure","cause":[{"_tag":"Fail","error":{"_tag":"Nope"}},{"_tag":"Interrupt","fiberId":1}]}}"#, + ); + assert_eq!( + failed.events, + vec![Event::Failed { + request_id: "2".into(), + message: r#"failed: {"_tag":"Nope"}; interrupted"#.into() + }] + ); + assert!(matches!( + session.react(r#"{"_tag":"Defect","defect":"boom"}"#).events[0], + Event::Defect(_) + )); + assert_eq!( + session.react(r#"{"_tag":"Pong"}"#).events, + vec![Event::Pong] + ); + assert_eq!(session.react("not json").events, vec![Event::Ignored]); + assert_eq!( + session.react(r#"{"_tag":"Mystery"}"#).events, + vec![Event::Ignored] + ); + } +} diff --git a/apps/desktop-gpui/src/shell.rs b/apps/desktop-gpui/src/shell.rs new file mode 100644 index 0000000000000..f68bd3c1ec604 --- /dev/null +++ b/apps/desktop-gpui/src/shell.rs @@ -0,0 +1,221 @@ +//! The shell read model built from `orchestration.subscribeShell`, mirroring +//! `packages/client-runtime/src/state/shellReducer.ts` for what this client shows. + +use crate::wire::generated::{ + OrchestrationSubscribeShellSuccess as Item, + OrchestrationSubscribeShellSuccessVariant4Location as Location, + OrchestrationSubscribeShellSuccessVariant5Location as RemovedLocation, +}; +use crate::wire::{Project, Thread}; +use std::collections::HashMap; + +#[derive(Default)] +pub struct ShellState { + projects: HashMap, + threads: HashMap, + sequence: i64, + seeded: bool, +} + +impl ShellState { + /// False until the first snapshot arrives; counts before that would read as "0". + pub fn is_seeded(&self) -> bool { + self.seeded + } + + pub fn project_count(&self) -> usize { + self.projects.len() + } + + /// Active threads only: the archive has its own query. + pub fn thread_count(&self) -> usize { + self.threads.len() + } + + /// Applies one stream item. Returns whether the counts or sync marker may have changed. + pub fn apply(&mut self, item: Item) -> bool { + match item { + // The completion marker carries nothing the counts need. + Item::Variant0 { .. } => false, + Item::Variant1 { snapshot, .. } => { + self.projects = snapshot + .projects + .0 + .into_iter() + .map(|p| (p.id.clone(), p)) + .collect(); + self.threads = snapshot + .threads + .0 + .into_iter() + .map(|t| (t.id.clone(), t)) + .collect(); + self.sequence = snapshot.snapshot_sequence; + self.seeded = true; + true + } + Item::Variant2 { + sequence, project, .. + } => { + if !self.advance(sequence) { + return false; + } + self.projects.insert(project.id.clone(), project); + true + } + Item::Variant3 { + sequence, + project_id, + .. + } => { + if !self.advance(sequence) { + return false; + } + self.projects.remove(&project_id); + true + } + Item::Variant4 { + sequence, + location, + thread, + .. + } => { + if !self.advance(sequence) { + return false; + } + match location { + Location::Active => { + self.threads.insert(thread.id.clone(), thread); + } + // Archived threads leave the normal shell; older servers still send these. + Location::Archive => { + self.threads.remove(&thread.id); + } + } + true + } + Item::Variant5 { + sequence, + thread_id, + location, + .. + } => { + if !self.advance(sequence) { + return false; + } + let _: RemovedLocation = location; + self.threads.remove(&thread_id); + true + } + } + } + + /// Events at or below the last applied sequence are stale. + fn advance(&mut self, sequence: i64) -> bool { + if sequence <= self.sequence { + return false; + } + self.sequence = sequence; + true + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::Value; + + // Encoded with the contracts' own schema from packages/client-runtime's shell test fixtures. + const SNAPSHOT: &str = include_str!("../tests/fixtures/shell-snapshot.json"); + const PROJECT_UPDATED: &str = include_str!("../tests/fixtures/shell-project-updated.json"); + const THREAD_UPDATED: &str = include_str!("../tests/fixtures/shell-thread-updated.json"); + + fn item(text: &str) -> Item { + serde_json::from_str(text).expect("fixture decodes") + } + + fn with(text: &str, edit: impl FnOnce(&mut Value)) -> Item { + let mut value: Value = serde_json::from_str(text).expect("json"); + edit(&mut value); + serde_json::from_value(value).expect("edited fixture decodes") + } + + fn seeded() -> ShellState { + let mut state = ShellState::default(); + assert!(!state.is_seeded()); + state.apply(item(SNAPSHOT)); + state + } + + #[test] + fn snapshot_seeds_counts() { + let state = seeded(); + assert!(state.is_seeded()); + assert_eq!((state.project_count(), state.thread_count()), (1, 1)); + } + + #[test] + fn updates_upsert_and_removes_delete() { + let mut state = seeded(); + // A renamed existing project does not add a row. + assert!(state.apply(item(PROJECT_UPDATED))); + assert_eq!(state.project_count(), 1); + assert!(state.apply(item(THREAD_UPDATED))); + assert_eq!(state.thread_count(), 2); + + let removed_thread = serde_json::json!({ + "kind": "thread.removed", "sequence": 8, "location": "active", "threadId": "thread-two" + }); + assert!(state.apply(serde_json::from_value(removed_thread).unwrap())); + assert_eq!(state.thread_count(), 1); + + let removed_project = serde_json::json!({ + "kind": "project.removed", "sequence": 9, "projectId": "project-v2" + }); + assert!(state.apply(serde_json::from_value(removed_project).unwrap())); + assert_eq!(state.project_count(), 0); + } + + #[test] + fn new_project_id_adds_a_row() { + let mut state = seeded(); + state.apply(with(PROJECT_UPDATED, |value| { + value["project"]["id"] = "project-two".into() + })); + assert_eq!(state.project_count(), 2); + } + + #[test] + fn events_at_or_below_the_last_sequence_are_ignored() { + let mut state = seeded(); + for sequence in [4, 5] { + let stale = with(THREAD_UPDATED, |value| value["sequence"] = sequence.into()); + assert!(!state.apply(stale)); + } + assert_eq!(state.thread_count(), 1); + assert!(state.apply(item(THREAD_UPDATED))); + // Replaying the same sequence is stale now. + assert!(!state.apply(item(THREAD_UPDATED))); + assert_eq!(state.thread_count(), 2); + } + + #[test] + fn archive_located_thread_updates_leave_the_shell() { + let mut state = seeded(); + state.apply(item(THREAD_UPDATED)); + let archived = with(THREAD_UPDATED, |value| { + value["sequence"] = 8.into(); + value["location"] = "archive".into(); + }); + assert!(state.apply(archived)); + assert_eq!(state.thread_count(), 1); + } + + #[test] + fn a_later_snapshot_replaces_everything() { + let mut state = seeded(); + state.apply(item(THREAD_UPDATED)); + state.apply(item(SNAPSHOT)); + assert_eq!(state.thread_count(), 1); + } +} diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index 9e7cbd99470fb..fb5526b2b966c 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -6,17 +6,47 @@ pub enum Status { NotConnected, Starting, Connecting, - Connected(String), + /// Socket is up. Counts stay `None` until the first shell snapshot lands. + Connected { + label: String, + counts: Option, + }, Failed(String), } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Counts { + pub projects: usize, + pub threads: usize, +} + +fn plural(count: usize, noun: &str) -> String { + if count == 1 { + noun.to_owned() + } else { + format!("{noun}s") + } +} + impl fmt::Display for Status { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::NotConnected => f.write_str("Not connected"), Self::Starting => f.write_str("Starting server..."), Self::Connecting => f.write_str("Connecting..."), - Self::Connected(label) => write!(f, "Connected to {label}"), + Self::Connected { + label, + counts: None, + } => write!(f, "Connected to {label}"), + Self::Connected { + label, + counts: Some(Counts { projects, threads }), + } => write!( + f, + "Connected to {label} \u{b7} {projects} {} \u{b7} {threads} {}", + plural(*projects, "project"), + plural(*threads, "thread"), + ), Self::Failed(message) => f.write_str(message), } } @@ -28,3 +58,40 @@ pub fn publish(sender: &StatusSender, status: Status) { // Unbounded, so this only fails once the window is gone. let _ = sender.try_send(status); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn connected_text_includes_counts_once_known() { + let label = "Local".to_owned(); + let waiting = Status::Connected { + label: label.clone(), + counts: None, + }; + assert_eq!(waiting.to_string(), "Connected to Local"); + let known = Status::Connected { + label: label.clone(), + counts: Some(Counts { + projects: 2, + threads: 14, + }), + }; + assert_eq!( + known.to_string(), + "Connected to Local \u{b7} 2 projects \u{b7} 14 threads" + ); + let single = Status::Connected { + label, + counts: Some(Counts { + projects: 1, + threads: 1, + }), + }; + assert_eq!( + single.to_string(), + "Connected to Local \u{b7} 1 project \u{b7} 1 thread" + ); + } +} diff --git a/apps/desktop-gpui/src/wire/generated.rs b/apps/desktop-gpui/src/wire/generated.rs new file mode 100644 index 0000000000000..829e3bf68a6d9 --- /dev/null +++ b/apps/desktop-gpui/src/wire/generated.rs @@ -0,0 +1,4058 @@ +// @generated by `cargo test --test wire_types` from packages/contracts/schema/rpc.schema.json. +// Do not edit. Regenerate with T3_UPDATE_GENERATED=1 cargo test --test wire_types. + +///`Author` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct Author { + #[serde( + rename = "avatarUrl", + deserialize_with = "::std::option::Option::deserialize" + )] + pub avatar_url: ::std::option::Option<::std::string::String>, + #[serde(rename = "isBot", skip_serializing_if = "::std::option::Option::is_none")] + pub is_bot: ::std::option::Option, + pub login: ::std::string::String, + #[serde(deserialize_with = "::std::option::Option::deserialize")] + pub name: ::std::option::Option<::std::string::String>, +} +///`Counts` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(transparent)] +pub struct Counts(pub ::std::vec::Vec); +impl ::std::ops::Deref for Counts { + type Target = ::std::vec::Vec; + fn deref(&self) -> &::std::vec::Vec { + &self.0 + } +} +impl ::std::convert::From for ::std::vec::Vec { + fn from(value: Counts) -> Self { + value.0 + } +} +impl ::std::convert::From<::std::vec::Vec> for Counts { + fn from(value: ::std::vec::Vec) -> Self { + Self(value) + } +} +///`CountsItem` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct CountsItem { + pub count: i64, + #[serde(rename = "projectId")] + pub project_id: ::std::string::String, +} +///`DefaultProjectScripts` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(transparent)] +pub struct DefaultProjectScripts(pub ::std::vec::Vec); +impl ::std::ops::Deref for DefaultProjectScripts { + type Target = ::std::vec::Vec; + fn deref(&self) -> &::std::vec::Vec { + &self.0 + } +} +impl ::std::convert::From +for ::std::vec::Vec { + fn from(value: DefaultProjectScripts) -> Self { + value.0 + } +} +impl ::std::convert::From<::std::vec::Vec> +for DefaultProjectScripts { + fn from(value: ::std::vec::Vec) -> Self { + Self(value) + } +} +///`DefaultProjectScriptsItem` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct DefaultProjectScriptsItem { + #[serde(rename = "async", skip_serializing_if = "::std::option::Option::is_none")] + pub async_: ::std::option::Option, + #[serde( + rename = "autoOpenPreview", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub auto_open_preview: ::std::option::Option, + pub command: ::std::string::String, + pub icon: DefaultProjectScriptsItemIcon, + pub id: ::std::string::String, + pub name: ::std::string::String, + #[serde( + rename = "previewUrl", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub preview_url: ::std::option::Option<::std::string::String>, + #[serde( + rename = "runOnSettle", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub run_on_settle: ::std::option::Option, + #[serde(rename = "runOnWorktreeCreate")] + pub run_on_worktree_create: bool, +} +///`DefaultProjectScriptsItemIcon` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum DefaultProjectScriptsItemIcon { + #[serde(rename = "play")] + Play, + #[serde(rename = "test")] + Test, + #[serde(rename = "lint")] + Lint, + #[serde(rename = "configure")] + Configure, + #[serde(rename = "build")] + Build, + #[serde(rename = "debug")] + Debug, +} +impl ::std::fmt::Display for DefaultProjectScriptsItemIcon { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Play => f.write_str("play"), + Self::Test => f.write_str("test"), + Self::Lint => f.write_str("lint"), + Self::Configure => f.write_str("configure"), + Self::Build => f.write_str("build"), + Self::Debug => f.write_str("debug"), + } + } +} +impl ::std::str::FromStr for DefaultProjectScriptsItemIcon { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "play" => Ok(Self::Play), + "test" => Ok(Self::Test), + "lint" => Ok(Self::Lint), + "configure" => Ok(Self::Configure), + "build" => Ok(Self::Build), + "debug" => Ok(Self::Debug), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for DefaultProjectScriptsItemIcon { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for DefaultProjectScriptsItemIcon { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`EndedWatch` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct EndedWatch { + #[serde( + rename = "checksAwaitedSince", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub checks_awaited_since: ::std::option::Option<::std::string::String>, + pub conflicting: bool, + #[serde(rename = "failedChecks")] + pub failed_checks: ::std::vec::Vec<::std::string::String>, + #[serde(rename = "headSha", deserialize_with = "::std::option::Option::deserialize")] + pub head_sha: ::std::option::Option<::std::string::String>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub instructions: ::std::option::Option<::std::string::String>, + #[serde( + rename = "owedChecks", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub owed_checks: ::std::option::Option<::std::vec::Vec<::std::string::String>>, + pub passed: bool, + #[serde( + rename = "passedChecks", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub passed_checks: ::std::option::Option<::std::vec::Vec<::std::string::String>>, + #[serde(rename = "remarkIds")] + pub remark_ids: ::std::vec::Vec<::std::string::String>, + #[serde(rename = "remarksThrough")] + pub remarks_through: ::std::string::String, + #[serde(rename = "startedAt")] + pub started_at: ::std::string::String, + pub wakes: i64, +} +///`EnvironmentAuthorizationErrorEncoded` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct EnvironmentAuthorizationErrorEncoded { + pub message: ::std::string::String, + #[serde( + rename = "requiredPermission", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub required_permission: ::std::option::Option<::std::string::String>, + #[serde(rename = "requiredScope")] + pub required_scope: PermissionsEntry, + #[serde(rename = "_tag")] + pub tag: EnvironmentAuthorizationErrorEncodedTag, +} +///`EnvironmentAuthorizationErrorEncodedTag` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum EnvironmentAuthorizationErrorEncodedTag { + EnvironmentAuthorizationError, +} +impl ::std::fmt::Display for EnvironmentAuthorizationErrorEncodedTag { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::EnvironmentAuthorizationError => { + f.write_str("EnvironmentAuthorizationError") + } + } + } +} +impl ::std::str::FromStr for EnvironmentAuthorizationErrorEncodedTag { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "EnvironmentAuthorizationError" => Ok(Self::EnvironmentAuthorizationError), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for EnvironmentAuthorizationErrorEncodedTag { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for EnvironmentAuthorizationErrorEncodedTag { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ForkedFrom` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ForkedFrom { + #[serde(rename = "providerThreadId")] + pub provider_thread_id: ::std::string::String, + #[serde( + rename = "providerTurnId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub provider_turn_id: ::std::option::Option<::std::string::String>, + #[serde(rename = "type")] + pub type_: ForkedFromType, +} +///`ForkedFromType` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ForkedFromType { + #[serde(rename = "provider_thread")] + ProviderThread, +} +impl ::std::fmt::Display for ForkedFromType { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ProviderThread => f.write_str("provider_thread"), + } + } +} +impl ::std::str::FromStr for ForkedFromType { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "provider_thread" => Ok(Self::ProviderThread), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ForkedFromType { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ForkedFromType { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`Goal` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct Goal { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub checks: ::std::option::Option, + #[serde( + rename = "lastCheck", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub last_check: ::std::option::Option<::std::string::String>, + pub objective: ::std::string::String, + pub status: GoalStatus, + #[serde( + rename = "timeUsedSeconds", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub time_used_seconds: ::std::option::Option, + #[serde( + rename = "tokenBudget", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub token_budget: ::std::option::Option, + #[serde( + rename = "tokensUsed", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub tokens_used: ::std::option::Option, +} +///`GoalStatus` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum GoalStatus { + #[serde(rename = "active")] + Active, + #[serde(rename = "paused")] + Paused, + #[serde(rename = "blocked")] + Blocked, + #[serde(rename = "usage_limited")] + UsageLimited, + #[serde(rename = "budget_limited")] + BudgetLimited, + #[serde(rename = "complete")] + Complete, +} +impl ::std::fmt::Display for GoalStatus { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Active => f.write_str("active"), + Self::Paused => f.write_str("paused"), + Self::Blocked => f.write_str("blocked"), + Self::UsageLimited => f.write_str("usage_limited"), + Self::BudgetLimited => f.write_str("budget_limited"), + Self::Complete => f.write_str("complete"), + } + } +} +impl ::std::str::FromStr for GoalStatus { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "active" => Ok(Self::Active), + "paused" => Ok(Self::Paused), + "blocked" => Ok(Self::Blocked), + "usage_limited" => Ok(Self::UsageLimited), + "budget_limited" => Ok(Self::BudgetLimited), + "complete" => Ok(Self::Complete), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for GoalStatus { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for GoalStatus { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`LimitRecovery` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct LimitRecovery { + #[serde(rename = "autoResume")] + pub auto_resume: bool, + #[serde( + rename = "requestId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub request_id: ::std::option::Option<::std::string::String>, + #[serde(rename = "resetAt")] + pub reset_at: ::std::string::String, + #[serde(rename = "runId")] + pub run_id: ::std::string::String, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub snooze: ::std::option::Option, +} +///`Lineage` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct Lineage { + #[serde( + rename = "parentThreadId", + deserialize_with = "::std::option::Option::deserialize" + )] + pub parent_thread_id: ::std::option::Option<::std::string::String>, + #[serde( + rename = "relationshipToParent", + deserialize_with = "::std::option::Option::deserialize" + )] + pub relationship_to_parent: ::std::option::Option, + #[serde(rename = "rootThreadId")] + pub root_thread_id: ::std::string::String, + #[serde(rename = "sideChat", skip_serializing_if = "::std::option::Option::is_none")] + pub side_chat: ::std::option::Option, +} +///`LineageRelationshipToParent` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum LineageRelationshipToParent { + #[serde(rename = "fork")] + Fork, + #[serde(rename = "subagent")] + Subagent, +} +impl ::std::fmt::Display for LineageRelationshipToParent { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Fork => f.write_str("fork"), + Self::Subagent => f.write_str("subagent"), + } + } +} +impl ::std::str::FromStr for LineageRelationshipToParent { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "fork" => Ok(Self::Fork), + "subagent" => Ok(Self::Subagent), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for LineageRelationshipToParent { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for LineageRelationshipToParent { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`LinkOrigin` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct LinkOrigin { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub access: ::std::option::Option, + pub label: ::std::string::String, + #[serde(rename = "sessionId")] + pub session_id: ::std::string::String, +} +///`LinkOriginAccess` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum LinkOriginAccess { + #[serde(rename = "read-only")] + ReadOnly, + #[serde(rename = "approval-required")] + ApprovalRequired, + #[serde(rename = "auto-accept-edits")] + AutoAcceptEdits, + #[serde(rename = "auto")] + Auto, + #[serde(rename = "full-access")] + FullAccess, +} +impl ::std::fmt::Display for LinkOriginAccess { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ReadOnly => f.write_str("read-only"), + Self::ApprovalRequired => f.write_str("approval-required"), + Self::AutoAcceptEdits => f.write_str("auto-accept-edits"), + Self::Auto => f.write_str("auto"), + Self::FullAccess => f.write_str("full-access"), + } + } +} +impl ::std::str::FromStr for LinkOriginAccess { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "read-only" => Ok(Self::ReadOnly), + "approval-required" => Ok(Self::ApprovalRequired), + "auto-accept-edits" => Ok(Self::AutoAcceptEdits), + "auto" => Ok(Self::Auto), + "full-access" => Ok(Self::FullAccess), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for LinkOriginAccess { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for LinkOriginAccess { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`LinkedPullRequest` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct LinkedPullRequest { + pub number: ::std::num::NonZeroU64, + #[serde(rename = "projectId")] + pub project_id: ::std::string::String, + pub repository: ::std::string::String, + pub url: ::std::string::String, +} +///`ModelSelection` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ModelSelection { + #[serde( + rename = "instanceId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub instance_id: ::std::option::Option<::serde_json::Value>, + pub model: ::serde_json::Value, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub options: ::std::option::Option<::serde_json::Value>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub provider: ::std::option::Option<::serde_json::Value>, +} +///`Opens` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct Opens { + #[serde(rename = "openedAt")] + pub opened_at: f64, + pub picks: ::std::vec::Vec, + pub score: f64, +} +///`OpensPicksItem` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct OpensPicksItem { + pub at: f64, + pub query: OpensPicksItemQuery, +} +///`OpensPicksItemQuery` +#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[serde(transparent)] +pub struct OpensPicksItemQuery(::std::string::String); +impl ::std::ops::Deref for OpensPicksItemQuery { + type Target = ::std::string::String; + fn deref(&self) -> &::std::string::String { + &self.0 + } +} +impl ::std::convert::From for ::std::string::String { + fn from(value: OpensPicksItemQuery) -> Self { + value.0 + } +} +impl ::std::str::FromStr for OpensPicksItemQuery { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + ::std::convert::TryFrom::try_from(value) + } +} +impl ::std::convert::TryFrom<&str> for OpensPicksItemQuery { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + if value.chars().count() > 32usize { + return Err("longer than 32 characters".into()); + } + Ok(Self(value.to_string())) + } +} +impl ::std::convert::TryFrom<::std::string::String> for OpensPicksItemQuery { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + ::std::convert::TryFrom::try_from(value.as_str()) + } +} +impl<'de> ::serde::Deserialize<'de> for OpensPicksItemQuery { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: ::serde::Deserializer<'de>, + { + ::std::convert::TryFrom::try_from( + ::std::string::String::deserialize(deserializer)?, + ) + .map_err(|e: self::error::ConversionError| { + ::custom(e.to_string()) + }) + } +} +///`OrchestrationGetArchivedShellSnapshotError` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum OrchestrationGetArchivedShellSnapshotError { + OrchestrationV2GetShellSnapshotErrorEncoded( + OrchestrationV2GetShellSnapshotErrorEncoded, + ), + EnvironmentAuthorizationErrorEncoded(EnvironmentAuthorizationErrorEncoded), +} +impl ::std::convert::From +for OrchestrationGetArchivedShellSnapshotError { + fn from(value: OrchestrationV2GetShellSnapshotErrorEncoded) -> Self { + Self::OrchestrationV2GetShellSnapshotErrorEncoded(value) + } +} +impl ::std::convert::From +for OrchestrationGetArchivedShellSnapshotError { + fn from(value: EnvironmentAuthorizationErrorEncoded) -> Self { + Self::EnvironmentAuthorizationErrorEncoded(value) + } +} +///`OrchestrationSubscribeShellPayload` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, Default)] +pub struct OrchestrationSubscribeShellPayload { + #[serde( + rename = "afterSequence", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub after_sequence: ::std::option::Option, + #[serde( + rename = "omitIdleSubagents", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub omit_idle_subagents: ::std::option::Option, + #[serde( + rename = "omitOlderSettled", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub omit_older_settled: ::std::option::Option, + #[serde( + rename = "requestCompletionMarker", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub request_completion_marker: ::std::option::Option, +} +///`OrchestrationSubscribeShellSuccess` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum OrchestrationSubscribeShellSuccess { + Variant0 { kind: OrchestrationSubscribeShellSuccessVariant0Kind }, + Variant1 { + kind: OrchestrationSubscribeShellSuccessVariant1Kind, + #[serde( + rename = "resolvedRepositoryIdentityRoots", + default, + skip_serializing_if = "::std::vec::Vec::is_empty" + )] + resolved_repository_identity_roots: ::std::vec::Vec<::std::string::String>, + snapshot: OrchestrationSubscribeShellSuccessVariant1Snapshot, + }, + Variant2 { + kind: OrchestrationSubscribeShellSuccessVariant2Kind, + project: ProjectsEntry, + sequence: i64, + }, + Variant3 { + kind: OrchestrationSubscribeShellSuccessVariant3Kind, + #[serde(rename = "projectId")] + project_id: ::std::string::String, + sequence: i64, + }, + Variant4 { + kind: OrchestrationSubscribeShellSuccessVariant4Kind, + location: OrchestrationSubscribeShellSuccessVariant4Location, + sequence: i64, + thread: ThreadsEntry, + }, + Variant5 { + kind: OrchestrationSubscribeShellSuccessVariant5Kind, + location: OrchestrationSubscribeShellSuccessVariant5Location, + sequence: i64, + #[serde(rename = "threadId")] + thread_id: ::std::string::String, + }, +} +///`OrchestrationSubscribeShellSuccessVariant0Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant0Kind { + #[serde(rename = "synchronized")] + Synchronized, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant0Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Synchronized => f.write_str("synchronized"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant0Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "synchronized" => Ok(Self::Synchronized), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for OrchestrationSubscribeShellSuccessVariant0Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant0Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationSubscribeShellSuccessVariant1Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant1Kind { + #[serde(rename = "snapshot")] + Snapshot, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant1Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Snapshot => f.write_str("snapshot"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant1Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "snapshot" => Ok(Self::Snapshot), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for OrchestrationSubscribeShellSuccessVariant1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationSubscribeShellSuccessVariant1Snapshot` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct OrchestrationSubscribeShellSuccessVariant1Snapshot { + #[serde(rename = "archivedThreads")] + pub archived_threads: Threads, + #[serde( + rename = "olderSettled", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub older_settled: ::std::option::Option< + OrchestrationSubscribeShellSuccessVariant1SnapshotOlderSettled, + >, + pub projects: Projects, + #[serde(rename = "schemaVersion")] + pub schema_version: ::std::num::NonZeroU64, + #[serde(rename = "snapshotSequence")] + pub snapshot_sequence: i64, + pub threads: Threads, +} +///`OrchestrationSubscribeShellSuccessVariant1SnapshotOlderSettled` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct OrchestrationSubscribeShellSuccessVariant1SnapshotOlderSettled { + #[serde(rename = "asOfSequence")] + pub as_of_sequence: i64, + pub before: ::std::string::String, + pub counts: Counts, +} +///`OrchestrationSubscribeShellSuccessVariant2Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant2Kind { + #[serde(rename = "project.updated")] + ProjectUpdated, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant2Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ProjectUpdated => f.write_str("project.updated"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant2Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "project.updated" => Ok(Self::ProjectUpdated), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for OrchestrationSubscribeShellSuccessVariant2Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant2Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationSubscribeShellSuccessVariant3Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant3Kind { + #[serde(rename = "project.removed")] + ProjectRemoved, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant3Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ProjectRemoved => f.write_str("project.removed"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant3Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "project.removed" => Ok(Self::ProjectRemoved), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for OrchestrationSubscribeShellSuccessVariant3Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant3Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationSubscribeShellSuccessVariant4Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant4Kind { + #[serde(rename = "thread.updated")] + ThreadUpdated, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant4Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ThreadUpdated => f.write_str("thread.updated"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant4Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "thread.updated" => Ok(Self::ThreadUpdated), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for OrchestrationSubscribeShellSuccessVariant4Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant4Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationSubscribeShellSuccessVariant4Location` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant4Location { + #[serde(rename = "active")] + Active, + #[serde(rename = "archive")] + Archive, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant4Location { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Active => f.write_str("active"), + Self::Archive => f.write_str("archive"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant4Location { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "active" => Ok(Self::Active), + "archive" => Ok(Self::Archive), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> +for OrchestrationSubscribeShellSuccessVariant4Location { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant4Location { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationSubscribeShellSuccessVariant5Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant5Kind { + #[serde(rename = "thread.removed")] + ThreadRemoved, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant5Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ThreadRemoved => f.write_str("thread.removed"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant5Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "thread.removed" => Ok(Self::ThreadRemoved), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for OrchestrationSubscribeShellSuccessVariant5Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant5Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationSubscribeShellSuccessVariant5Location` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationSubscribeShellSuccessVariant5Location { + #[serde(rename = "active")] + Active, + #[serde(rename = "archive")] + Archive, +} +impl ::std::fmt::Display for OrchestrationSubscribeShellSuccessVariant5Location { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Active => f.write_str("active"), + Self::Archive => f.write_str("archive"), + } + } +} +impl ::std::str::FromStr for OrchestrationSubscribeShellSuccessVariant5Location { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "active" => Ok(Self::Active), + "archive" => Ok(Self::Archive), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> +for OrchestrationSubscribeShellSuccessVariant5Location { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationSubscribeShellSuccessVariant5Location { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`OrchestrationV2GetShellSnapshotErrorEncoded` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct OrchestrationV2GetShellSnapshotErrorEncoded { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub cause: ::std::option::Option<::serde_json::Value>, + pub message: ::std::string::String, + #[serde(rename = "_tag")] + pub tag: OrchestrationV2GetShellSnapshotErrorEncodedTag, +} +///`OrchestrationV2GetShellSnapshotErrorEncodedTag` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum OrchestrationV2GetShellSnapshotErrorEncodedTag { + OrchestrationV2GetShellSnapshotError, +} +impl ::std::fmt::Display for OrchestrationV2GetShellSnapshotErrorEncodedTag { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::OrchestrationV2GetShellSnapshotError => { + f.write_str("OrchestrationV2GetShellSnapshotError") + } + } + } +} +impl ::std::str::FromStr for OrchestrationV2GetShellSnapshotErrorEncodedTag { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "OrchestrationV2GetShellSnapshotError" => { + Ok(Self::OrchestrationV2GetShellSnapshotError) + } + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for OrchestrationV2GetShellSnapshotErrorEncodedTag { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for OrchestrationV2GetShellSnapshotErrorEncodedTag { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`PendingBackgroundTasks` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(transparent)] +pub struct PendingBackgroundTasks(pub ::std::vec::Vec); +impl ::std::ops::Deref for PendingBackgroundTasks { + type Target = ::std::vec::Vec; + fn deref(&self) -> &::std::vec::Vec { + &self.0 + } +} +impl ::std::convert::From +for ::std::vec::Vec { + fn from(value: PendingBackgroundTasks) -> Self { + value.0 + } +} +impl ::std::convert::From<::std::vec::Vec> +for PendingBackgroundTasks { + fn from(value: ::std::vec::Vec) -> Self { + Self(value) + } +} +///`PendingBackgroundTasksItem` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, Default)] +pub struct PendingBackgroundTasksItem { + #[serde(flatten, skip_serializing_if = "::std::option::Option::is_none")] + pub subtype_0: ::std::option::Option, + #[serde(flatten, skip_serializing_if = "::std::option::Option::is_none")] + pub subtype_1: ::std::option::Option, + #[serde(flatten, skip_serializing_if = "::std::option::Option::is_none")] + pub subtype_2: ::std::option::Option, + #[serde(flatten, skip_serializing_if = "::std::option::Option::is_none")] + pub subtype_3: ::std::option::Option, + #[serde(flatten, skip_serializing_if = "::std::option::Option::is_none")] + pub subtype_4: ::std::option::Option, +} +///`PendingBackgroundTasksItemSubtype0` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct PendingBackgroundTasksItemSubtype0 { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub awaited: ::std::option::Option, + #[serde( + rename = "childThreadId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub child_thread_id: ::std::option::Option<::std::string::String>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub description: ::std::option::Option<::std::string::String>, + pub kind: PendingBackgroundTasksItemSubtype0Kind, + #[serde( + rename = "parentTaskId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub parent_task_id: ::std::option::Option<::std::string::String>, + #[serde( + rename = "providerInstanceId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub provider_instance_id: ::std::option::Option<::std::string::String>, + #[serde(rename = "taskId")] + pub task_id: ::std::string::String, +} +///`PendingBackgroundTasksItemSubtype0Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum PendingBackgroundTasksItemSubtype0Kind { + #[serde(rename = "subagent")] + Subagent, +} +impl ::std::fmt::Display for PendingBackgroundTasksItemSubtype0Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Subagent => f.write_str("subagent"), + } + } +} +impl ::std::str::FromStr for PendingBackgroundTasksItemSubtype0Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "subagent" => Ok(Self::Subagent), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for PendingBackgroundTasksItemSubtype0Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for PendingBackgroundTasksItemSubtype0Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`PendingBackgroundTasksItemSubtype1` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct PendingBackgroundTasksItemSubtype1 { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub awaited: ::std::option::Option, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub description: ::std::option::Option<::std::string::String>, + pub kind: PendingBackgroundTasksItemSubtype1Kind, + #[serde( + rename = "parentTaskId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub parent_task_id: ::std::option::Option<::std::string::String>, + #[serde(rename = "taskId")] + pub task_id: ::std::string::String, +} +///`PendingBackgroundTasksItemSubtype1Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum PendingBackgroundTasksItemSubtype1Kind { + #[serde(rename = "command")] + Command, +} +impl ::std::fmt::Display for PendingBackgroundTasksItemSubtype1Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Command => f.write_str("command"), + } + } +} +impl ::std::str::FromStr for PendingBackgroundTasksItemSubtype1Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "command" => Ok(Self::Command), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for PendingBackgroundTasksItemSubtype1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for PendingBackgroundTasksItemSubtype1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`PendingBackgroundTasksItemSubtype2` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct PendingBackgroundTasksItemSubtype2 { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub awaited: ::std::option::Option, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub description: ::std::option::Option<::std::string::String>, + pub kind: PendingBackgroundTasksItemSubtype2Kind, + #[serde( + rename = "parentTaskId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub parent_task_id: ::std::option::Option<::std::string::String>, + #[serde(rename = "taskId")] + pub task_id: ::std::string::String, +} +///`PendingBackgroundTasksItemSubtype2Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum PendingBackgroundTasksItemSubtype2Kind { + #[serde(rename = "monitor")] + Monitor, +} +impl ::std::fmt::Display for PendingBackgroundTasksItemSubtype2Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Monitor => f.write_str("monitor"), + } + } +} +impl ::std::str::FromStr for PendingBackgroundTasksItemSubtype2Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "monitor" => Ok(Self::Monitor), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for PendingBackgroundTasksItemSubtype2Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for PendingBackgroundTasksItemSubtype2Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`PendingBackgroundTasksItemSubtype3` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct PendingBackgroundTasksItemSubtype3 { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub awaited: ::std::option::Option, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub description: ::std::option::Option<::std::string::String>, + pub kind: PendingBackgroundTasksItemSubtype3Kind, + #[serde( + rename = "parentTaskId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub parent_task_id: ::std::option::Option<::std::string::String>, + #[serde(rename = "taskId")] + pub task_id: ::std::string::String, +} +///`PendingBackgroundTasksItemSubtype3Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum PendingBackgroundTasksItemSubtype3Kind { + #[serde(rename = "background_task")] + BackgroundTask, +} +impl ::std::fmt::Display for PendingBackgroundTasksItemSubtype3Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::BackgroundTask => f.write_str("background_task"), + } + } +} +impl ::std::str::FromStr for PendingBackgroundTasksItemSubtype3Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "background_task" => Ok(Self::BackgroundTask), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for PendingBackgroundTasksItemSubtype3Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for PendingBackgroundTasksItemSubtype3Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`PendingBackgroundTasksItemSubtype4` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct PendingBackgroundTasksItemSubtype4 { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub awaited: ::std::option::Option, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub description: ::std::option::Option<::std::string::String>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub kind: ::std::option::Option<::std::string::String>, + #[serde( + rename = "parentTaskId", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub parent_task_id: ::std::option::Option<::std::string::String>, + #[serde(rename = "taskId")] + pub task_id: ::std::string::String, +} +///`PermissionsEntry` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum PermissionsEntry { + #[serde(rename = "orchestration:read")] + OrchestrationRead, + #[serde(rename = "orchestration:operate")] + OrchestrationOperate, + #[serde(rename = "settings:write")] + SettingsWrite, + #[serde(rename = "providers:manage")] + ProvidersManage, + #[serde(rename = "environment:maintain")] + EnvironmentMaintain, + #[serde(rename = "preview:operate")] + PreviewOperate, + #[serde(rename = "diagnostics:read")] + DiagnosticsRead, + #[serde(rename = "terminal:read")] + TerminalRead, + #[serde(rename = "terminal:operate")] + TerminalOperate, + #[serde(rename = "filesystem:read")] + FilesystemRead, + #[serde(rename = "filesystem:write")] + FilesystemWrite, + #[serde(rename = "review:write")] + ReviewWrite, + #[serde(rename = "source-control:write")] + SourceControlWrite, + #[serde(rename = "access:read")] + AccessRead, + #[serde(rename = "access:write")] + AccessWrite, + #[serde(rename = "relay:read")] + RelayRead, + #[serde(rename = "relay:write")] + RelayWrite, +} +impl ::std::fmt::Display for PermissionsEntry { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::OrchestrationRead => f.write_str("orchestration:read"), + Self::OrchestrationOperate => f.write_str("orchestration:operate"), + Self::SettingsWrite => f.write_str("settings:write"), + Self::ProvidersManage => f.write_str("providers:manage"), + Self::EnvironmentMaintain => f.write_str("environment:maintain"), + Self::PreviewOperate => f.write_str("preview:operate"), + Self::DiagnosticsRead => f.write_str("diagnostics:read"), + Self::TerminalRead => f.write_str("terminal:read"), + Self::TerminalOperate => f.write_str("terminal:operate"), + Self::FilesystemRead => f.write_str("filesystem:read"), + Self::FilesystemWrite => f.write_str("filesystem:write"), + Self::ReviewWrite => f.write_str("review:write"), + Self::SourceControlWrite => f.write_str("source-control:write"), + Self::AccessRead => f.write_str("access:read"), + Self::AccessWrite => f.write_str("access:write"), + Self::RelayRead => f.write_str("relay:read"), + Self::RelayWrite => f.write_str("relay:write"), + } + } +} +impl ::std::str::FromStr for PermissionsEntry { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "orchestration:read" => Ok(Self::OrchestrationRead), + "orchestration:operate" => Ok(Self::OrchestrationOperate), + "settings:write" => Ok(Self::SettingsWrite), + "providers:manage" => Ok(Self::ProvidersManage), + "environment:maintain" => Ok(Self::EnvironmentMaintain), + "preview:operate" => Ok(Self::PreviewOperate), + "diagnostics:read" => Ok(Self::DiagnosticsRead), + "terminal:read" => Ok(Self::TerminalRead), + "terminal:operate" => Ok(Self::TerminalOperate), + "filesystem:read" => Ok(Self::FilesystemRead), + "filesystem:write" => Ok(Self::FilesystemWrite), + "review:write" => Ok(Self::ReviewWrite), + "source-control:write" => Ok(Self::SourceControlWrite), + "access:read" => Ok(Self::AccessRead), + "access:write" => Ok(Self::AccessWrite), + "relay:read" => Ok(Self::RelayRead), + "relay:write" => Ok(Self::RelayWrite), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for PermissionsEntry { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for PermissionsEntry { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectIcon` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ProjectIcon { + pub color: ProjectIconColor, + pub kind: ProjectIconKind, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub monogram: ::std::option::Option<::std::string::String>, + #[serde( + rename = "monogramText", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub monogram_text: ::std::option::Option<::std::string::String>, + pub name: ::std::string::String, +} +///`ProjectIcon2` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ProjectIcon2 { + pub color: ProjectIcon2Color, + pub kind: ProjectIcon2Kind, + pub text: ::std::string::String, +} +///`ProjectIcon2Color` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectIcon2Color { + #[serde(rename = "gray")] + Gray, + #[serde(rename = "red")] + Red, + #[serde(rename = "orange")] + Orange, + #[serde(rename = "amber")] + Amber, + #[serde(rename = "yellow")] + Yellow, + #[serde(rename = "lime")] + Lime, + #[serde(rename = "green")] + Green, + #[serde(rename = "emerald")] + Emerald, + #[serde(rename = "teal")] + Teal, + #[serde(rename = "cyan")] + Cyan, + #[serde(rename = "sky")] + Sky, + #[serde(rename = "blue")] + Blue, + #[serde(rename = "indigo")] + Indigo, + #[serde(rename = "violet")] + Violet, + #[serde(rename = "purple")] + Purple, + #[serde(rename = "fuchsia")] + Fuchsia, + #[serde(rename = "pink")] + Pink, + #[serde(rename = "rose")] + Rose, +} +impl ::std::fmt::Display for ProjectIcon2Color { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Gray => f.write_str("gray"), + Self::Red => f.write_str("red"), + Self::Orange => f.write_str("orange"), + Self::Amber => f.write_str("amber"), + Self::Yellow => f.write_str("yellow"), + Self::Lime => f.write_str("lime"), + Self::Green => f.write_str("green"), + Self::Emerald => f.write_str("emerald"), + Self::Teal => f.write_str("teal"), + Self::Cyan => f.write_str("cyan"), + Self::Sky => f.write_str("sky"), + Self::Blue => f.write_str("blue"), + Self::Indigo => f.write_str("indigo"), + Self::Violet => f.write_str("violet"), + Self::Purple => f.write_str("purple"), + Self::Fuchsia => f.write_str("fuchsia"), + Self::Pink => f.write_str("pink"), + Self::Rose => f.write_str("rose"), + } + } +} +impl ::std::str::FromStr for ProjectIcon2Color { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "gray" => Ok(Self::Gray), + "red" => Ok(Self::Red), + "orange" => Ok(Self::Orange), + "amber" => Ok(Self::Amber), + "yellow" => Ok(Self::Yellow), + "lime" => Ok(Self::Lime), + "green" => Ok(Self::Green), + "emerald" => Ok(Self::Emerald), + "teal" => Ok(Self::Teal), + "cyan" => Ok(Self::Cyan), + "sky" => Ok(Self::Sky), + "blue" => Ok(Self::Blue), + "indigo" => Ok(Self::Indigo), + "violet" => Ok(Self::Violet), + "purple" => Ok(Self::Purple), + "fuchsia" => Ok(Self::Fuchsia), + "pink" => Ok(Self::Pink), + "rose" => Ok(Self::Rose), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectIcon2Color { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ProjectIcon2Color { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectIcon2Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectIcon2Kind { + #[serde(rename = "monogram")] + Monogram, +} +impl ::std::fmt::Display for ProjectIcon2Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Monogram => f.write_str("monogram"), + } + } +} +impl ::std::str::FromStr for ProjectIcon2Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "monogram" => Ok(Self::Monogram), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectIcon2Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ProjectIcon2Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectIconColor` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectIconColor { + #[serde(rename = "gray")] + Gray, + #[serde(rename = "red")] + Red, + #[serde(rename = "orange")] + Orange, + #[serde(rename = "amber")] + Amber, + #[serde(rename = "yellow")] + Yellow, + #[serde(rename = "lime")] + Lime, + #[serde(rename = "green")] + Green, + #[serde(rename = "emerald")] + Emerald, + #[serde(rename = "teal")] + Teal, + #[serde(rename = "cyan")] + Cyan, + #[serde(rename = "sky")] + Sky, + #[serde(rename = "blue")] + Blue, + #[serde(rename = "indigo")] + Indigo, + #[serde(rename = "violet")] + Violet, + #[serde(rename = "purple")] + Purple, + #[serde(rename = "fuchsia")] + Fuchsia, + #[serde(rename = "pink")] + Pink, + #[serde(rename = "rose")] + Rose, +} +impl ::std::fmt::Display for ProjectIconColor { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Gray => f.write_str("gray"), + Self::Red => f.write_str("red"), + Self::Orange => f.write_str("orange"), + Self::Amber => f.write_str("amber"), + Self::Yellow => f.write_str("yellow"), + Self::Lime => f.write_str("lime"), + Self::Green => f.write_str("green"), + Self::Emerald => f.write_str("emerald"), + Self::Teal => f.write_str("teal"), + Self::Cyan => f.write_str("cyan"), + Self::Sky => f.write_str("sky"), + Self::Blue => f.write_str("blue"), + Self::Indigo => f.write_str("indigo"), + Self::Violet => f.write_str("violet"), + Self::Purple => f.write_str("purple"), + Self::Fuchsia => f.write_str("fuchsia"), + Self::Pink => f.write_str("pink"), + Self::Rose => f.write_str("rose"), + } + } +} +impl ::std::str::FromStr for ProjectIconColor { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "gray" => Ok(Self::Gray), + "red" => Ok(Self::Red), + "orange" => Ok(Self::Orange), + "amber" => Ok(Self::Amber), + "yellow" => Ok(Self::Yellow), + "lime" => Ok(Self::Lime), + "green" => Ok(Self::Green), + "emerald" => Ok(Self::Emerald), + "teal" => Ok(Self::Teal), + "cyan" => Ok(Self::Cyan), + "sky" => Ok(Self::Sky), + "blue" => Ok(Self::Blue), + "indigo" => Ok(Self::Indigo), + "violet" => Ok(Self::Violet), + "purple" => Ok(Self::Purple), + "fuchsia" => Ok(Self::Fuchsia), + "pink" => Ok(Self::Pink), + "rose" => Ok(Self::Rose), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectIconColor { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ProjectIconColor { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectIconKind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectIconKind { + #[serde(rename = "lucide")] + Lucide, +} +impl ::std::fmt::Display for ProjectIconKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Lucide => f.write_str("lucide"), + } + } +} +impl ::std::str::FromStr for ProjectIconKind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "lucide" => Ok(Self::Lucide), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectIconKind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ProjectIconKind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`Projects` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(transparent)] +pub struct Projects(pub ::std::vec::Vec); +impl ::std::ops::Deref for Projects { + type Target = ::std::vec::Vec; + fn deref(&self) -> &::std::vec::Vec { + &self.0 + } +} +impl ::std::convert::From for ::std::vec::Vec { + fn from(value: Projects) -> Self { + value.0 + } +} +impl ::std::convert::From<::std::vec::Vec> for Projects { + fn from(value: ::std::vec::Vec) -> Self { + Self(value) + } +} +///`ProjectsEntry` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ProjectsEntry { + #[serde( + rename = "archivedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub archived_at: ::std::option::Option<::std::string::String>, + #[serde(rename = "autoPull", skip_serializing_if = "::std::option::Option::is_none")] + pub auto_pull: ::std::option::Option, + #[serde(rename = "createdAt")] + pub created_at: ::std::string::String, + #[serde( + rename = "defaultModelSelection", + deserialize_with = "::std::option::Option::deserialize" + )] + pub default_model_selection: ::std::option::Option, + #[serde( + rename = "defaultThreadEnvMode", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub default_thread_env_mode: ::std::option::Option< + ProjectsEntryDefaultThreadEnvMode, + >, + #[serde( + rename = "faviconPath", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub favicon_path: ::std::option::Option<::std::string::String>, + pub id: ::std::string::String, + #[serde(rename = "pinnedAt", skip_serializing_if = "::std::option::Option::is_none")] + pub pinned_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "projectIcon", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub project_icon: ::std::option::Option<::serde_json::Value>, + #[serde( + rename = "repositoryIdentity", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub repository_identity: ::std::option::Option, + pub scripts: DefaultProjectScripts, + pub title: ::std::string::String, + #[serde(rename = "updatedAt")] + pub updated_at: ::std::string::String, + #[serde(rename = "workspaceRoot")] + pub workspace_root: ::std::string::String, +} +///`ProjectsEntryDefaultThreadEnvMode` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsEntryDefaultThreadEnvMode { + #[serde(rename = "local")] + Local, + #[serde(rename = "worktree")] + Worktree, +} +impl ::std::fmt::Display for ProjectsEntryDefaultThreadEnvMode { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Local => f.write_str("local"), + Self::Worktree => f.write_str("worktree"), + } + } +} +impl ::std::str::FromStr for ProjectsEntryDefaultThreadEnvMode { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "local" => Ok(Self::Local), + "worktree" => Ok(Self::Worktree), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectsEntryDefaultThreadEnvMode { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsEntryDefaultThreadEnvMode { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`PullRequests` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(transparent)] +pub struct PullRequests(pub ::std::vec::Vec); +impl ::std::ops::Deref for PullRequests { + type Target = ::std::vec::Vec; + fn deref(&self) -> &::std::vec::Vec { + &self.0 + } +} +impl ::std::convert::From for ::std::vec::Vec { + fn from(value: PullRequests) -> Self { + value.0 + } +} +impl ::std::convert::From<::std::vec::Vec> for PullRequests { + fn from(value: ::std::vec::Vec) -> Self { + Self(value) + } +} +///`PullRequestsItem` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct PullRequestsItem { + #[serde( + rename = "endedWatch", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub ended_watch: ::std::option::Option, + pub host: ::std::string::String, + #[serde(rename = "linkedAt")] + pub linked_at: ::std::string::String, + pub number: ::std::num::NonZeroU64, + pub repository: ::std::string::String, + #[serde( + rename = "repositoryWebUrl", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub repository_web_url: ::std::option::Option<::std::string::String>, + #[serde(deserialize_with = "::std::option::Option::deserialize")] + pub snapshot: ::std::option::Option, + pub source: PullRequestsItemSource, + #[serde(deserialize_with = "::std::option::Option::deserialize")] + pub stack: ::std::option::Option, + pub url: ::std::string::String, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub watch: ::std::option::Option, +} +///`PullRequestsItemSource` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum PullRequestsItemSource { + #[serde(rename = "manual")] + Manual, + #[serde(rename = "created")] + Created, + #[serde(rename = "agent")] + Agent, + #[serde(rename = "stack")] + Stack, + #[serde(rename = "stack-dismissed")] + StackDismissed, +} +impl ::std::fmt::Display for PullRequestsItemSource { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Manual => f.write_str("manual"), + Self::Created => f.write_str("created"), + Self::Agent => f.write_str("agent"), + Self::Stack => f.write_str("stack"), + Self::StackDismissed => f.write_str("stack-dismissed"), + } + } +} +impl ::std::str::FromStr for PullRequestsItemSource { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "manual" => Ok(Self::Manual), + "created" => Ok(Self::Created), + "agent" => Ok(Self::Agent), + "stack" => Ok(Self::Stack), + "stack-dismissed" => Ok(Self::StackDismissed), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for PullRequestsItemSource { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for PullRequestsItemSource { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`RepositoryIdentity` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct RepositoryIdentity { + #[serde(rename = "canonicalKey")] + pub canonical_key: ::std::string::String, + #[serde( + rename = "displayName", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub display_name: ::std::option::Option<::std::string::String>, + pub locator: RepositoryIdentityLocator, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub name: ::std::option::Option<::std::string::String>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub origin: ::std::option::Option, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub owner: ::std::option::Option<::std::string::String>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub provider: ::std::option::Option<::std::string::String>, + #[serde(rename = "rootPath", skip_serializing_if = "::std::option::Option::is_none")] + pub root_path: ::std::option::Option<::std::string::String>, + #[serde(rename = "webUrl", skip_serializing_if = "::std::option::Option::is_none")] + pub web_url: ::std::option::Option<::std::string::String>, +} +///`RepositoryIdentityLocator` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct RepositoryIdentityLocator { + #[serde(rename = "remoteName")] + pub remote_name: ::std::string::String, + #[serde(rename = "remoteUrl")] + pub remote_url: ::std::string::String, + pub source: RepositoryIdentityLocatorSource, +} +///`RepositoryIdentityLocatorSource` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum RepositoryIdentityLocatorSource { + #[serde(rename = "git-remote")] + GitRemote, +} +impl ::std::fmt::Display for RepositoryIdentityLocatorSource { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::GitRemote => f.write_str("git-remote"), + } + } +} +impl ::std::str::FromStr for RepositoryIdentityLocatorSource { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "git-remote" => Ok(Self::GitRemote), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for RepositoryIdentityLocatorSource { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for RepositoryIdentityLocatorSource { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`RepositoryIdentityOrigin` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct RepositoryIdentityOrigin { + #[serde(rename = "canonicalKey")] + pub canonical_key: ::std::string::String, + #[serde( + rename = "displayName", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub display_name: ::std::option::Option<::std::string::String>, + #[serde( + rename = "remoteUrl", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub remote_url: ::std::option::Option<::std::string::String>, +} +///`Snapshot` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct Snapshot { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub additions: ::std::option::Option, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub author: ::std::option::Option, + #[serde(rename = "baseBranch")] + pub base_branch: ::std::string::String, + #[serde( + rename = "changedFiles", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub changed_files: ::std::option::Option, + #[serde( + rename = "checksState", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub checks_state: ::std::option::Option, + #[serde(rename = "closedAt", skip_serializing_if = "::std::option::Option::is_none")] + pub closed_at: ::std::option::Option<::std::string::String>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub deletions: ::std::option::Option, + #[serde(rename = "headBranch")] + pub head_branch: ::std::string::String, + #[serde(rename = "isDraft")] + pub is_draft: bool, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub mergeability: ::std::option::Option, + #[serde(rename = "mergedAt", skip_serializing_if = "::std::option::Option::is_none")] + pub merged_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "reviewDecision", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub review_decision: ::std::option::Option, + pub state: SnapshotState, + #[serde(rename = "syncedAt")] + pub synced_at: ::std::string::String, + pub title: ::std::string::String, + #[serde( + rename = "updatedAt", + deserialize_with = "::std::option::Option::deserialize" + )] + pub updated_at: ::std::option::Option<::std::string::String>, +} +///`SnapshotChecksState` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum SnapshotChecksState { + #[serde(rename = "passing")] + Passing, + #[serde(rename = "failing")] + Failing, + #[serde(rename = "pending")] + Pending, +} +impl ::std::fmt::Display for SnapshotChecksState { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Passing => f.write_str("passing"), + Self::Failing => f.write_str("failing"), + Self::Pending => f.write_str("pending"), + } + } +} +impl ::std::str::FromStr for SnapshotChecksState { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "passing" => Ok(Self::Passing), + "failing" => Ok(Self::Failing), + "pending" => Ok(Self::Pending), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for SnapshotChecksState { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for SnapshotChecksState { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`SnapshotMergeability` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum SnapshotMergeability { + #[serde(rename = "mergeable")] + Mergeable, + #[serde(rename = "conflicting")] + Conflicting, + #[serde(rename = "unknown")] + Unknown, +} +impl ::std::fmt::Display for SnapshotMergeability { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Mergeable => f.write_str("mergeable"), + Self::Conflicting => f.write_str("conflicting"), + Self::Unknown => f.write_str("unknown"), + } + } +} +impl ::std::str::FromStr for SnapshotMergeability { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "mergeable" => Ok(Self::Mergeable), + "conflicting" => Ok(Self::Conflicting), + "unknown" => Ok(Self::Unknown), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for SnapshotMergeability { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for SnapshotMergeability { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`SnapshotReviewDecision` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum SnapshotReviewDecision { + #[serde(rename = "approved")] + Approved, + #[serde(rename = "changes-requested")] + ChangesRequested, + #[serde(rename = "review-required")] + ReviewRequired, +} +impl ::std::fmt::Display for SnapshotReviewDecision { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Approved => f.write_str("approved"), + Self::ChangesRequested => f.write_str("changes-requested"), + Self::ReviewRequired => f.write_str("review-required"), + } + } +} +impl ::std::str::FromStr for SnapshotReviewDecision { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "approved" => Ok(Self::Approved), + "changes-requested" => Ok(Self::ChangesRequested), + "review-required" => Ok(Self::ReviewRequired), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for SnapshotReviewDecision { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for SnapshotReviewDecision { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`SnapshotState` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum SnapshotState { + #[serde(rename = "open")] + Open, + #[serde(rename = "closed")] + Closed, + #[serde(rename = "merged")] + Merged, +} +impl ::std::fmt::Display for SnapshotState { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Open => f.write_str("open"), + Self::Closed => f.write_str("closed"), + Self::Merged => f.write_str("merged"), + } + } +} +impl ::std::str::FromStr for SnapshotState { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "open" => Ok(Self::Open), + "closed" => Ok(Self::Closed), + "merged" => Ok(Self::Merged), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for SnapshotState { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for SnapshotState { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`Stack` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct Stack { + pub base: ::std::string::String, + pub id: ::std::string::String, + pub kind: StackKind, + pub layers: ::std::vec::Vec, + pub number: ::std::num::NonZeroU64, + pub url: ::std::string::String, +} +///`StackKind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum StackKind { + #[serde(rename = "native")] + Native, +} +impl ::std::fmt::Display for StackKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Native => f.write_str("native"), + } + } +} +impl ::std::str::FromStr for StackKind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "native" => Ok(Self::Native), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for StackKind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for StackKind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`StackLayersItem` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct StackLayersItem { + #[serde(rename = "headBranch")] + pub head_branch: ::std::string::String, + pub number: ::std::num::NonZeroU64, + pub state: StackLayersItemState, +} +///`StackLayersItemState` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum StackLayersItemState { + #[serde(rename = "open")] + Open, + #[serde(rename = "closed")] + Closed, + #[serde(rename = "merged")] + Merged, +} +impl ::std::fmt::Display for StackLayersItemState { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Open => f.write_str("open"), + Self::Closed => f.write_str("closed"), + Self::Merged => f.write_str("merged"), + } + } +} +impl ::std::str::FromStr for StackLayersItemState { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "open" => Ok(Self::Open), + "closed" => Ok(Self::Closed), + "merged" => Ok(Self::Merged), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for StackLayersItemState { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for StackLayersItemState { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`Threads` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(transparent)] +pub struct Threads(pub ::std::vec::Vec); +impl ::std::ops::Deref for Threads { + type Target = ::std::vec::Vec; + fn deref(&self) -> &::std::vec::Vec { + &self.0 + } +} +impl ::std::convert::From for ::std::vec::Vec { + fn from(value: Threads) -> Self { + value.0 + } +} +impl ::std::convert::From<::std::vec::Vec> for Threads { + fn from(value: ::std::vec::Vec) -> Self { + Self(value) + } +} +///`ThreadsEntry` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ThreadsEntry { + #[serde( + rename = "activeOrderKey", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub active_order_key: ::std::option::Option<::std::string::String>, + #[serde( + rename = "activeProviderThreadId", + deserialize_with = "::std::option::Option::deserialize" + )] + pub active_provider_thread_id: ::std::option::Option<::std::string::String>, + #[serde( + rename = "activeRunId", + deserialize_with = "::std::option::Option::deserialize" + )] + pub active_run_id: ::std::option::Option<::std::string::String>, + #[serde( + rename = "activityRunStartedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub activity_run_started_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "activityRunStatus", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub activity_run_status: ::std::option::Option, + #[serde( + rename = "archivedAt", + deserialize_with = "::std::option::Option::deserialize" + )] + pub archived_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "autoSettleDisabledAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub auto_settle_disabled_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "blockedOnThreadWait", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub blocked_on_thread_wait: ::std::option::Option, + #[serde(deserialize_with = "::std::option::Option::deserialize")] + pub branch: ::std::option::Option<::std::string::String>, + #[serde( + rename = "branchPullRequest", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub branch_pull_request: ::std::option::Option, + #[serde(rename = "createdAt")] + pub created_at: ::std::string::String, + #[serde(rename = "createdBy")] + pub created_by: ThreadsEntryCreatedBy, + #[serde(rename = "creationSource")] + pub creation_source: ThreadsEntryCreationSource, + #[serde( + rename = "delegatedFrom", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub delegated_from: ::std::option::Option, + #[serde( + rename = "deletedAt", + deserialize_with = "::std::option::Option::deserialize" + )] + pub deleted_at: ::std::option::Option<::std::string::String>, + #[serde(rename = "forkedFrom")] + pub forked_from: ThreadsEntryForkedFrom, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub goal: ::std::option::Option, + #[serde(rename = "hasActionableProposedPlan")] + pub has_actionable_proposed_plan: bool, + #[serde( + rename = "historyOrigin", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub history_origin: ::std::option::Option, + pub id: ::std::string::String, + #[serde(rename = "interactionMode")] + pub interaction_mode: ThreadsEntryInteractionMode, + #[serde(rename = "itemCount")] + pub item_count: i64, + #[serde( + rename = "lastError", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub last_error: ::std::option::Option<::std::string::String>, + #[serde( + rename = "lastErrorClass", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub last_error_class: ::std::option::Option, + #[serde( + rename = "lastErrorCode", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub last_error_code: ::std::option::Option<::std::string::String>, + #[serde( + rename = "lastVisitedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub last_visited_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "latestRunCompletedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub latest_run_completed_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "latestRunId", + deserialize_with = "::std::option::Option::deserialize" + )] + pub latest_run_id: ::std::option::Option<::std::string::String>, + #[serde( + rename = "latestRunRequestedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub latest_run_requested_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "latestRunStartedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub latest_run_started_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "latestRunStopRequested", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub latest_run_stop_requested: ::std::option::Option, + #[serde( + rename = "latestUserAuthoredMessageAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub latest_user_authored_message_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "latestUserMessageAt", + deserialize_with = "::std::option::Option::deserialize" + )] + pub latest_user_message_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "latestVisibleMessage", + deserialize_with = "::std::option::Option::deserialize" + )] + pub latest_visible_message: ::std::option::Option, + #[serde( + rename = "limitRecovery", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub limit_recovery: ::std::option::Option, + pub lineage: Lineage, + #[serde( + rename = "linkOrigin", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub link_origin: ::std::option::Option, + #[serde( + rename = "linkedPullRequest", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub linked_pull_request: ::std::option::Option, + #[serde(rename = "modelSelection")] + pub model_selection: ModelSelection, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub opens: ::std::option::Option, + #[serde( + rename = "pendingBackgroundTasks", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub pending_background_tasks: ::std::option::Option, + #[serde( + rename = "pendingGuestMessageCount", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub pending_guest_message_count: ::std::option::Option, + #[serde( + rename = "pendingRuntimeRequest", + deserialize_with = "::std::option::Option::deserialize" + )] + pub pending_runtime_request: ::std::option::Option< + ThreadsEntryPendingRuntimeRequest, + >, + #[serde( + rename = "pinOrderKey", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub pin_order_key: ::std::option::Option<::std::string::String>, + #[serde(rename = "pinnedAt", skip_serializing_if = "::std::option::Option::is_none")] + pub pinned_at: ::std::option::Option<::std::string::String>, + #[serde(rename = "projectId")] + pub project_id: ::std::string::String, + #[serde( + rename = "providerInstanceHistory", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub provider_instance_history: ::std::option::Option< + ::std::vec::Vec<::std::string::String>, + >, + #[serde(rename = "providerInstanceId")] + pub provider_instance_id: ::std::string::String, + #[serde( + rename = "pullRequests", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub pull_requests: ::std::option::Option, + #[serde(rename = "runtimeMode")] + pub runtime_mode: ThreadsEntryRuntimeMode, + #[serde( + rename = "settledAt", + deserialize_with = "::std::option::Option::deserialize" + )] + pub settled_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "settledOverride", + deserialize_with = "::std::option::Option::deserialize" + )] + pub settled_override: ::std::option::Option, + #[serde( + rename = "snoozedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub snoozed_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "snoozedUntil", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub snoozed_until: ::std::option::Option<::std::string::String>, + pub status: ThreadsEntryStatus, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub tags: ::std::option::Option<::std::vec::Vec<::std::string::String>>, + pub title: ::std::string::String, + #[serde( + rename = "titleRegeneration", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub title_regeneration: ::std::option::Option, + #[serde( + rename = "unsettledAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub unsettled_at: ::std::option::Option<::std::string::String>, + #[serde(rename = "updatedAt")] + pub updated_at: ::std::string::String, + #[serde( + rename = "usageLimitResetAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub usage_limit_reset_at: ::std::option::Option<::std::string::String>, + #[serde(rename = "visibleItemCount")] + pub visible_item_count: i64, + #[serde( + rename = "worktreePath", + deserialize_with = "::std::option::Option::deserialize" + )] + pub worktree_path: ::std::option::Option<::std::string::String>, +} +///`ThreadsEntryActivityRunStatus` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryActivityRunStatus { + #[serde(rename = "preparing")] + Preparing, + #[serde(rename = "starting")] + Starting, + #[serde(rename = "running")] + Running, + #[serde(rename = "waiting")] + Waiting, +} +impl ::std::fmt::Display for ThreadsEntryActivityRunStatus { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Preparing => f.write_str("preparing"), + Self::Starting => f.write_str("starting"), + Self::Running => f.write_str("running"), + Self::Waiting => f.write_str("waiting"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryActivityRunStatus { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "preparing" => Ok(Self::Preparing), + "starting" => Ok(Self::Starting), + "running" => Ok(Self::Running), + "waiting" => Ok(Self::Waiting), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryActivityRunStatus { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryActivityRunStatus { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryCreatedBy` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryCreatedBy { + #[serde(rename = "user")] + User, + #[serde(rename = "agent")] + Agent, + #[serde(rename = "system")] + System, +} +impl ::std::fmt::Display for ThreadsEntryCreatedBy { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::User => f.write_str("user"), + Self::Agent => f.write_str("agent"), + Self::System => f.write_str("system"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryCreatedBy { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "user" => Ok(Self::User), + "agent" => Ok(Self::Agent), + "system" => Ok(Self::System), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryCreatedBy { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryCreatedBy { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryCreationSource` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryCreationSource { + #[serde(rename = "web")] + Web, + #[serde(rename = "mobile")] + Mobile, + #[serde(rename = "mcp")] + Mcp, + #[serde(rename = "provider")] + Provider, + #[serde(rename = "server")] + Server, +} +impl ::std::fmt::Display for ThreadsEntryCreationSource { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Web => f.write_str("web"), + Self::Mobile => f.write_str("mobile"), + Self::Mcp => f.write_str("mcp"), + Self::Provider => f.write_str("provider"), + Self::Server => f.write_str("server"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryCreationSource { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "web" => Ok(Self::Web), + "mobile" => Ok(Self::Mobile), + "mcp" => Ok(Self::Mcp), + "provider" => Ok(Self::Provider), + "server" => Ok(Self::Server), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryCreationSource { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryCreationSource { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryDelegatedFrom` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ThreadsEntryDelegatedFrom { + #[serde(rename = "environmentId")] + pub environment_id: ::std::string::String, + #[serde(rename = "threadId")] + pub thread_id: ::std::string::String, + pub title: ::std::string::String, +} +///`ThreadsEntryForkedFrom` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum ThreadsEntryForkedFrom { + Variant0 { + #[serde(rename = "runId")] + run_id: ::std::string::String, + #[serde(rename = "threadId")] + thread_id: ::std::string::String, + #[serde(rename = "type")] + type_: ThreadsEntryForkedFromVariant0Type, + }, + Variant1 { + #[serde(rename = "nodeId")] + node_id: ::std::string::String, + #[serde(rename = "type")] + type_: ThreadsEntryForkedFromVariant1Type, + }, + Variant2(ForkedFrom), + Variant3, +} +impl ::std::convert::From for ThreadsEntryForkedFrom { + fn from(value: ForkedFrom) -> Self { + Self::Variant2(value) + } +} +///`ThreadsEntryForkedFromVariant0Type` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryForkedFromVariant0Type { + #[serde(rename = "run")] + Run, +} +impl ::std::fmt::Display for ThreadsEntryForkedFromVariant0Type { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Run => f.write_str("run"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryForkedFromVariant0Type { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "run" => Ok(Self::Run), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryForkedFromVariant0Type { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ThreadsEntryForkedFromVariant0Type { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryForkedFromVariant1Type` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryForkedFromVariant1Type { + #[serde(rename = "node")] + Node, +} +impl ::std::fmt::Display for ThreadsEntryForkedFromVariant1Type { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Node => f.write_str("node"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryForkedFromVariant1Type { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "node" => Ok(Self::Node), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryForkedFromVariant1Type { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ThreadsEntryForkedFromVariant1Type { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryHistoryOrigin` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryHistoryOrigin { + #[serde(rename = "native")] + Native, + #[serde(rename = "v1_import")] + V1Import, +} +impl ::std::fmt::Display for ThreadsEntryHistoryOrigin { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Native => f.write_str("native"), + Self::V1Import => f.write_str("v1_import"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryHistoryOrigin { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "native" => Ok(Self::Native), + "v1_import" => Ok(Self::V1Import), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryHistoryOrigin { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryHistoryOrigin { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryInteractionMode` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryInteractionMode { + #[serde(rename = "default")] + Default, + #[serde(rename = "plan")] + Plan, +} +impl ::std::fmt::Display for ThreadsEntryInteractionMode { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Default => f.write_str("default"), + Self::Plan => f.write_str("plan"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryInteractionMode { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "default" => Ok(Self::Default), + "plan" => Ok(Self::Plan), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryInteractionMode { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryInteractionMode { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryLastErrorClass` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryLastErrorClass { + #[serde(rename = "usage_limit")] + UsageLimit, + #[serde(rename = "provider_error")] + ProviderError, + #[serde(rename = "transport_error")] + TransportError, + #[serde(rename = "permission_error")] + PermissionError, + #[serde(rename = "validation_error")] + ValidationError, + #[serde(rename = "unknown")] + Unknown, +} +impl ::std::fmt::Display for ThreadsEntryLastErrorClass { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::UsageLimit => f.write_str("usage_limit"), + Self::ProviderError => f.write_str("provider_error"), + Self::TransportError => f.write_str("transport_error"), + Self::PermissionError => f.write_str("permission_error"), + Self::ValidationError => f.write_str("validation_error"), + Self::Unknown => f.write_str("unknown"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryLastErrorClass { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "usage_limit" => Ok(Self::UsageLimit), + "provider_error" => Ok(Self::ProviderError), + "transport_error" => Ok(Self::TransportError), + "permission_error" => Ok(Self::PermissionError), + "validation_error" => Ok(Self::ValidationError), + "unknown" => Ok(Self::Unknown), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryLastErrorClass { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryLastErrorClass { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryLatestVisibleMessage` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ThreadsEntryLatestVisibleMessage { + pub id: ::std::string::String, + pub role: ThreadsEntryLatestVisibleMessageRole, + pub text: ::std::string::String, + #[serde(rename = "updatedAt")] + pub updated_at: ::std::string::String, +} +///`ThreadsEntryLatestVisibleMessageRole` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryLatestVisibleMessageRole { + #[serde(rename = "user")] + User, + #[serde(rename = "assistant")] + Assistant, + #[serde(rename = "system")] + System, +} +impl ::std::fmt::Display for ThreadsEntryLatestVisibleMessageRole { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::User => f.write_str("user"), + Self::Assistant => f.write_str("assistant"), + Self::System => f.write_str("system"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryLatestVisibleMessageRole { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "user" => Ok(Self::User), + "assistant" => Ok(Self::Assistant), + "system" => Ok(Self::System), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryLatestVisibleMessageRole { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ThreadsEntryLatestVisibleMessageRole { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryPendingRuntimeRequest` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ThreadsEntryPendingRuntimeRequest { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub blocking: ::std::option::Option, + #[serde(rename = "createdAt")] + pub created_at: ::std::string::String, + pub id: ::std::string::String, + pub kind: ThreadsEntryPendingRuntimeRequestKind, +} +///`ThreadsEntryPendingRuntimeRequestKind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryPendingRuntimeRequestKind { + #[serde(rename = "command")] + Command, + #[serde(rename = "file-read")] + FileRead, + #[serde(rename = "file-change")] + FileChange, + #[serde(rename = "mcp-elicitation")] + McpElicitation, + #[serde(rename = "permission")] + Permission, + #[serde(rename = "dynamic_tool_call")] + DynamicToolCall, + #[serde(rename = "user_input")] + UserInput, + #[serde(rename = "auth_refresh")] + AuthRefresh, +} +impl ::std::fmt::Display for ThreadsEntryPendingRuntimeRequestKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Command => f.write_str("command"), + Self::FileRead => f.write_str("file-read"), + Self::FileChange => f.write_str("file-change"), + Self::McpElicitation => f.write_str("mcp-elicitation"), + Self::Permission => f.write_str("permission"), + Self::DynamicToolCall => f.write_str("dynamic_tool_call"), + Self::UserInput => f.write_str("user_input"), + Self::AuthRefresh => f.write_str("auth_refresh"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryPendingRuntimeRequestKind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "command" => Ok(Self::Command), + "file-read" => Ok(Self::FileRead), + "file-change" => Ok(Self::FileChange), + "mcp-elicitation" => Ok(Self::McpElicitation), + "permission" => Ok(Self::Permission), + "dynamic_tool_call" => Ok(Self::DynamicToolCall), + "user_input" => Ok(Self::UserInput), + "auth_refresh" => Ok(Self::AuthRefresh), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryPendingRuntimeRequestKind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ThreadsEntryPendingRuntimeRequestKind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryRuntimeMode` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryRuntimeMode { + #[serde(rename = "approval-required")] + ApprovalRequired, + #[serde(rename = "auto-accept-edits")] + AutoAcceptEdits, + #[serde(rename = "auto")] + Auto, + #[serde(rename = "full-access")] + FullAccess, +} +impl ::std::fmt::Display for ThreadsEntryRuntimeMode { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ApprovalRequired => f.write_str("approval-required"), + Self::AutoAcceptEdits => f.write_str("auto-accept-edits"), + Self::Auto => f.write_str("auto"), + Self::FullAccess => f.write_str("full-access"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryRuntimeMode { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "approval-required" => Ok(Self::ApprovalRequired), + "auto-accept-edits" => Ok(Self::AutoAcceptEdits), + "auto" => Ok(Self::Auto), + "full-access" => Ok(Self::FullAccess), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryRuntimeMode { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryRuntimeMode { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntrySettledOverride` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntrySettledOverride { + #[serde(rename = "settled")] + Settled, + #[serde(rename = "active")] + Active, +} +impl ::std::fmt::Display for ThreadsEntrySettledOverride { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Settled => f.write_str("settled"), + Self::Active => f.write_str("active"), + } + } +} +impl ::std::str::FromStr for ThreadsEntrySettledOverride { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "settled" => Ok(Self::Settled), + "active" => Ok(Self::Active), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntrySettledOverride { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntrySettledOverride { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryStatus` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ThreadsEntryStatus { + #[serde(rename = "idle")] + Idle, + #[serde(rename = "preparing")] + Preparing, + #[serde(rename = "queued")] + Queued, + #[serde(rename = "starting")] + Starting, + #[serde(rename = "running")] + Running, + #[serde(rename = "waiting")] + Waiting, + #[serde(rename = "completed")] + Completed, + #[serde(rename = "interrupted")] + Interrupted, + #[serde(rename = "failed")] + Failed, + #[serde(rename = "cancelled")] + Cancelled, + #[serde(rename = "rolled_back")] + RolledBack, +} +impl ::std::fmt::Display for ThreadsEntryStatus { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Idle => f.write_str("idle"), + Self::Preparing => f.write_str("preparing"), + Self::Queued => f.write_str("queued"), + Self::Starting => f.write_str("starting"), + Self::Running => f.write_str("running"), + Self::Waiting => f.write_str("waiting"), + Self::Completed => f.write_str("completed"), + Self::Interrupted => f.write_str("interrupted"), + Self::Failed => f.write_str("failed"), + Self::Cancelled => f.write_str("cancelled"), + Self::RolledBack => f.write_str("rolled_back"), + } + } +} +impl ::std::str::FromStr for ThreadsEntryStatus { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "idle" => Ok(Self::Idle), + "preparing" => Ok(Self::Preparing), + "queued" => Ok(Self::Queued), + "starting" => Ok(Self::Starting), + "running" => Ok(Self::Running), + "waiting" => Ok(Self::Waiting), + "completed" => Ok(Self::Completed), + "interrupted" => Ok(Self::Interrupted), + "failed" => Ok(Self::Failed), + "cancelled" => Ok(Self::Cancelled), + "rolled_back" => Ok(Self::RolledBack), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ThreadsEntryStatus { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ThreadsEntryStatus { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ThreadsEntryTitleRegeneration` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ThreadsEntryTitleRegeneration { + #[serde(rename = "requestId")] + pub request_id: ::std::string::String, + #[serde(rename = "startedAt")] + pub started_at: ::std::string::String, +} +///`WireRoot` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(transparent)] +pub struct WireRoot(pub ::serde_json::Value); +impl ::std::ops::Deref for WireRoot { + type Target = ::serde_json::Value; + fn deref(&self) -> &::serde_json::Value { + &self.0 + } +} +impl ::std::convert::From for ::serde_json::Value { + fn from(value: WireRoot) -> Self { + value.0 + } +} +impl ::std::convert::From<::serde_json::Value> for WireRoot { + fn from(value: ::serde_json::Value) -> Self { + Self(value) + } +} +/// Error types. +pub mod error { + /// Error from a `TryFrom` or `FromStr` implementation. + pub struct ConversionError(::std::borrow::Cow<'static, str>); + impl ::std::error::Error for ConversionError {} + impl ::std::fmt::Display for ConversionError { + fn fmt( + &self, + f: &mut ::std::fmt::Formatter<'_>, + ) -> Result<(), ::std::fmt::Error> { + ::std::fmt::Display::fmt(&self.0, f) + } + } + impl ::std::fmt::Debug for ConversionError { + fn fmt( + &self, + f: &mut ::std::fmt::Formatter<'_>, + ) -> Result<(), ::std::fmt::Error> { + ::std::fmt::Debug::fmt(&self.0, f) + } + } + impl From<&'static str> for ConversionError { + fn from(value: &'static str) -> Self { + Self(value.into()) + } + } + impl From for ConversionError { + fn from(value: String) -> Self { + Self(value.into()) + } + } +} diff --git a/apps/desktop-gpui/src/wire/mod.rs b/apps/desktop-gpui/src/wire/mod.rs new file mode 100644 index 0000000000000..f1c9f2d703b5b --- /dev/null +++ b/apps/desktop-gpui/src/wire/mod.rs @@ -0,0 +1,11 @@ +//! Wire types generated from the TypeScript contracts (`packages/contracts/schema/rpc.schema.json`). + +#[allow(clippy::all, dead_code, unused_imports, unused_qualifications)] +#[rustfmt::skip] +pub mod generated; + +pub use generated::{ + OrchestrationSubscribeShellPayload as SubscribeShellPayload, + OrchestrationSubscribeShellSuccess as ShellItem, ProjectsEntry as Project, + ThreadsEntry as Thread, +}; diff --git a/apps/desktop-gpui/tests/fixtures/shell-project-updated.json b/apps/desktop-gpui/tests/fixtures/shell-project-updated.json new file mode 100644 index 0000000000000..fc288d4f48a77 --- /dev/null +++ b/apps/desktop-gpui/tests/fixtures/shell-project-updated.json @@ -0,0 +1,14 @@ +{ + "kind": "project.updated", + "sequence": 6, + "project": { + "id": "project-v2", + "title": "Renamed", + "workspaceRoot": "/workspace/project", + "repositoryIdentity": null, + "defaultModelSelection": null, + "scripts": [], + "createdAt": "2026-06-20T00:00:00.000Z", + "updatedAt": "2026-06-20T00:00:00.000Z" + } +} diff --git a/apps/desktop-gpui/tests/fixtures/shell-snapshot.json b/apps/desktop-gpui/tests/fixtures/shell-snapshot.json new file mode 100644 index 0000000000000..5042b8d8056b1 --- /dev/null +++ b/apps/desktop-gpui/tests/fixtures/shell-snapshot.json @@ -0,0 +1,61 @@ +{ + "kind": "snapshot", + "snapshot": { + "schemaVersion": 1, + "snapshotSequence": 5, + "threads": [ + { + "createdBy": "user", + "creationSource": "web", + "id": "thread-v2", + "projectId": "project-v2", + "title": "Thread", + "providerInstanceId": "codex", + "modelSelection": { + "instanceId": "codex", + "model": "gpt-5.4" + }, + "runtimeMode": "full-access", + "interactionMode": "default", + "branch": null, + "worktreePath": null, + "lineage": { + "parentThreadId": null, + "relationshipToParent": null, + "rootThreadId": "thread-v2" + }, + "forkedFrom": null, + "activeProviderThreadId": null, + "latestRunId": null, + "activeRunId": null, + "status": "idle", + "pendingRuntimeRequest": null, + "latestVisibleMessage": null, + "latestUserMessageAt": null, + "hasActionableProposedPlan": false, + "itemCount": 0, + "visibleItemCount": 0, + "createdAt": "2026-06-20T00:00:00.000Z", + "updatedAt": "2026-06-20T00:00:00.000Z", + "archivedAt": null, + "settledOverride": null, + "settledAt": null, + "lastVisitedAt": null, + "deletedAt": null + } + ], + "archivedThreads": [], + "projects": [ + { + "id": "project-v2", + "title": "Project", + "workspaceRoot": "/workspace/project", + "repositoryIdentity": null, + "defaultModelSelection": null, + "scripts": [], + "createdAt": "2026-06-20T00:00:00.000Z", + "updatedAt": "2026-06-20T00:00:00.000Z" + } + ] + } +} diff --git a/apps/desktop-gpui/tests/fixtures/shell-thread-updated.json b/apps/desktop-gpui/tests/fixtures/shell-thread-updated.json new file mode 100644 index 0000000000000..e6fe26908aac9 --- /dev/null +++ b/apps/desktop-gpui/tests/fixtures/shell-thread-updated.json @@ -0,0 +1,44 @@ +{ + "kind": "thread.updated", + "sequence": 7, + "location": "active", + "thread": { + "createdBy": "user", + "creationSource": "web", + "id": "thread-two", + "projectId": "project-v2", + "title": "Thread", + "providerInstanceId": "codex", + "modelSelection": { + "instanceId": "codex", + "model": "gpt-5.4" + }, + "runtimeMode": "full-access", + "interactionMode": "default", + "branch": null, + "worktreePath": null, + "lineage": { + "parentThreadId": null, + "relationshipToParent": null, + "rootThreadId": "thread-v2" + }, + "forkedFrom": null, + "activeProviderThreadId": null, + "latestRunId": null, + "activeRunId": null, + "status": "idle", + "pendingRuntimeRequest": null, + "latestVisibleMessage": null, + "latestUserMessageAt": null, + "hasActionableProposedPlan": false, + "itemCount": 0, + "visibleItemCount": 0, + "createdAt": "2026-06-20T00:00:00.000Z", + "updatedAt": "2026-06-20T00:00:00.000Z", + "archivedAt": null, + "settledOverride": null, + "settledAt": null, + "lastVisitedAt": null, + "deletedAt": null + } +} diff --git a/apps/desktop-gpui/tests/wire_types.rs b/apps/desktop-gpui/tests/wire_types.rs new file mode 100644 index 0000000000000..88f87240ef780 --- /dev/null +++ b/apps/desktop-gpui/tests/wire_types.rs @@ -0,0 +1,133 @@ +//! Regenerates `src/wire/generated.rs` from `packages/contracts/schema/rpc.schema.json` and fails +//! when the committed file differs. Run with `T3_UPDATE_GENERATED=1 cargo test --test wire_types` +//! to rewrite it. The schema is checked against the TypeScript contracts by +//! `vp run --filter @t3tools/contracts generate --check`. + +use serde_json::{Map, Value}; +use std::collections::BTreeSet; +use std::path::PathBuf; + +/// RPC methods whose payload, success and error types are generated. Add a method here when the +/// client starts calling it. +const METHODS: &[&str] = &["orchestration.subscribeShell"]; + +/// Properties typify cannot represent, kept as `serde_json::Value`: (definition, property). +/// Gaps are listed in the README. +const OPAQUE: &[(&str, &str)] = &[("ProjectsEntry", "projectIcon")]; + +const HEADER: &str = "// @generated by `cargo test --test wire_types` from packages/contracts/schema/rpc.schema.json.\n// Do not edit. Regenerate with T3_UPDATE_GENERATED=1 cargo test --test wire_types.\n\n"; + +fn manifest_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) +} + +fn collect_refs(node: &Value, out: &mut BTreeSet) { + match node { + Value::Object(map) => { + for (key, value) in map { + match (key.as_str(), value) { + ("$ref", Value::String(target)) => { + if let Some(name) = target.strip_prefix("#/$defs/") { + out.insert(name.to_owned()); + } + } + _ => collect_refs(value, out), + } + } + } + Value::Array(items) => items.iter().for_each(|item| collect_refs(item, out)), + _ => {} + } +} + +/// schemars 0.8 reads `#/definitions/`, draft 2020-12 writes `#/$defs/`. +fn retarget(node: &mut Value) { + match node { + Value::Object(map) => { + for (key, value) in map.iter_mut() { + if key == "$ref" { + if let Value::String(target) = value + && let Some(name) = target.strip_prefix("#/$defs/") + { + *target = format!("#/definitions/{name}"); + } + } else { + retarget(value); + } + } + } + Value::Array(items) => items.iter_mut().for_each(retarget), + _ => {} + } +} + +fn subset() -> Value { + let path = manifest_dir().join("../../packages/contracts/schema/rpc.schema.json"); + let document: Value = + serde_json::from_str(&std::fs::read_to_string(&path).expect("read rpc.schema.json")) + .expect("parse rpc.schema.json"); + let all = document["$defs"].as_object().expect("$defs"); + let mut pending: Vec = Vec::new(); + for method in METHODS { + let entry = &document["methods"][*method]; + for part in ["payload", "success", "error"] { + let target = entry[part]["$ref"] + .as_str() + .unwrap_or_else(|| panic!("{method} {part} is not a $ref in the schema index")); + pending.push(target.trim_start_matches("#/$defs/").to_owned()); + } + } + let mut needed = BTreeSet::new(); + while let Some(name) = pending.pop() { + if needed.insert(name.clone()) { + let mut refs = BTreeSet::new(); + collect_refs(&all[&name], &mut refs); + pending.extend(refs); + } + } + let definitions: Map = needed + .into_iter() + .map(|name| { + let mut body = all[&name].clone(); + for (def, property) in OPAQUE { + if *def == name { + body["properties"][*property] = serde_json::json!({}); + } + } + retarget(&mut body); + (name, body) + }) + .collect(); + serde_json::json!({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "WireRoot", + "definitions": definitions, + }) +} + +fn generate() -> String { + let root: schemars::schema::RootSchema = + serde_json::from_value(subset()).expect("schema subset is valid JSON Schema"); + let mut space = typify::TypeSpace::new(&typify::TypeSpaceSettings::default()); + space + .add_root_schema(root) + .expect("typify accepts the schema subset"); + let tokens = space.to_stream().expect("typify emits tokens"); + let file = syn::parse2(tokens).expect("generated code parses"); + format!("{HEADER}{}", prettyplease::unparse(&file)) +} + +#[test] +fn generated_wire_types_match_the_schema() { + let path = manifest_dir().join("src/wire/generated.rs"); + let fresh = generate(); + if std::env::var_os("T3_UPDATE_GENERATED").is_some() { + std::fs::write(&path, &fresh).expect("write generated.rs"); + return; + } + let current = std::fs::read_to_string(&path).unwrap_or_default(); + assert!( + current == fresh, + "src/wire/generated.rs is stale. Run T3_UPDATE_GENERATED=1 cargo test --test wire_types" + ); +} diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 180b65a91686a..31a1a65e99280 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -21,6 +21,7 @@ } }, "scripts": { + "generate": "node scripts/export-rpc-json-schema.ts", "typecheck": "effect-tsgo tsrs --noEmit", "test": "vp test run --config ../../vite.config.ts --dir ." }, diff --git a/packages/contracts/schema/rpc.schema.json b/packages/contracts/schema/rpc.schema.json new file mode 100644 index 0000000000000..24a560f4233bc --- /dev/null +++ b/packages/contracts/schema/rpc.schema.json @@ -0,0 +1,52466 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "T3 Code WebSocket RPC (encoded)", + "$defs": { + "AcpRegistryOperationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AcpRegistryOperationError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "agent_not_configured", + "agent_not_found", + "archive_invalid", + "authentication_failed", + "checksum_mismatch", + "download_failed", + "install_failed", + "instance_not_found", + "logout_unsupported", + "logout_failed", + "probe_failed", + "project_not_found", + "registry_unavailable", + "runner_unavailable", + "session_import_failed", + "session_delete_unsupported", + "session_delete_failed", + "session_list_unsupported", + "session_resume_unsupported", + "providers_unsupported", + "providers_list_failed", + "provider_configuration_failed", + "unsupported_distribution", + "unsupported_platform" + ] + }, + "message": { + "type": "string" + }, + "authMethods": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 1024 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "agent", + "env_var", + "terminal" + ] + }, + "command": { + "type": "string", + "maxLength": 2048 + }, + "envVarNames": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 16 + }, + "link": { + "type": "string", + "maxLength": 2048 + } + }, + "required": [ + "id", + "name", + "description", + "type" + ], + "additionalProperties": true + }, + "maxItems": 32 + }, + "authAction": { + "$ref": "#/$defs/Action" + }, + "cause": {} + }, + "required": [ + "_tag", + "reason", + "message" + ], + "additionalProperties": true + }, + "Action": { + "type": "object", + "properties": { + "elicitationId": { + "type": "string" + }, + "url": { + "type": "string", + "maxLength": 2048 + }, + "message": { + "type": "string", + "maxLength": 1024 + }, + "createdAt": { + "type": "string", + "maxLength": 128 + }, + "expiresAt": { + "type": "string", + "maxLength": 128 + } + }, + "required": [ + "elicitationId", + "url", + "message" + ], + "additionalProperties": true + }, + "Actions": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "ready", + "draft", + "close", + "reopen", + "delete-branch", + "update-branch", + "enable-auto-merge", + "disable-auto-merge", + "revert", + "approve-workflows" + ] + }, + { + "type": "null" + } + ] + } + }, + "Agent": { + "type": "object", + "properties": { + "requiredVersion": { + "type": "string" + }, + "installedVersions": { + "type": "array", + "items": { + "type": "string" + } + }, + "runningVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "requiredVersion", + "installedVersions", + "runningVersion" + ], + "additionalProperties": true + }, + "AgentSessionImportProjectChangedErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AgentSessionImportProjectChangedError" + ] + }, + "projectId": { + "type": "string" + } + }, + "required": [ + "_tag", + "projectId" + ], + "additionalProperties": true + }, + "AgentSessionImportProjectNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AgentSessionImportProjectNotFoundError" + ] + }, + "projectId": { + "type": "string" + } + }, + "required": [ + "_tag", + "projectId" + ], + "additionalProperties": true + }, + "AgentSessionScanErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AgentSessionScanError" + ] + }, + "operation": { + "type": "string", + "enum": [ + "read-settings", + "read-projects" + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "operation", + "cause" + ], + "additionalProperties": true + }, + "AgentSessionsImportError": { + "anyOf": [ + { + "$ref": "#/$defs/AgentSessionImportProjectChangedErrorEncoded" + }, + { + "$ref": "#/$defs/AgentSessionImportProjectNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/AgentSessionScanErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "AgentSessionsImportPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "expectedWorkspaceRoot": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "projectId" + ], + "additionalProperties": true + }, + "AgentSessionsImportSuccess": { + "type": "object", + "properties": { + "importedCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "skippedCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "importedCount", + "skippedCount" + ], + "additionalProperties": true + }, + "AgentSessionsScanError": { + "anyOf": [ + { + "$ref": "#/$defs/AgentSessionScanErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "AgentSessionsScanPayload": { + "not": { + "type": "null" + } + }, + "AgentSessionsScanSuccess": { + "type": "object", + "properties": { + "candidates": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "title": { + "type": "string" + }, + "projectId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sources": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "claudeAgent", + "codex", + "pi" + ] + } + }, + "threadCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "lastActiveAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "alreadyImported": { + "type": "boolean" + }, + "git": { + "anyOf": [ + { + "type": "object", + "properties": { + "remoteKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "repository": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "remoteKey", + "repository" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "path", + "title", + "sources", + "threadCount", + "lastActiveAt", + "alreadyImported" + ], + "additionalProperties": true + } + }, + "scannedAt": { + "type": "string" + }, + "truncated": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "candidates", + "scannedAt" + ], + "additionalProperties": true + }, + "AllT3": { + "type": "object", + "properties": { + "processCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "currentCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cpuTimeMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "currentRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "peakRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioReadBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioWriteBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioReadBytesPerSecond": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "ioWriteBytesPerSecond": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "processStarts": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "processExits": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "processCount", + "currentCpuPercent", + "cpuTimeMs", + "currentRssBytes", + "peakRssBytes", + "ioReadBytes", + "ioWriteBytes", + "ioReadBytesPerSecond", + "ioWriteBytesPerSecond", + "processStarts", + "processExits" + ], + "additionalProperties": true + }, + "App": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "app-id" + ] + }, + "appId": { + "type": "string" + } + }, + "required": [ + "_tag", + "appId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "display-name" + ] + }, + "displayName": { + "type": "string" + } + }, + "required": [ + "_tag", + "displayName" + ], + "additionalProperties": true + } + ] + }, + "AssetAttachmentNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetAttachmentNotFoundError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + } + }, + "required": [ + "_tag", + "resource" + ], + "additionalProperties": true + }, + "AssetGitHubMediaUrlValidationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetGitHubMediaUrlValidationError" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + "AssetPreviewTypeValidationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetPreviewTypeValidationError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + } + }, + "required": [ + "_tag", + "resource" + ], + "additionalProperties": true + }, + "AssetProjectFaviconInspectionErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetProjectFaviconInspectionError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetProjectFaviconNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetProjectFaviconNotFoundError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + } + }, + "required": [ + "_tag", + "resource" + ], + "additionalProperties": true + }, + "AssetProjectFaviconResolutionErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetProjectFaviconResolutionError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetSigningKeyLoadErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetSigningKeyLoadError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetWorkspaceAssetInspectionErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetWorkspaceAssetInspectionError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetWorkspaceAssetNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetWorkspaceAssetNotFoundError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "checkedPath": { + "type": "string" + } + }, + "required": [ + "_tag", + "resource" + ], + "additionalProperties": true + }, + "AssetWorkspaceContextNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetWorkspaceContextNotFoundError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + } + }, + "required": [ + "_tag", + "resource" + ], + "additionalProperties": true + }, + "AssetWorkspaceContextResolutionErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetWorkspaceContextResolutionError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetWorkspacePathValidationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetWorkspacePathValidationError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetWorkspaceResolutionErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetWorkspaceResolutionError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetWorkspaceRootNormalizationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AssetWorkspaceRootNormalizationError" + ] + }, + "resource": { + "$ref": "#/$defs/Resource" + }, + "cause": {} + }, + "required": [ + "_tag", + "resource", + "cause" + ], + "additionalProperties": true + }, + "AssetsCreateUrlError": { + "anyOf": [ + { + "$ref": "#/$defs/AssetWorkspaceContextNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/AssetWorkspaceContextResolutionErrorEncoded" + }, + { + "$ref": "#/$defs/AssetWorkspaceRootNormalizationErrorEncoded" + }, + { + "$ref": "#/$defs/AssetWorkspacePathValidationErrorEncoded" + }, + { + "$ref": "#/$defs/AssetPreviewTypeValidationErrorEncoded" + }, + { + "$ref": "#/$defs/AssetWorkspaceAssetInspectionErrorEncoded" + }, + { + "$ref": "#/$defs/AssetWorkspaceAssetNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/AssetWorkspaceResolutionErrorEncoded" + }, + { + "$ref": "#/$defs/AssetAttachmentNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/AssetProjectFaviconResolutionErrorEncoded" + }, + { + "$ref": "#/$defs/AssetProjectFaviconInspectionErrorEncoded" + }, + { + "$ref": "#/$defs/AssetProjectFaviconNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/AssetGitHubMediaUrlValidationErrorEncoded" + }, + { + "$ref": "#/$defs/AssetSigningKeyLoadErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "AssetsCreateUrlPayload": { + "type": "object", + "properties": { + "resource": { + "$ref": "#/$defs/Resource" + } + }, + "required": [ + "resource" + ], + "additionalProperties": true + }, + "AssetsCreateUrlSuccess": { + "type": "object", + "properties": { + "relativeUrl": { + "type": "string" + }, + "expiresAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "sourcePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "imageDimensions": { + "anyOf": [ + { + "$ref": "#/$defs/ImageSize" + }, + { + "type": "null" + } + ] + }, + "fileRevision": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "relativeUrl", + "expiresAt" + ], + "additionalProperties": true + }, + "AssetsPersistChatAttachmentsError": { + "anyOf": [ + { + "$ref": "#/$defs/PersistChatAttachmentsErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "AssetsPersistChatAttachmentsPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "messageId": { + "type": "string" + }, + "attachments": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "image" + ] + }, + "name": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "sizeBytes": { + "type": "integer", + "minimum": 0, + "maximum": 10485760 + }, + "dataUrl": { + "type": "string" + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/Source" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "name", + "mimeType", + "sizeBytes", + "dataUrl" + ], + "additionalProperties": true + } + ] + } + } + }, + "required": [ + "threadId", + "messageId", + "attachments" + ], + "additionalProperties": true + }, + "AssetsPersistChatAttachmentsSuccess": { + "type": "object", + "properties": { + "attachments": { + "$ref": "#/$defs/Attachments" + } + }, + "required": [ + "attachments" + ], + "additionalProperties": true + }, + "AttachmentUploadSigningKeyErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AttachmentUploadSigningKeyError" + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "cause" + ], + "additionalProperties": true + }, + "Attachments": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/AttachmentsEntry2" + }, + { + "$ref": "#/$defs/AttachmentsEntry3" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "sizeBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "type", + "id", + "name", + "mimeType", + "sizeBytes" + ], + "additionalProperties": true + } + ] + } + }, + "AttachmentsByQuestionId": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/AttachmentsEntry2" + }, + { + "$ref": "#/$defs/AttachmentsEntry3" + } + ] + }, + "maxItems": 100 + } + }, + "AttachmentsCreateUploadUrlError": { + "anyOf": [ + { + "$ref": "#/$defs/AttachmentUploadSigningKeyErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "AttachmentsCreateUploadUrlPayload": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "image" + ] + }, + "name": { + "type": "string" + }, + "mimeType": { + "type": "string", + "enum": [ + "image/gif", + "image/jpeg", + "image/png", + "image/webp" + ] + }, + "sizeBytes": { + "type": "integer", + "minimum": 1, + "maximum": 10485760 + } + }, + "required": [ + "name", + "mimeType", + "sizeBytes" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "name": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "sizeBytes": { + "type": "integer", + "minimum": 1, + "maximum": 52428800 + } + }, + "required": [ + "type", + "name", + "mimeType", + "sizeBytes" + ], + "additionalProperties": true + } + ] + }, + "AttachmentsCreateUploadUrlSuccess": { + "type": "object", + "properties": { + "attachmentId": { + "type": "string" + }, + "relativeUrl": { + "type": "string" + }, + "expiresAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "attachmentId", + "relativeUrl", + "expiresAt" + ], + "additionalProperties": true + }, + "AttachmentsDeletePayload": { + "type": "object", + "properties": { + "attachmentId": { + "type": "string" + } + }, + "required": [ + "attachmentId" + ], + "additionalProperties": true + }, + "AttachmentsDeleteSuccess": { + "type": "null" + }, + "AttachmentsEntry2": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "image" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "sizeBytes": { + "type": "integer", + "minimum": 0, + "maximum": 10485760 + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/Source" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "id", + "name", + "mimeType", + "sizeBytes" + ], + "additionalProperties": true + }, + "AttachmentsEntry3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "sizeBytes": { + "type": "integer", + "minimum": 1, + "maximum": 52428800 + }, + "source": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "pasted-text" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "id", + "name", + "mimeType", + "sizeBytes" + ], + "additionalProperties": true + }, + "AuthAccessStreamErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "AuthAccessStreamError" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "Author": { + "type": "object", + "properties": { + "isBot": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "login": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "avatarUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "login", + "name", + "avatarUrl" + ], + "additionalProperties": true + }, + "Authors": { + "type": "array", + "items": { + "$ref": "#/$defs/Author" + } + }, + "BasePoint": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "checkpointId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "turnItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "providerTurnRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "BranchChanges": { + "type": "object", + "properties": { + "baseRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "insertions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "baseRef", + "insertions", + "deletions" + ], + "additionalProperties": true + }, + "Checks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "action-required", + "success", + "failure", + "skipped", + "neutral", + "cancelled" + ] + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name", + "status", + "description", + "url" + ], + "additionalProperties": true + } + }, + "ClientSessionsEntry": { + "type": "object", + "properties": { + "sessionId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/Scopes" + }, + "permissions": { + "$ref": "#/$defs/Permissions" + }, + "method": { + "type": "string", + "enum": [ + "browser-session-cookie", + "bearer-access-token", + "dpop-access-token" + ] + }, + "client": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "ipAddress": { + "type": "string" + }, + "userAgent": { + "type": "string" + }, + "deviceType": { + "type": "string", + "enum": [ + "desktop", + "mobile", + "tablet", + "bot", + "unknown" + ] + }, + "os": { + "type": "string" + }, + "browser": { + "type": "string" + } + }, + "required": [ + "deviceType" + ], + "additionalProperties": true + }, + "issuedAt": { + "type": "string" + }, + "expiresAt": { + "type": "string" + }, + "lastConnectedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "connected": { + "type": "boolean" + }, + "current": { + "type": "boolean" + } + }, + "required": [ + "sessionId", + "subject", + "scopes", + "method", + "client", + "issuedAt", + "expiresAt", + "lastConnectedAt", + "connected", + "current" + ], + "additionalProperties": true + }, + "CloudGetRelayClientStatusSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "available" + ] + }, + "executablePath": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "override", + "managed", + "path" + ] + }, + "version": { + "type": "string" + } + }, + "required": [ + "status", + "executablePath", + "source", + "version" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "missing" + ] + }, + "version": { + "type": "string" + } + }, + "required": [ + "status", + "version" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "unsupported" + ] + }, + "platform": { + "type": "string" + }, + "arch": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "status", + "platform", + "arch", + "version" + ], + "additionalProperties": true + } + ] + }, + "CloudInstallRelayClientError": { + "anyOf": [ + { + "$ref": "#/$defs/RelayClientInstallFailedErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "CloudInstallRelayClientSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "progress" + ] + }, + "stage": { + "type": "string", + "enum": [ + "checking", + "waiting_for_lock", + "downloading", + "verifying", + "installing", + "validating", + "activating" + ] + } + }, + "required": [ + "type", + "stage" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "complete" + ] + }, + "status": { + "$ref": "#/$defs/CloudGetRelayClientStatusSuccess" + } + }, + "required": [ + "type", + "status" + ], + "additionalProperties": true + } + ] + }, + "Code": { + "type": "object", + "properties": { + "additions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "additions", + "deletions" + ], + "additionalProperties": true + }, + "Command": { + "anyOf": [ + { + "type": "string", + "enum": [ + "workspace.focusNext", + "workspace.focusPrevious", + "sidebar.toggle", + "navigation.back", + "navigation.forward", + "terminal.toggle", + "terminal.split", + "terminal.splitVertical", + "terminal.new", + "terminal.close", + "rightPanel.toggle", + "rightPanel.new", + "threadPanel.toggle", + "rightPanel.toggleMaximized", + "rightPanel.close", + "view.reopenClosed", + "pullRequest.copyNumber", + "diff.toggle", + "preview.toggle", + "preview.refresh", + "preview.focusUrl", + "preview.zoomIn", + "preview.zoomOut", + "preview.resetZoom", + "commandPalette.toggle", + "filePicker.toggle", + "projectSearch.toggle", + "usage.open", + "theme.select", + "appearance.cycle", + "themeEditor.toggle", + "composer.stash", + "composer.sendAlternate", + "composer.sendBackground", + "composer.sendAndNewThread", + "composer.host", + "composer.cycleHost", + "composer.effort", + "composer.increaseEffort", + "composer.decreaseEffort", + "composer.mode", + "composer.workspace", + "composer.previousWorktree", + "composer.branch", + "chat.new", + "chat.newLocal", + "chat.newWithoutProject", + "chat.find", + "editor.openFavorite", + "usage.cost", + "usage.tokens", + "usage.limits", + "usage.period.day", + "usage.period.week", + "usage.period.month", + "usage.period.quarter", + "modelPicker.toggle", + "modelPicker.previousProvider", + "modelPicker.nextProvider", + "modelPicker.jump.1", + "modelPicker.jump.2", + "modelPicker.jump.3", + "modelPicker.jump.4", + "modelPicker.jump.5", + "modelPicker.jump.6", + "modelPicker.jump.7", + "modelPicker.jump.8", + "modelPicker.jump.9", + "thread.stop", + "thread.steerQueuedMessage", + "thread.editQueuedMessage", + "thread.previous", + "thread.next", + "thread.copyReference", + "thread.settle", + "thread.pin", + "thread.undo", + "thread.jump.1", + "thread.jump.2", + "thread.jump.3", + "thread.jump.4", + "thread.jump.5", + "thread.jump.6", + "thread.jump.7", + "thread.jump.8", + "thread.jump.9" + ] + }, + { + "type": "string", + "pattern": "^script\\.[\\s\\S]*?\\.run$" + } + ] + }, + "Comments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "body": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "editedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reactions": { + "anyOf": [ + { + "$ref": "#/$defs/Reactions" + }, + { + "type": "null" + } + ] + }, + "canEdit": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "author", + "body", + "createdAt", + "url" + ], + "additionalProperties": true + } + }, + "Context": { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "guest": { + "anyOf": [ + { + "type": "object", + "properties": { + "guestMessageId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "text": { + "type": "string", + "maxLength": 4000 + }, + "shareId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "guestMessageId", + "name", + "text" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "records": { + "type": "array", + "maxItems": 200 + } + }, + "required": [ + "version", + "records" + ], + "additionalProperties": true + }, + "Counts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "count": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "projectId", + "count" + ], + "additionalProperties": true + } + }, + "Dedupe": { + "type": "object", + "properties": { + "replayKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coalesceKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coalesceWindowSeconds": { + "anyOf": [ + { + "type": "integer", + "minimum": 10, + "maximum": 3600 + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true, + "description": "Replay and coalesce key templates. Window in seconds, default 120." + }, + "DefaultProjectScripts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "command": { + "type": "string" + }, + "icon": { + "type": "string", + "enum": [ + "play", + "test", + "lint", + "configure", + "build", + "debug" + ] + }, + "runOnWorktreeCreate": { + "type": "boolean" + }, + "runOnSettle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "async": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "previewUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "autoOpenPreview": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "name", + "command", + "icon", + "runOnWorktreeCreate" + ], + "additionalProperties": true + } + }, + "DeferredWork": { + "type": "object", + "properties": { + "cursor": { + "type": "string" + }, + "itemCount": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "lastOrdinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "cursor", + "itemCount", + "lastOrdinal" + ], + "additionalProperties": true + }, + "DelegatedCompletion": { + "type": "object", + "properties": { + "parentRunId": { + "type": "string" + }, + "generation": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "taskIds": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "parentRunId", + "generation", + "taskIds" + ], + "additionalProperties": true + }, + "Desktop": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "starting", + "healthy", + "degraded", + "unavailable", + "stopped" + ] + }, + "lastSampleAt": { + "$ref": "#/$defs/LastError" + }, + "lastError": { + "$ref": "#/$defs/LastError" + } + }, + "required": [ + "status", + "lastSampleAt", + "lastError" + ], + "additionalProperties": true + }, + "DeviceActionError": { + "anyOf": [ + { + "$ref": "#/$defs/DeviceHostUnavailableErrorEncoded" + }, + { + "$ref": "#/$defs/DevicePlatformUnavailableErrorEncoded" + }, + { + "$ref": "#/$defs/DeviceNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/DeviceBootErrorEncoded" + }, + { + "$ref": "#/$defs/DeviceOperationErrorEncoded" + }, + { + "$ref": "#/$defs/DeviceActionUnavailableErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "DeviceActionPayload": { + "anyOf": [ + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setAppearance" + ] + }, + "value": { + "type": "string", + "enum": [ + "light", + "dark" + ] + } + }, + "required": [ + "deviceId", + "type", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setTextSize" + ] + }, + "value": { + "type": "string", + "enum": [ + "small", + "default", + "large", + "extra-large" + ] + } + }, + "required": [ + "deviceId", + "type", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setToggle" + ] + }, + "setting": { + "type": "string", + "enum": [ + "reduceMotion", + "increaseContrast", + "reduceTransparency", + "showBorders", + "voiceOver", + "networkEnabled" + ] + }, + "value": { + "type": "boolean" + } + }, + "required": [ + "deviceId", + "type", + "setting", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setLiquidGlass" + ] + }, + "value": { + "type": "string", + "enum": [ + "clear", + "tinted" + ] + } + }, + "required": [ + "deviceId", + "type", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setColorFilter" + ] + }, + "value": { + "type": "string", + "enum": [ + "none", + "grayscale", + "red-green", + "green-red", + "blue-yellow" + ] + } + }, + "required": [ + "deviceId", + "type", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setOrientation" + ] + }, + "value": { + "type": "string", + "enum": [ + "portrait", + "landscape_left", + "portrait_upside_down", + "landscape_right" + ] + } + }, + "required": [ + "deviceId", + "type", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setLocation" + ] + }, + "latitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "longitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "deviceId", + "type", + "latitude", + "longitude" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "clearLocation" + ] + } + }, + "required": [ + "deviceId", + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "setPermission" + ] + }, + "appId": { + "type": "string" + }, + "permission": { + "type": "string", + "enum": [ + "camera", + "microphone", + "photos", + "contacts", + "calendar", + "reminders", + "location", + "notifications", + "motion", + "media-library", + "faceid" + ] + }, + "decision": { + "type": "string", + "enum": [ + "grant", + "revoke", + "reset" + ] + } + }, + "required": [ + "deviceId", + "type", + "appId", + "permission", + "decision" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "openUrl" + ] + }, + "url": { + "type": "string" + } + }, + "required": [ + "deviceId", + "type", + "url" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "launchApp" + ] + }, + "appId": { + "type": "string" + } + }, + "required": [ + "deviceId", + "type", + "appId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "terminateApp" + ] + }, + "appId": { + "type": "string" + } + }, + "required": [ + "deviceId", + "type", + "appId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "shake" + ] + } + }, + "required": [ + "deviceId", + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "sendPush" + ] + }, + "appId": { + "type": "string" + }, + "payload": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + }, + "required": [ + "deviceId", + "type", + "appId", + "payload" + ], + "additionalProperties": true + } + ] + }, + "DeviceActionSuccess": { + "type": "object", + "properties": { + "hostId": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "appearance": { + "anyOf": [ + { + "type": "string", + "enum": [ + "light", + "dark" + ] + }, + { + "type": "null" + } + ] + }, + "textSize": { + "anyOf": [ + { + "type": "string", + "enum": [ + "small", + "default", + "large", + "extra-large" + ] + }, + { + "type": "null" + } + ] + }, + "reduceMotion": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "increaseContrast": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "reduceTransparency": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "showBorders": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "voiceOver": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "liquidGlass": { + "anyOf": [ + { + "type": "string", + "enum": [ + "clear", + "tinted" + ] + }, + { + "type": "null" + } + ] + }, + "colorFilter": { + "anyOf": [ + { + "type": "string", + "enum": [ + "none", + "grayscale", + "red-green", + "green-red", + "blue-yellow" + ] + }, + { + "type": "null" + } + ] + }, + "networkEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "location": { + "anyOf": [ + { + "type": "object", + "properties": { + "latitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "longitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "latitude", + "longitude" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "foregroundApp": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "readAt": { + "type": "string" + } + }, + "required": [ + "hostId", + "deviceId", + "settings", + "foregroundApp", + "readAt" + ], + "additionalProperties": true + }, + "DeviceActionUnavailableErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DeviceActionUnavailableError" + ] + }, + "operation": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + }, + "reason": { + "type": "string", + "enum": [ + "unsupported", + "helper_missing" + ] + } + }, + "required": [ + "_tag", + "operation", + "platform", + "reason" + ], + "additionalProperties": true + }, + "DeviceBootErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DeviceBootError" + ] + }, + "hostId": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "disk_space", + "timeout", + "launch_failed" + ] + }, + "diagnostics": { + "anyOf": [ + { + "type": "string", + "maxLength": 2000 + }, + { + "type": "null" + } + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "hostId", + "deviceId", + "reason", + "cause" + ], + "additionalProperties": true + }, + "DeviceClosePayload": { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + }, + "deviceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "shutdown": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "DeviceConfigurePayload": { + "type": "object", + "properties": { + "enabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "agentAccessEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "onboardingCompleted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "DeviceConfigureSuccess": { + "type": "object", + "properties": { + "supportsHostRetry": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "supportsToolUpdate": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "supportsToolInspection": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "supportsEasHosts": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "supportsSimlockHosts": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "hostStatus": { + "type": "string", + "enum": [ + "disabled", + "idle", + "installing", + "starting", + "ready", + "failed" + ] + }, + "hostStatusDetail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hostStatuses": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "disabled", + "idle", + "installing", + "starting", + "ready", + "failed" + ] + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "androidDiscoveryIncomplete": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": true + } + }, + "devices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "hostId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "booted": { + "type": "boolean" + }, + "physical": { + "type": "boolean" + } + }, + "required": [ + "hostId", + "id", + "platform", + "name", + "version", + "booted", + "physical" + ], + "additionalProperties": true + } + }, + "sessions": { + "type": "array", + "items": { + "$ref": "#/$defs/SessionsEntry" + } + }, + "bootingDevices": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "hostId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "booted": { + "type": "boolean" + }, + "physical": { + "type": "boolean" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "hostId", + "id", + "platform", + "name", + "version", + "booted", + "physical", + "threadId" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + }, + "onboardingCompleted": { + "type": "boolean" + }, + "agentAccessEnabled": { + "type": "boolean" + }, + "hubBasePath": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "hosts": { + "type": "array", + "items": { + "$ref": "#/$defs/HostsEntry" + } + }, + "easHosts": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/EasHostsEntry" + } + }, + { + "type": "null" + } + ] + }, + "simlockHosts": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/SimlockHostsEntry" + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "hostStatus", + "hostStatuses", + "devices", + "sessions", + "onboardingCompleted", + "agentAccessEnabled", + "hubBasePath", + "revision", + "hosts" + ], + "additionalProperties": true + }, + "DeviceDetailPayload": { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + } + }, + "required": [ + "deviceId" + ], + "additionalProperties": true + }, + "DeviceEasAccountError": { + "anyOf": [ + { + "$ref": "#/$defs/DeviceEasAccountErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "DeviceEasAccountErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DeviceEasAccountError" + ] + }, + "reason": { + "type": "string" + } + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "DeviceEasAccountPayload": { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "reference": { + "type": "string" + } + }, + "required": [ + "provider", + "reference" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "DeviceEasAccountSuccess": { + "type": "object", + "properties": { + "signedInAs": { + "type": "string" + }, + "credentialSource": { + "type": "string", + "enum": [ + "settings", + "env", + "eas-login" + ] + }, + "apps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "fullName": { + "type": "string" + } + }, + "required": [ + "id", + "fullName" + ], + "additionalProperties": true + } + } + }, + "required": [ + "signedInAs", + "credentialSource", + "apps" + ], + "additionalProperties": true + }, + "DeviceHostUnavailableErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DeviceHostUnavailableError" + ] + }, + "hostId": { + "type": "string" + }, + "hostLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reason": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "hostId", + "reason" + ], + "additionalProperties": true + }, + "DeviceHosts": { + "type": "array", + "items": { + "$ref": "#/$defs/DeviceTestHostPayload" + } + }, + "DeviceListPayload": { + "type": "object", + "properties": { + "updateTool": { + "anyOf": [ + { + "type": "string", + "enum": [ + "hub", + "agent" + ] + }, + { + "type": "null" + } + ] + }, + "inspectOnly": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "retryHostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "DeviceNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DeviceNotFoundError" + ] + }, + "hostId": { + "type": "string" + }, + "deviceId": { + "type": "string" + } + }, + "required": [ + "_tag", + "hostId", + "deviceId" + ], + "additionalProperties": true + }, + "DeviceOpenPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + }, + "boot": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "deviceId", + "platform" + ], + "additionalProperties": true + }, + "DeviceOperationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DeviceOperationError" + ] + }, + "operation": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "command_failed", + "request_failed", + "invalid_payload", + "settings_failed", + "hub_rejected" + ] + }, + "exitCode": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "operation", + "reason", + "cause" + ], + "additionalProperties": true + }, + "DevicePlatformUnavailableErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DevicePlatformUnavailableError" + ] + }, + "hostId": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + }, + "reason": { + "type": "string" + } + }, + "required": [ + "_tag", + "hostId", + "platform", + "reason" + ], + "additionalProperties": true + }, + "DeviceShutdownPayload": { + "type": "object", + "properties": { + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deviceId": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + } + }, + "required": [ + "deviceId", + "platform" + ], + "additionalProperties": true + }, + "DeviceSimlockIdentityError": { + "anyOf": [ + { + "$ref": "#/$defs/DeviceSimlockIdentityErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "DeviceSimlockIdentityErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "DeviceSimlockIdentityError" + ] + }, + "reason": { + "type": "string" + } + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "DeviceSimlockIdentityPayload": { + "type": "object", + "properties": { + "gatewayUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hostId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "agentToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "DeviceSimlockIdentitySuccess": { + "type": "object", + "properties": { + "gatewayUrl": { + "type": "string" + }, + "signedInAs": { + "type": "string" + }, + "tokenSource": { + "type": "string", + "enum": [ + "entered", + "settings", + "env" + ] + } + }, + "required": [ + "gatewayUrl", + "signedInAs", + "tokenSource" + ], + "additionalProperties": true + }, + "DeviceTestHostPayload": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "ssh" + ] + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "target": { + "type": "string" + }, + "identityFile": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "port": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "label", + "target" + ], + "additionalProperties": true + }, + "DeviceTestHostSuccess": { + "anyOf": [ + { + "$ref": "#/$defs/HostsEntry" + }, + { + "$ref": "#/$defs/EasHostsEntry" + }, + { + "$ref": "#/$defs/SimlockHostsEntry" + } + ] + }, + "EasDeviceHosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "eas" + ] + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "appId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "accessToken": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "reference": { + "type": "string" + } + }, + "required": [ + "provider", + "reference" + ], + "additionalProperties": true + } + ] + }, + "maxSessionMinutes": { + "type": "integer", + "minimum": 1, + "maximum": 115 + }, + "idleMinutes": { + "type": "integer", + "minimum": 1, + "maximum": 60 + }, + "maxConcurrentSessions": { + "type": "integer", + "minimum": 1, + "maximum": 20 + } + }, + "required": [ + "kind", + "id", + "label" + ], + "additionalProperties": true + } + }, + "EasHostsEntry": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "platforms": { + "$ref": "#/$defs/Platforms" + }, + "tools": { + "anyOf": [ + { + "$ref": "#/$defs/Tools" + }, + { + "type": "null" + } + ] + }, + "toolInspectionError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hubInstalled": { + "type": "boolean" + }, + "agentDeviceInstalled": { + "type": "boolean" + }, + "kind": { + "type": "string", + "enum": [ + "eas" + ] + } + }, + "required": [ + "id", + "label", + "platforms", + "hubInstalled", + "agentDeviceInstalled", + "kind" + ], + "additionalProperties": true + }, + "Editor": { + "type": "string", + "enum": [ + "cursor", + "trae", + "kiro", + "vscode", + "vscode-insiders", + "vscodium", + "zed", + "antigravity", + "idea", + "aqua", + "clion", + "datagrip", + "dataspell", + "goland", + "phpstorm", + "pycharm", + "rider", + "rubymine", + "rustrover", + "webstorm", + "file-manager" + ] + }, + "EndedWatch": { + "type": "object", + "properties": { + "startedAt": { + "type": "string" + }, + "headSha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "failedChecks": { + "type": "array", + "items": { + "type": "string" + } + }, + "passed": { + "type": "boolean" + }, + "passedChecks": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "owedChecks": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "remarksThrough": { + "type": "string" + }, + "remarkIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "conflicting": { + "type": "boolean" + }, + "wakes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "checksAwaitedSince": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "instructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "startedAt", + "headSha", + "failedChecks", + "passed", + "remarksThrough", + "remarkIds", + "conflicting", + "wakes" + ], + "additionalProperties": true + }, + "Environment": { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "label": { + "type": "string" + }, + "platform": { + "type": "object", + "properties": { + "os": { + "type": "string", + "enum": [ + "darwin", + "linux", + "windows", + "unknown" + ] + }, + "arch": { + "type": "string", + "enum": [ + "arm64", + "x64", + "other" + ] + }, + "machine": {} + }, + "required": [ + "os", + "arch" + ], + "additionalProperties": true + }, + "serverVersion": { + "type": "string" + }, + "orchestrationProtocolVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "databaseMigration": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "capabilities": { + "type": "object", + "properties": { + "repositoryIdentity": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "connectionProbe": { + "type": "boolean" + }, + "attachmentUploads": { + "type": "boolean" + }, + "questionAttachments": { + "type": "boolean" + }, + "fileAttachments": { + "type": "object", + "properties": { + "maxUploadBytes": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "maxUploadBytes" + ], + "additionalProperties": true + }, + "pullRequests": { + "type": "boolean" + }, + "pullRequestChecks": { + "type": "boolean" + }, + "sideChats": { + "type": "boolean" + }, + "inlineMessageContext": { + "type": "boolean" + }, + "requiredWorktreeBootstrap": { + "type": "boolean" + }, + "threadSettlement": { + "type": "boolean" + }, + "threadAutoSettlement": { + "type": "boolean" + }, + "storageCleanup": { + "type": "boolean" + }, + "storageCleanupRun": { + "type": "boolean" + }, + "databaseStorage": { + "type": "boolean" + }, + "otlpEndpointCheck": { + "type": "boolean" + }, + "projectWorktreeCleanup": { + "type": "boolean" + }, + "settledWorktreeCleanup": { + "type": "boolean" + }, + "worktreesDirectory": { + "type": "boolean" + }, + "threadAutoSettlementScope": { + "type": "boolean" + }, + "threadRestartContinuation": { + "type": "boolean" + }, + "projectSettingsOverrides": { + "type": "boolean" + }, + "projectProviderInstanceOverrides": { + "type": "boolean" + }, + "threadSnooze": { + "type": "boolean" + }, + "threadForceStop": { + "type": "boolean" + }, + "environmentThemes": { + "type": "boolean" + }, + "usageLimitSources": { + "type": "boolean" + }, + "providerWorkspaceSnapshotsOnDemand": { + "type": "boolean" + }, + "usagePriceOverrides": { + "type": "boolean" + }, + "usageModelAliases": { + "type": "boolean" + }, + "threadPinning": { + "type": "boolean" + }, + "threadPinReorder": { + "type": "boolean" + }, + "threadActiveReorder": { + "type": "boolean" + }, + "threadAutoSettleOptOut": { + "type": "boolean" + }, + "threadProjectFromScratch": { + "type": "boolean" + }, + "threadTitleRegeneration": { + "type": "boolean" + }, + "threadVisitedTracking": { + "type": "boolean" + }, + "threadPullRequestLinking": { + "type": "boolean" + }, + "serverResolvedCommandContext": { + "type": "boolean" + }, + "threadStop": { + "type": "boolean" + }, + "localServerStop": { + "type": "boolean" + }, + "threadPullRequests": { + "type": "boolean" + }, + "threadPullRequestWatch": { + "type": "boolean" + }, + "pullRequestStackActions": { + "type": "boolean" + }, + "serverSelfUpdate": { + "type": "string", + "enum": [ + "boot-service", + "respawn", + "desktop-managed" + ] + }, + "npmReleaseLookup": { + "type": "boolean" + }, + "serverInstallation": {}, + "serverSelfUpdateProgress": { + "type": "boolean" + }, + "serverUpdateThreadContinuation": { + "type": "boolean" + }, + "agentActivityPublishing": { + "type": "boolean" + }, + "projectCloneTracking": { + "type": "boolean" + }, + "projectOrganization": { + "type": "boolean" + }, + "ignoredFileListing": { + "type": "boolean" + }, + "environmentIcon": { + "type": "boolean" + }, + "desktopAppUpdate": { + "type": "boolean" + }, + "desktopAppChannelSwitch": { + "type": "boolean" + }, + "commandOutputStreaming": { + "type": "boolean" + }, + "serverBrowser": { + "type": "boolean" + }, + "peerLinks": { + "type": "boolean" + }, + "mcpModeLimitHeader": { + "type": "boolean" + }, + "linkFence": { + "type": "boolean" + }, + "triage": { + "type": "boolean" + }, + "oneTimeSchedules": { + "type": "boolean" + } + }, + "additionalProperties": true + } + }, + "required": [ + "environmentId", + "label", + "platform", + "serverVersion", + "capabilities" + ], + "additionalProperties": true + }, + "EnvironmentAuthorizationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "EnvironmentAuthorizationError" + ] + }, + "message": { + "type": "string" + }, + "requiredScope": { + "$ref": "#/$defs/PermissionsEntry" + }, + "requiredPermission": { + "type": "string" + } + }, + "required": [ + "_tag", + "message", + "requiredScope" + ], + "additionalProperties": true + }, + "EnvironmentThemes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "version": { + "anyOf": [ + { + "type": "number", + "enum": [ + 1 + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "appearance": { + "type": "string", + "enum": [ + "light", + "dark" + ] + }, + "canvas": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "accent": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "colors": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "variants": { + "anyOf": [ + { + "type": "object", + "properties": { + "light": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "dark": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "name", + "appearance" + ], + "additionalProperties": true + } + }, + "ExternalLauncherBrowserSpawnErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ExternalLauncherBrowserSpawnError" + ] + }, + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "cause": {}, + "target": { + "type": "string" + } + }, + "required": [ + "_tag", + "command", + "args", + "cause", + "target" + ], + "additionalProperties": true + }, + "ExternalLauncherCommandNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ExternalLauncherCommandNotFoundError" + ] + }, + "editor": { + "$ref": "#/$defs/Editor" + }, + "command": { + "type": "string" + } + }, + "required": [ + "_tag", + "editor", + "command" + ], + "additionalProperties": true + }, + "ExternalLauncherEditorSpawnErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ExternalLauncherEditorSpawnError" + ] + }, + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "cause": {}, + "editor": { + "$ref": "#/$defs/Editor" + }, + "target": { + "type": "string" + } + }, + "required": [ + "_tag", + "command", + "args", + "cause", + "editor", + "target" + ], + "additionalProperties": true + }, + "ExternalLauncherUnknownEditorErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ExternalLauncherUnknownEditorError" + ] + }, + "editor": { + "type": "string" + } + }, + "required": [ + "_tag", + "editor" + ], + "additionalProperties": true + }, + "ExternalLauncherUnsupportedEditorErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ExternalLauncherUnsupportedEditorError" + ] + }, + "editor": { + "$ref": "#/$defs/Editor" + } + }, + "required": [ + "_tag", + "editor" + ], + "additionalProperties": true + }, + "ExternalLauncherUnsupportedTargetErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ExternalLauncherUnsupportedTargetError" + ] + }, + "editor": { + "$ref": "#/$defs/Editor" + } + }, + "required": [ + "_tag", + "editor" + ], + "additionalProperties": true + }, + "Failure": { + "type": "object", + "properties": { + "class": { + "type": "string", + "enum": [ + "usage_limit", + "provider_error", + "transport_error", + "permission_error", + "validation_error", + "unknown" + ] + }, + "message": { + "type": "string" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "retryable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "resetAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "class", + "message", + "code", + "retryable" + ], + "additionalProperties": true + }, + "Failure2": { + "type": "string", + "enum": [ + "workspace_root_not_found", + "workspace_root_create_failed", + "workspace_root_stat_failed", + "workspace_root_not_directory", + "search_index_create_failed", + "search_index_scan_timed_out", + "search_index_search_failed", + "directory_list_failed" + ] + }, + "Files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "additions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "path", + "kind", + "additions", + "deletions" + ], + "additionalProperties": true + } + }, + "FilesTotal": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "additions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "count", + "additions", + "deletions" + ], + "additionalProperties": true + }, + "FilesystemBrowseError": { + "anyOf": [ + { + "$ref": "#/$defs/FilesystemBrowseErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "FilesystemBrowseErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "FilesystemBrowseError" + ] + }, + "partialPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "failure": { + "anyOf": [ + { + "type": "string", + "enum": [ + "windows_path_unsupported", + "current_project_required", + "read_directory_failed" + ] + }, + { + "type": "null" + } + ] + }, + "parentPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "platform": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "FilesystemBrowsePayload": { + "type": "object", + "properties": { + "partialPath": { + "type": "string" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "partialPath" + ], + "additionalProperties": true + }, + "FilesystemBrowseSuccess": { + "type": "object", + "properties": { + "parentPath": { + "type": "string" + }, + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "fullPath": { + "type": "string" + } + }, + "required": [ + "name", + "fullPath" + ], + "additionalProperties": true + } + } + }, + "required": [ + "parentPath", + "entries" + ], + "additionalProperties": true + }, + "FilesystemGetMetadataPayload": { + "type": "object", + "properties": { + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 64 + } + }, + "required": [ + "paths" + ], + "additionalProperties": true + }, + "FilesystemGetMetadataSuccess": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "file", + "directory", + "other" + ] + }, + "byteLength": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "mimeType": { + "type": "string" + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + } + }, + "required": [ + "entries" + ], + "additionalProperties": true + }, + "Filters": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "equals", + "notEquals", + "startsWith" + ] + }, + "value": { + "type": "string" + } + }, + "required": [ + "path", + "op", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "in" + ] + }, + "value": { + "type": "array", + "items": { + "type": "string", + "description": "Non-empty, without commas or surrounding whitespace." + }, + "minItems": 1 + } + }, + "required": [ + "path", + "op", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "exists" + ] + } + }, + "required": [ + "path", + "op" + ], + "additionalProperties": true + } + ], + "description": "Path: `headers.x-event` or `body.action`. String compare. Objects pass only 'exists'. A `[]` segment matches any array item. `notEquals` passes only if no item equals." + }, + "maxItems": 16, + "description": "All must match. Max 16. Omitted: keep stored. [] clears." + }, + "ForkedFrom": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "provider_thread" + ] + }, + "providerThreadId": { + "type": "string" + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "providerThreadId" + ], + "additionalProperties": true + }, + "GitCommandErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "GitCommandError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "exitCode": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "stdoutLength": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "stderrLength": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "outputLength": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "reason": {}, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "command", + "cwd", + "detail" + ], + "additionalProperties": true + }, + "GitManagerErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "GitManagerError" + ] + }, + "operation": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "cwd", + "detail" + ], + "additionalProperties": true + }, + "GitPreparePullRequestThreadError": { + "anyOf": [ + { + "$ref": "#/$defs/GitManagerErrorEncoded" + }, + { + "$ref": "#/$defs/GitPullRequestMaterializationErrorEncoded" + }, + { + "$ref": "#/$defs/GitCommandErrorEncoded" + }, + { + "$ref": "#/$defs/SourceControlProviderErrorEncoded" + }, + { + "$ref": "#/$defs/TextGenerationErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "GitPreparePullRequestThreadPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "local", + "worktree" + ] + }, + "threadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "reference", + "mode" + ], + "additionalProperties": true + }, + "GitPreparePullRequestThreadSuccess": { + "type": "object", + "properties": { + "pullRequest": { + "$ref": "#/$defs/PullRequest" + }, + "branch": { + "type": "string" + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "isOnPullRequestHead": { + "type": "boolean" + } + }, + "required": [ + "pullRequest", + "branch", + "worktreePath" + ], + "additionalProperties": true + }, + "GitPullRequestMaterializationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "GitPullRequestMaterializationError" + ] + }, + "cwd": { + "type": "string" + }, + "pullRequestNumber": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "headRepository": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "headBranch": { + "type": "string" + }, + "localBranch": { + "type": "string" + }, + "cause": {} + }, + "required": [ + "_tag", + "cwd", + "pullRequestNumber", + "headRepository", + "headBranch", + "localBranch", + "cause" + ], + "additionalProperties": true + }, + "GitResolvePullRequestPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "reference": { + "type": "string" + } + }, + "required": [ + "cwd", + "reference" + ], + "additionalProperties": true + }, + "GitResolvePullRequestSuccess": { + "type": "object", + "properties": { + "pullRequest": { + "$ref": "#/$defs/PullRequest" + } + }, + "required": [ + "pullRequest" + ], + "additionalProperties": true + }, + "GitRunStackedActionPayload": { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "commitMessage": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "featureBranch": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "filePaths": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 + }, + { + "type": "null" + } + ] + }, + "threadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "projectId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "actionId", + "cwd", + "action" + ], + "additionalProperties": true + }, + "GitRunStackedActionSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "kind": { + "type": "string", + "enum": [ + "action_started" + ] + }, + "phases": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "branch", + "commit", + "push", + "pr" + ] + } + } + }, + "required": [ + "actionId", + "cwd", + "action", + "kind", + "phases" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "kind": { + "type": "string", + "enum": [ + "phase_started" + ] + }, + "phase": { + "type": "string", + "enum": [ + "branch", + "commit", + "push", + "pr" + ] + }, + "label": { + "type": "string" + } + }, + "required": [ + "actionId", + "cwd", + "action", + "kind", + "phase", + "label" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "kind": { + "type": "string", + "enum": [ + "hook_started" + ] + }, + "hookName": { + "type": "string" + } + }, + "required": [ + "actionId", + "cwd", + "action", + "kind", + "hookName" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "kind": { + "type": "string", + "enum": [ + "hook_output" + ] + }, + "hookName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "stream": { + "type": "string", + "enum": [ + "stdout", + "stderr" + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "actionId", + "cwd", + "action", + "kind", + "hookName", + "stream", + "text" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "kind": { + "type": "string", + "enum": [ + "hook_finished" + ] + }, + "hookName": { + "type": "string" + }, + "exitCode": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "durationMs": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "actionId", + "cwd", + "action", + "kind", + "hookName", + "exitCode", + "durationMs" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "kind": { + "type": "string", + "enum": [ + "action_finished" + ] + }, + "result": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "branch": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "created", + "skipped_not_requested" + ] + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": true + }, + "commit": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "created", + "skipped_no_changes", + "skipped_not_requested" + ] + }, + "commitSha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "subject": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": true + }, + "push": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pushed", + "skipped_not_requested", + "skipped_up_to_date" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "upstreamBranch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "setUpstream": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": true + }, + "pr": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "created", + "opened_existing", + "skipped_not_requested" + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "number": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "baseBranch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "headBranch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": true + }, + "toast": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cta": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "none" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "open_pr" + ] + }, + "label": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "kind", + "label", + "url" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "run_action" + ] + }, + "label": { + "type": "string" + }, + "action": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + } + }, + "required": [ + "kind", + "label", + "action" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "title", + "cta" + ], + "additionalProperties": true + } + }, + "required": [ + "action", + "branch", + "commit", + "push", + "pr", + "toast" + ], + "additionalProperties": true + } + }, + "required": [ + "actionId", + "cwd", + "action", + "kind", + "result" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "actionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "commit", + "push", + "create_pr", + "commit_push", + "commit_push_pr" + ] + }, + "kind": { + "type": "string", + "enum": [ + "action_failed" + ] + }, + "phase": { + "anyOf": [ + { + "type": "string", + "enum": [ + "branch", + "commit", + "push", + "pr" + ] + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "actionId", + "cwd", + "action", + "kind", + "phase", + "message" + ], + "additionalProperties": true + } + ] + }, + "Goal": { + "type": "object", + "properties": { + "objective": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "paused", + "blocked", + "usage_limited", + "budget_limited", + "complete" + ] + }, + "tokensUsed": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "tokenBudget": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "timeUsedSeconds": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "checks": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "lastCheck": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "objective", + "status" + ], + "additionalProperties": true + }, + "GuestShare": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "host": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "expiresAt": { + "type": "string" + }, + "guestName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "sent", + "dismissed" + ] + }, + "submittedAt": { + "type": "string" + }, + "attachments": { + "anyOf": [ + { + "$ref": "#/$defs/Attachments" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "text", + "status", + "submittedAt" + ], + "additionalProperties": true + } + } + }, + "required": [ + "id", + "host", + "createdAt", + "expiresAt", + "guestName", + "messages" + ], + "additionalProperties": true + }, + "GuestShareCreateError": { + "anyOf": [ + { + "$ref": "#/$defs/GuestShareCreateErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "GuestShareCreateErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "GuestShareCreateError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "disabled", + "connect_unavailable", + "thread_unavailable", + "read_failed", + "changed" + ] + } + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "GuestShareCreatePayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "GuestShareCreateSuccess": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "share": { + "$ref": "#/$defs/GuestShare" + } + }, + "required": [ + "url", + "share" + ], + "additionalProperties": true + }, + "Health": { + "type": "object", + "properties": { + "native": { + "$ref": "#/$defs/Desktop" + }, + "desktop": { + "$ref": "#/$defs/Desktop" + }, + "sidecarVersion": { + "$ref": "#/$defs/LastError" + }, + "sidecarPid": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + }, + "restartCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "collectionDurationMicros": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "scannedProcessCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "retainedProcessCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "inaccessibleProcessCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "native", + "desktop", + "sidecarVersion", + "sidecarPid", + "restartCount", + "collectionDurationMicros", + "scannedProcessCount", + "retainedProcessCount", + "inaccessibleProcessCount" + ], + "additionalProperties": true + }, + "HostsEntry": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "platforms": { + "$ref": "#/$defs/Platforms" + }, + "tools": { + "anyOf": [ + { + "$ref": "#/$defs/Tools" + }, + { + "type": "null" + } + ] + }, + "toolInspectionError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hubInstalled": { + "type": "boolean" + }, + "agentDeviceInstalled": { + "type": "boolean" + }, + "kind": { + "type": "string", + "enum": [ + "local", + "ssh" + ] + } + }, + "required": [ + "id", + "label", + "platforms", + "hubInstalled", + "agentDeviceInstalled", + "kind" + ], + "additionalProperties": true + }, + "Icon": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "website" + ] + }, + "pageUrl": { + "type": "string" + }, + "faviconUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "faviconUrlDark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "pageUrl" + ], + "additionalProperties": true + }, + "Icon2": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "native-app" + ] + }, + "app": { + "$ref": "#/$defs/App" + } + }, + "required": [ + "_tag", + "app" + ], + "additionalProperties": true + }, + "Icon3": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "themed-logo" + ] + }, + "logoUrl": { + "type": "string" + }, + "logoUrlDark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "logoUrl" + ], + "additionalProperties": true + }, + "Identity2": { + "type": "object", + "properties": { + "pid": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "startTimeMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "pid", + "startTimeMs" + ], + "additionalProperties": true + }, + "ImageSize": { + "type": "object", + "properties": { + "width": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "height": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "width", + "height" + ], + "additionalProperties": true + }, + "Issues": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "keybindings.malformed-config" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "kind", + "message" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "keybindings.invalid-entry" + ] + }, + "message": { + "type": "string" + }, + "index": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "kind", + "message", + "index" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "Item": { + "anyOf": [ + { + "$ref": "#/$defs/Item2" + }, + { + "$ref": "#/$defs/Item3" + }, + { + "$ref": "#/$defs/Item4" + }, + { + "$ref": "#/$defs/Item5" + }, + { + "$ref": "#/$defs/Item6" + }, + { + "$ref": "#/$defs/Item7" + }, + { + "$ref": "#/$defs/Item8" + }, + { + "$ref": "#/$defs/Item9" + }, + { + "$ref": "#/$defs/Item10" + }, + { + "$ref": "#/$defs/Item11" + }, + { + "$ref": "#/$defs/Item12" + }, + { + "$ref": "#/$defs/Item13" + }, + { + "$ref": "#/$defs/Item14" + }, + { + "$ref": "#/$defs/Item15" + }, + { + "$ref": "#/$defs/Item16" + }, + { + "$ref": "#/$defs/Item17" + }, + { + "$ref": "#/$defs/Item18" + }, + { + "$ref": "#/$defs/Item19" + }, + { + "$ref": "#/$defs/Item20" + }, + { + "$ref": "#/$defs/Item21" + }, + { + "$ref": "#/$defs/Item22" + }, + { + "$ref": "#/$defs/Item23" + }, + { + "$ref": "#/$defs/Item24" + }, + { + "$ref": "#/$defs/Item25" + }, + { + "$ref": "#/$defs/Item26" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": true + } + ] + }, + "Item10": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "command_execution" + ] + }, + "input": { + "type": "string" + }, + "output": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "outputOmitted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "outputIndicatesFailure": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "exitCode": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "monitor": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "input" + ], + "additionalProperties": true + }, + "Item11": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "file_search" + ] + }, + "pattern": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "results": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "line": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "column": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "preview": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "fileName" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type" + ], + "additionalProperties": true + }, + "Item12": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "web_search" + ] + }, + "patterns": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "results": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "snippet": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type" + ], + "additionalProperties": true + }, + "Item13": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "approval_request" + ] + }, + "requestId": { + "type": "string" + }, + "requestKind": { + "type": "string", + "enum": [ + "command", + "file-read", + "file-change", + "mcp-elicitation", + "permission" + ] + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "fullAccessOverride": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ask_rule", + "provider_check" + ] + }, + { + "type": "null" + } + ] + }, + "appName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "options": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "decision": { + "type": "string", + "enum": [ + "accept", + "acceptForSession", + "acceptAlways", + "decline", + "cancel" + ] + }, + "label": { + "type": "string" + }, + "warning": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "decision", + "label" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "requestId", + "requestKind" + ], + "additionalProperties": true + }, + "Item14": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "checkpoint" + ] + }, + "checkpointId": { + "type": "string" + }, + "scopeId": { + "type": "string" + }, + "files": { + "$ref": "#/$defs/Files" + }, + "filesTotal": { + "$ref": "#/$defs/FilesTotal" + }, + "filesOmitted": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "checkpointId", + "scopeId", + "files" + ], + "additionalProperties": true + }, + "Item15": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "run_interrupt_request" + ] + }, + "message": { + "type": "string" + }, + "unresponsiveAt": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "message" + ], + "additionalProperties": true + }, + "Item16": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "run_interrupt_result" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "message" + ], + "additionalProperties": true + }, + "Item17": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "system_notice" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "message" + ], + "additionalProperties": true + }, + "Item18": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "error" + ] + }, + "failure": { + "$ref": "#/$defs/Failure" + }, + "retry": { + "anyOf": [ + { + "type": "object", + "properties": { + "attempt": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "maxAttempts": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "retryDelayMs": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "attempt", + "maxAttempts", + "retryDelayMs" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "failure" + ], + "additionalProperties": true + }, + "Item19": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "compaction" + ] + }, + "driver": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "beforeTokenCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "afterTokenCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "driver" + ], + "additionalProperties": true + }, + "Item2": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "notification" + ] + }, + "source": { + "$ref": "#/$defs/Source2" + }, + "outcome": { + "type": "string", + "enum": [ + "completed", + "failed", + "cancelled", + "updated", + "unknown" + ] + }, + "summary": { + "type": "string" + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "source", + "outcome", + "summary" + ], + "additionalProperties": true + }, + "Item20": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "handoff" + ] + }, + "contextHandoffId": { + "type": "string" + }, + "fromProviderThreadIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "toProviderThreadId": { + "type": "string" + }, + "fromProviderInstanceIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "toProviderInstanceId": { + "type": "string" + }, + "fromModelSelections": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/ModelSelection" + } + }, + { + "type": "null" + } + ] + }, + "toModel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "strategy": { + "type": "string", + "enum": [ + "delta_since_target_last_seen", + "fork_delta_summary", + "full_thread_summary", + "checkpoint_summary", + "manual_context" + ] + }, + "summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "contextHandoffId", + "fromProviderThreadIds", + "toProviderThreadId", + "fromProviderInstanceIds", + "toProviderInstanceId", + "strategy" + ], + "additionalProperties": true + }, + "Item21": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "fork" + ] + }, + "source": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "run" + ] + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "node" + ] + }, + "nodeId": { + "type": "string" + } + }, + "required": [ + "type", + "nodeId" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/ForkedFrom" + } + ] + }, + "targetThreadId": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "source", + "targetThreadId" + ], + "additionalProperties": true + }, + "Item22": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "thread_created" + ] + }, + "targetThreadId": { + "type": "string" + }, + "targetRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "targetProviderInstanceId": { + "type": "string" + }, + "targetModel": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "targetThreadId", + "targetRunId", + "targetProviderInstanceId", + "targetModel" + ], + "additionalProperties": true + }, + "Item23": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "secret_request" + ] + }, + "label": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "placeholder": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "delivery": { + "anyOf": [ + { + "type": "string", + "enum": [ + "file" + ] + }, + { + "type": "null" + } + ] + }, + "secretStatus": { + "type": "string", + "enum": [ + "pending", + "saved", + "declined", + "cancelled" + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "label", + "reason", + "secretStatus" + ], + "additionalProperties": true + }, + "Item24": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "link_request" + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reason": { + "type": "string" + }, + "environmentId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "requestedAccess": { + "anyOf": [ + { + "type": "string", + "enum": [ + "read-only", + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + { + "type": "null" + } + ] + }, + "linkStatus": { + "type": "string", + "enum": [ + "pending", + "linked", + "declined", + "cancelled", + "failed" + ] + }, + "linkedEnvironmentId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "linkedLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "linkedAccess": { + "anyOf": [ + { + "type": "string", + "enum": [ + "read-only", + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + { + "type": "null" + } + ] + }, + "failure": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "reason", + "linkStatus" + ], + "additionalProperties": true + }, + "Item25": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "subagent" + ] + }, + "subagentId": { + "type": "string" + }, + "origin": { + "type": "string", + "enum": [ + "provider_native", + "app_owned" + ] + }, + "driver": { + "type": "string" + }, + "providerInstanceId": { + "type": "string" + }, + "childThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "remoteChild": { + "anyOf": [ + { + "$ref": "#/$defs/RemoteChild" + }, + { + "type": "null" + } + ] + }, + "prompt": { + "type": "string" + }, + "progress": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "result": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "promptOmitted": { + "type": "boolean", + "enum": [ + true + ] + }, + "resultOmitted": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "subagentId", + "origin", + "driver", + "providerInstanceId", + "childThreadId", + "prompt", + "result" + ], + "additionalProperties": true + }, + "Item26": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "dynamic_tool" + ] + }, + "toolName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "viewedImagePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "input": {}, + "output": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "outputOmitted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "toolName", + "input" + ], + "additionalProperties": true + }, + "Item3": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "type": { + "type": "string", + "enum": [ + "user_message" + ] + }, + "messageId": { + "type": "string" + }, + "scheduledTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "idleCompaction": { + "anyOf": [ + { + "type": "string", + "enum": [ + "beforeCacheExpires", + "onReturn" + ] + }, + { + "type": "null" + } + ] + }, + "workflowScript": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "inputIntent": { + "type": "string", + "enum": [ + "turn_start", + "queued_turn", + "steer", + "promoted_queued_to_steer" + ] + }, + "text": { + "type": "string" + }, + "textOmitted": { + "type": "boolean", + "enum": [ + true + ] + }, + "context": { + "anyOf": [ + { + "$ref": "#/$defs/Context" + }, + { + "type": "null" + } + ] + }, + "attachments": { + "$ref": "#/$defs/Attachments" + }, + "modelSwitch": { + "anyOf": [ + { + "type": "object", + "properties": { + "from": { + "$ref": "#/$defs/ModelSelection" + }, + "to": { + "$ref": "#/$defs/ModelSelection" + } + }, + "required": [ + "from", + "to" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "createdBy", + "creationSource", + "type", + "messageId", + "inputIntent", + "text", + "attachments" + ], + "additionalProperties": true + }, + "Item4": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "assistant_message" + ] + }, + "messageId": { + "type": "string" + }, + "text": { + "type": "string" + }, + "textOmitted": { + "type": "boolean", + "enum": [ + true + ] + }, + "attachments": { + "anyOf": [ + { + "$ref": "#/$defs/Attachments" + }, + { + "type": "null" + } + ] + }, + "streaming": { + "type": "boolean" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "messageId", + "text", + "streaming" + ], + "additionalProperties": true + }, + "Item5": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "reasoning" + ] + }, + "text": { + "type": "string" + }, + "streaming": { + "type": "boolean" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "text", + "streaming" + ], + "additionalProperties": true + }, + "Item6": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "proposed_plan" + ] + }, + "planId": { + "type": "string" + }, + "markdown": { + "type": "string" + }, + "streaming": { + "type": "boolean" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "planId", + "markdown", + "streaming" + ], + "additionalProperties": true + }, + "Item7": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "todo_list" + ] + }, + "planId": { + "type": "string" + }, + "steps": { + "$ref": "#/$defs/Steps" + }, + "explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "planId", + "steps" + ], + "additionalProperties": true + }, + "Item8": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "user_input_request" + ] + }, + "requestId": { + "type": "string" + }, + "questions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "header": { + "type": "string" + }, + "question": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "preview": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "label", + "description" + ], + "additionalProperties": true + } + }, + "multiSelect": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "allowCustomAnswer": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "initialAnswer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "header", + "question", + "options" + ], + "additionalProperties": true + } + }, + "questionAnswer": { + "anyOf": [ + { + "type": "object", + "properties": { + "requestId": { + "type": "string" + }, + "questionTextById": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "answers": { + "type": "object" + }, + "attachmentsByQuestionId": { + "$ref": "#/$defs/AttachmentsByQuestionId" + } + }, + "required": [ + "requestId", + "answers", + "attachmentsByQuestionId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "responseMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "message" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "requestId", + "questions" + ], + "additionalProperties": true + }, + "Item9": { + "type": "object", + "properties": { + "deferredWork": { + "$ref": "#/$defs/DeferredWork" + }, + "toolNonExecutionKind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolSurface": { + "anyOf": [ + { + "type": "string", + "enum": [ + "browser", + "computer" + ] + }, + { + "type": "null" + } + ] + }, + "toolIcon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + }, + "toolSource": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSource" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "parentItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "file_change" + ] + }, + "fileName": { + "type": "string" + }, + "additions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "deletions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "diffStr": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "oldStr": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "newStr": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "changes": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "operation": { + "type": "string" + }, + "path": { + "type": "string" + }, + "oldPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "fileType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "mimeType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "operation", + "path" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "parentItemId", + "ordinal", + "status", + "title", + "startedAt", + "completedAt", + "updatedAt", + "type", + "fileName" + ], + "additionalProperties": true + }, + "Keybindings": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "command": { + "$ref": "#/$defs/Command" + }, + "shortcut": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "metaKey": { + "type": "boolean" + }, + "ctrlKey": { + "type": "boolean" + }, + "shiftKey": { + "type": "boolean" + }, + "altKey": { + "type": "boolean" + }, + "modKey": { + "type": "boolean" + } + }, + "required": [ + "key", + "metaKey", + "ctrlKey", + "shiftKey", + "altKey", + "modKey" + ], + "additionalProperties": true + }, + "whenAst": { + "anyOf": [ + { + "$ref": "#/$defs/Union_" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "command", + "shortcut" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "KeybindingsConfigParseErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "KeybindingsConfigParseError" + ] + }, + "configPath": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "configPath", + "detail" + ], + "additionalProperties": true + }, + "LastError": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "type": "string" + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + }, + "LimitRecovery": { + "type": "object", + "properties": { + "requestId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "runId": { + "type": "string" + }, + "resetAt": { + "type": "string" + }, + "autoResume": { + "type": "boolean" + }, + "snooze": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "runId", + "resetAt", + "autoResume" + ], + "additionalProperties": true + }, + "Lineage": { + "type": "object", + "properties": { + "parentThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "relationshipToParent": { + "anyOf": [ + { + "type": "string", + "enum": [ + "fork", + "subagent" + ] + }, + { + "type": "null" + } + ] + }, + "rootThreadId": { + "type": "string" + }, + "sideChat": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "parentThreadId", + "relationshipToParent", + "rootThreadId" + ], + "additionalProperties": true + }, + "LinkOrigin": { + "type": "object", + "properties": { + "sessionId": { + "type": "string" + }, + "label": { + "type": "string" + }, + "access": { + "anyOf": [ + { + "type": "string", + "enum": [ + "read-only", + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "sessionId", + "label" + ], + "additionalProperties": true + }, + "LinkedPullRequest": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "url": { + "type": "string" + } + }, + "required": [ + "projectId", + "repository", + "number", + "url" + ], + "additionalProperties": true + }, + "Local": { + "type": "object", + "properties": { + "isRepo": { + "type": "boolean" + }, + "sourceControlProvider": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "baseUrl": { + "type": "string" + } + }, + "required": [ + "kind", + "name", + "baseUrl" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "hasPrimaryRemote": { + "type": "boolean" + }, + "isDefaultRef": { + "type": "boolean" + }, + "refName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hasWorkingTreeChanges": { + "type": "boolean" + }, + "workingTree": { + "$ref": "#/$defs/WorkingTree" + }, + "branchChanges": { + "anyOf": [ + { + "$ref": "#/$defs/BranchChanges" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "isRepo", + "hasPrimaryRemote", + "isDefaultRef", + "refName", + "hasWorkingTreeChanges", + "workingTree" + ], + "additionalProperties": true + }, + "McpAppRequestErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "McpAppRequestError" + ] + }, + "threadId": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "not-an-app", + "provider-unsupported", + "session-stopped", + "tool-not-callable", + "unsupported-content", + "request-failed" + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "threadId", + "reason" + ], + "additionalProperties": true + }, + "McpAppsCallToolError": { + "anyOf": [ + { + "$ref": "#/$defs/McpAppRequestErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "McpAppsCallToolPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "arguments": { + "type": "object" + } + }, + "required": [ + "threadId", + "itemId", + "name", + "arguments" + ], + "additionalProperties": true + }, + "McpAppsCallToolSuccess": { + "type": "object", + "properties": { + "content": { + "type": "array" + }, + "structuredContent": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "isError": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "_meta": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "content" + ], + "additionalProperties": true + }, + "McpAppsReadResourcePayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "uri": { + "type": "string" + } + }, + "required": [ + "threadId", + "itemId", + "uri" + ], + "additionalProperties": true + }, + "McpAppsReadResourceSuccess": { + "type": "object", + "properties": { + "contents": { + "type": "array" + } + }, + "required": [ + "contents" + ], + "additionalProperties": true + }, + "McpAppsToolInfoPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "threadId", + "itemId", + "name" + ], + "additionalProperties": true + }, + "McpAppsToolInfoSuccess": { + "type": "object", + "properties": { + "callable": { + "type": "boolean" + }, + "readOnly": { + "type": "boolean" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tool": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "callable", + "readOnly" + ], + "additionalProperties": true + }, + "McpAppsUpdateModelContextPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "conversationThreadId": { + "type": "string" + }, + "content": { + "anyOf": [ + { + "type": "array" + }, + { + "type": "null" + } + ] + }, + "structuredContent": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "itemId", + "conversationThreadId" + ], + "additionalProperties": true + }, + "ModelFilter": { + "type": "object", + "properties": { + "include": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "model": { + "type": "string" + } + }, + "required": [ + "model" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + }, + "exclude": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "model": { + "type": "string" + } + }, + "required": [ + "model" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "ModelSelection": { + "type": "object", + "properties": { + "provider": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "instanceId": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "model": {}, + "options": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "model" + ], + "additionalProperties": true + }, + "NativeItemRef": { + "type": "object", + "properties": { + "driver": { + "type": "string" + }, + "nativeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "strength": { + "type": "string", + "enum": [ + "strong", + "weak", + "none" + ] + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "driver", + "nativeId", + "strength" + ], + "additionalProperties": true + }, + "NativeMetadata": { + "type": "object", + "properties": { + "modelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "itemIdentityVersion": { + "anyOf": [ + { + "type": "number", + "enum": [ + 2 + ] + }, + { + "type": "null" + } + ] + }, + "forkHeadMessageId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "claudeAttachmentBudget": { + "anyOf": [ + { + "type": "object", + "properties": { + "bytesUsed": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "pagesUsed": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "bytesUsed", + "pagesUsed" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "NavStatus": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Idle" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Loading" + ] + }, + "url": { + "type": "string" + }, + "title": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "_tag", + "url", + "title" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Success" + ] + }, + "url": { + "type": "string" + }, + "title": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "_tag", + "url", + "title" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "LoadFailed" + ] + }, + "url": { + "type": "string" + }, + "title": { + "type": "string", + "maxLength": 512 + }, + "code": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "description": { + "type": "string" + }, + "download": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "fileName": { + "type": "string" + } + }, + "required": [ + "id", + "fileName" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "url", + "title", + "code", + "description" + ], + "additionalProperties": true + } + ] + }, + "Notification": { + "type": "object", + "properties": { + "source": { + "$ref": "#/$defs/Source2" + }, + "outcome": { + "type": "string", + "enum": [ + "completed", + "failed", + "cancelled", + "updated", + "unknown" + ] + }, + "summary": { + "type": "string" + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "source", + "outcome", + "summary" + ], + "additionalProperties": true + }, + "Objects_": { + "type": "object", + "properties": { + "role": { + "type": "string" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "description": { + "type": "string" + }, + "bounds": { + "anyOf": [ + { + "type": "object", + "properties": { + "x": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "y": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "width": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "height": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "x", + "y", + "width", + "height" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "state": { + "type": "object", + "properties": { + "active": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "busy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "checked": { + "anyOf": [ + { + "type": "string", + "enum": [ + "on", + "off", + "mixed" + ] + }, + { + "type": "null" + } + ] + }, + "editable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "enabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "expanded": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "focused": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "selected": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "visible": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 32 + }, + "children": { + "type": "array", + "items": { + "$ref": "#/$defs/Objects_" + }, + "maxItems": 10000 + } + }, + "required": [ + "role", + "bounds", + "children" + ], + "additionalProperties": true + }, + "Opens": { + "type": "object", + "properties": { + "openedAt": { + "type": "number" + }, + "score": { + "type": "number", + "minimum": 0 + }, + "picks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "query": { + "type": "string", + "maxLength": 32 + }, + "at": { + "type": "number" + } + }, + "required": [ + "query", + "at" + ], + "additionalProperties": true + }, + "maxItems": 3 + } + }, + "required": [ + "openedAt", + "score", + "picks" + ], + "additionalProperties": true + }, + "OptionDescriptors": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "select" + ] + }, + "options": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "isDefault": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "label" + ], + "additionalProperties": true + } + }, + "currentValue": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "promptInjectedValues": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "role": { + "anyOf": [ + { + "type": "string", + "enum": [ + "reasoning" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "label", + "type", + "options" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "boolean" + ] + }, + "currentValue": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "label", + "type" + ], + "additionalProperties": true + } + ] + } + }, + "OrchestrationDispatchCommandError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2DispatchCommandErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationDispatchCommandErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationDispatchCommandError" + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "bootstrapThreadDisposition": { + "anyOf": [ + { + "type": "string", + "enum": [ + "deleted", + "not-created" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "OrchestrationDispatchCommandPayload": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.stop" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "stopDelegatedTasks": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.create" + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "linkOrigin": { + "anyOf": [ + { + "$ref": "#/$defs/LinkOrigin" + }, + { + "type": "null" + } + ] + }, + "delegatedFrom": { + "anyOf": [ + { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "environmentId", + "threadId", + "title" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "importedNativeThread": { + "anyOf": [ + { + "type": "object", + "properties": { + "ref": { + "type": "object", + "properties": { + "driver": { + "type": "string" + }, + "nativeId": { + "type": "string" + }, + "strength": { + "type": "string", + "enum": [ + "strong" + ] + } + }, + "required": [ + "driver", + "nativeId", + "strength" + ], + "additionalProperties": true + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/$defs/NativeMetadata" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "ref" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "createdBy", + "creationSource", + "commandId", + "threadId", + "projectId", + "title", + "modelSelection", + "runtimeMode", + "interactionMode", + "branch", + "worktreePath" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.archive" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.guest-share.close" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.guest-message.dismiss" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "guestMessageId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "guestMessageId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.unarchive" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.delete" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "onlyIfUnused": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.settle" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "settledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.auto-settle" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "snapshotAt": { + "type": "string" + }, + "settledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "snapshotAt" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.unsettle" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "user" + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "reason" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.snooze" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "snoozedUntil": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "snoozedUntil" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.unsnooze" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "user" + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "reason" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.auto-settle.set" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "enabled" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.pin" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "orderKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.unpin" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.tag.set" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "tagged": { + "type": "boolean" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "tag", + "tagged" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.pin.reorder" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "orderKey": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "orderKey" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.active.reorder" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "orderKey": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "orderKey" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.visit" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "visitedAt": { + "type": "string" + }, + "opened": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "searchQuery": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "visitedAt" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.mark-unread" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.metadata.update" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "regenerateTitle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedWorktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedBranch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedEmpty": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "limitRecovery": { + "anyOf": [ + { + "type": "object", + "properties": { + "runId": { + "type": "string" + }, + "resetAt": { + "type": "string" + }, + "autoResume": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "snooze": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "runId", + "resetAt" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "linkedPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.pull-request.link" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "host": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "url": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "manual", + "created", + "agent", + "stack", + "stack-dismissed" + ] + }, + "repositoryWebUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "host", + "repository", + "number", + "url", + "source" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.pull-request.unlink" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "host": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "type", + "commandId", + "threadId", + "host", + "repository", + "number" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.pull-request-link.sync" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "host": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "snapshot": { + "$ref": "#/$defs/Snapshot" + }, + "stack": { + "anyOf": [ + { + "$ref": "#/$defs/Stack" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "host", + "repository", + "number", + "snapshot", + "stack" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.pull-request.watch" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "host": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "watching": { + "type": "boolean" + }, + "instructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "link": { + "anyOf": [ + { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "manual", + "created", + "agent", + "stack", + "stack-dismissed" + ] + }, + "repositoryWebUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "url", + "source" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "host", + "repository", + "number", + "watching" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.pull-request.sync" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "snapshotSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "expected": { + "type": "object", + "properties": { + "workspaceRoot": { + "type": "string" + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "linkedPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + }, + "branchPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "workspaceRoot", + "branch", + "worktreePath", + "linkedPullRequest", + "branchPullRequest" + ], + "additionalProperties": true + }, + "branchPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + }, + "linkedPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "projectId", + "snapshotSequence", + "expected", + "branchPullRequest" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.title.regeneration.complete" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "requestId": { + "type": "string" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "requestId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.runtime-mode.set" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runtimeMode" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.interaction-mode.set" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "interactionMode" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.model-selection.set" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "modelSelection" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "provider-session.detach" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "providerSessionId": { + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "providerSessionId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "message.dispatch" + ] + }, + "notification": { + "anyOf": [ + { + "$ref": "#/$defs/Notification" + }, + { + "type": "null" + } + ] + }, + "guestMessageId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "scheduledTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "idleCompaction": { + "anyOf": [ + { + "type": "string", + "enum": [ + "beforeCacheExpires", + "onReturn" + ] + }, + { + "type": "null" + } + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "messageId": { + "type": "string" + }, + "text": { + "type": "string" + }, + "context": { + "anyOf": [ + { + "$ref": "#/$defs/Context" + }, + { + "type": "null" + } + ] + }, + "attachments": { + "$ref": "#/$defs/Attachments" + }, + "titleSeed": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "modelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "sourcePlanRef": { + "anyOf": [ + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "planId": { + "type": "string" + } + }, + "required": [ + "threadId", + "planId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "restartContinuationOfRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "usageLimitContinuationOfRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "manualContinuationOfRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "usageLimitRecoveryRequestId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deliveryIntent": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto", + "steer", + "restart" + ] + }, + { + "type": "null" + } + ] + }, + "delegatedCompletion": { + "anyOf": [ + { + "$ref": "#/$defs/DelegatedCompletion" + }, + { + "type": "null" + } + ] + }, + "dispatchMode": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "defer_start" + ] + }, + "workspaceStrategy": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "root" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/WorkspaceStrategy" + }, + { + "$ref": "#/$defs/WorkspaceStrategy2" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "steer_active" + ] + }, + "targetRunId": { + "type": "string" + } + }, + "required": [ + "type", + "targetRunId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "restart_active" + ] + }, + "targetRunId": { + "type": "string" + } + }, + "required": [ + "type", + "targetRunId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "queue_after_active" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "start_immediately" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "type", + "createdBy", + "creationSource", + "commandId", + "threadId", + "messageId", + "text", + "attachments", + "dispatchMode" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "prepared-run.release" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "notification.delivery.accept" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "messageId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "messageId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "prepared-run.progress" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + }, + "phase": { + "type": "string", + "enum": [ + "worktree", + "setup" + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId", + "phase" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "prepared-run.fail" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + }, + "failure": { + "$ref": "#/$defs/Failure" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId", + "failure" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "prepared-run.retry" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "subagent.stop" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + }, + "subagentId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId", + "subagentId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "run.interrupt" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "holdQueue": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "queued-message.promote-to-steer" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "queuedRunId": { + "type": "string" + }, + "targetRunId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "queuedRunId", + "targetRunId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "queue.resume" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "queued-run.start" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "queued-run.reorder" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + }, + "beforeRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId", + "beforeRunId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "queued-run.cancel" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "queued-run.edit" + ] + }, + "context": { + "anyOf": [ + { + "$ref": "#/$defs/Context" + }, + { + "type": "null" + } + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + }, + "text": { + "type": "string" + }, + "attachments": { + "anyOf": [ + { + "$ref": "#/$defs/Attachments" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "runId", + "text" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "runtime-request.respond" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "requestId": { + "type": "string" + }, + "decision": { + "anyOf": [ + { + "type": "string", + "enum": [ + "accept", + "acceptForSession", + "acceptAlways", + "decline", + "cancel" + ] + }, + { + "type": "null" + } + ] + }, + "feedback": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "answers": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "attachmentsByQuestionId": { + "anyOf": [ + { + "$ref": "#/$defs/AttachmentsByQuestionId" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "threadId", + "requestId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.user-input.dismiss" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "requestId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "requestId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "checkpoint.rollback" + ] + }, + "restoreFiles": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "scopeId": { + "type": "string" + }, + "checkpointId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "scopeId", + "checkpointId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.fork" + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "commandId": { + "type": "string" + }, + "sourceThreadId": { + "type": "string" + }, + "targetThreadId": { + "type": "string" + }, + "sourcePoint": { + "$ref": "#/$defs/SourcePoint" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sideChat": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "createdBy", + "creationSource", + "commandId", + "sourceThreadId", + "targetThreadId", + "sourcePoint" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.merge_back" + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "commandId": { + "type": "string" + }, + "sourceThreadId": { + "type": "string" + }, + "targetThreadId": { + "type": "string" + }, + "sourcePoint": { + "$ref": "#/$defs/SourcePoint" + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "createdBy", + "creationSource", + "commandId", + "sourceThreadId", + "targetThreadId", + "sourcePoint" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "delegated_task.request" + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "commandId": { + "type": "string" + }, + "parentThreadId": { + "type": "string" + }, + "parentRunId": { + "type": "string" + }, + "parentNodeId": { + "type": "string" + }, + "task": { + "type": "string" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + }, + "workspaceStrategy": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "root" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/WorkspaceStrategy" + }, + { + "$ref": "#/$defs/WorkspaceStrategy2" + }, + { + "type": "null" + } + ] + }, + "completionWake": { + "anyOf": [ + { + "type": "string", + "enum": [ + "always", + "settled_only" + ] + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "createdBy", + "creationSource", + "commandId", + "parentThreadId", + "parentRunId", + "parentNodeId", + "task", + "modelSelection", + "runtimeMode", + "interactionMode" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "delegated_task.wake-policy" + ] + }, + "commandId": { + "type": "string" + }, + "parentThreadId": { + "type": "string" + }, + "taskId": { + "type": "string" + }, + "completionWake": { + "type": "string", + "enum": [ + "always", + "settled_only" + ] + } + }, + "required": [ + "type", + "commandId", + "parentThreadId", + "taskId", + "completionWake" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "delegated_task.completion-delivery.acknowledge" + ] + }, + "commandId": { + "type": "string" + }, + "parentThreadId": { + "type": "string" + }, + "taskId": { + "type": "string" + }, + "observedByRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "parentThreadId", + "taskId", + "observedByRunId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "delegated_task.completion-delivery.dispose" + ] + }, + "commandId": { + "type": "string" + }, + "parentThreadId": { + "type": "string" + }, + "taskId": { + "type": "string" + } + }, + "required": [ + "type", + "commandId", + "parentThreadId", + "taskId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread.created.record" + ] + }, + "commandId": { + "type": "string" + }, + "parentThreadId": { + "type": "string" + }, + "parentRunId": { + "type": "string" + }, + "parentNodeId": { + "type": "string" + }, + "targetThreadId": { + "type": "string" + }, + "targetRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "parentThreadId", + "parentRunId", + "parentNodeId", + "targetThreadId", + "targetRunId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "provider.switch" + ] + }, + "commandId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + } + }, + "required": [ + "type", + "commandId", + "threadId", + "modelSelection" + ], + "additionalProperties": true + } + ] + }, + "OrchestrationDispatchCommandSuccess": { + "type": "object", + "properties": { + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "sequence" + ], + "additionalProperties": true + }, + "OrchestrationForceStopThreadError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2ForceStopThreadErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationForceStopThreadPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "OrchestrationForceStopThreadSuccess": { + "type": "object", + "properties": { + "outcome": { + "type": "string", + "enum": [ + "killed", + "released", + "releasing", + "shared" + ] + }, + "sharedWithThreadIds": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "outcome", + "sharedWithThreadIds" + ], + "additionalProperties": true + }, + "OrchestrationGetArchivedShellSnapshotError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2GetShellSnapshotErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationGetArchivedShellSnapshotSuccess": { + "type": "object", + "properties": { + "schemaVersion": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "snapshotSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "projects": { + "$ref": "#/$defs/Projects" + }, + "threads": { + "$ref": "#/$defs/Threads" + } + }, + "required": [ + "schemaVersion", + "snapshotSequence", + "projects", + "threads" + ], + "additionalProperties": true + }, + "OrchestrationGetFullThreadDiffError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationGetFullThreadDiffErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationGetFullThreadDiffErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationGetFullThreadDiffError" + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "OrchestrationGetFullThreadDiffPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "toTurnCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ignoreWhitespace": { + "type": "boolean" + } + }, + "required": [ + "threadId", + "toTurnCount" + ], + "additionalProperties": true + }, + "OrchestrationGetNativeChildWorkError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2GetNativeChildWorkErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationGetNativeChildWorkSuccess": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "type": "object", + "properties": { + "conversationId": { + "type": "string" + }, + "role": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "steps": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "index": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "type": { + "type": "string" + }, + "status": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "text": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "toolCalls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "input": { + "type": "string" + } + }, + "required": [ + "name", + "input" + ], + "additionalProperties": true + } + } + }, + "required": [ + "index", + "type", + "status", + "text", + "toolCalls" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "conversationId", + "role", + "prompt", + "steps", + "truncated" + ], + "additionalProperties": true + } + } + }, + "required": [ + "children" + ], + "additionalProperties": true + }, + "OrchestrationGetOlderSettledThreadsPayload": { + "type": "object", + "properties": { + "before": { + "type": "object", + "properties": { + "settledAt": { + "type": "string" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "settledAt" + ], + "additionalProperties": true + }, + "projectIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "limit": { + "type": "integer", + "minimum": 0, + "maximum": 50 + } + }, + "required": [ + "before", + "limit" + ], + "additionalProperties": true + }, + "OrchestrationGetOlderSettledThreadsSuccess": { + "type": "object", + "properties": { + "snapshotSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "threads": { + "$ref": "#/$defs/Threads" + }, + "counts": { + "$ref": "#/$defs/Counts" + } + }, + "required": [ + "snapshotSequence", + "threads", + "counts" + ], + "additionalProperties": true + }, + "OrchestrationGetThreadFamilySuccess": { + "type": "object", + "properties": { + "snapshotSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "threads": { + "$ref": "#/$defs/Threads" + } + }, + "required": [ + "snapshotSequence", + "threads" + ], + "additionalProperties": true + }, + "OrchestrationGetThreadProjectionError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2GetThreadProjectionErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationGetThreadProjectionSuccess": { + "type": "object", + "properties": { + "thread": { + "type": "object", + "properties": { + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "providerInstanceId": { + "type": "string" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "linkedPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + }, + "pullRequests": { + "anyOf": [ + { + "$ref": "#/$defs/PullRequests" + }, + { + "type": "null" + } + ] + }, + "branchPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + }, + "activeProviderThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "historyOrigin": { + "anyOf": [ + { + "type": "string", + "enum": [ + "native", + "v1_import" + ] + }, + { + "type": "null" + } + ] + }, + "linkOrigin": { + "anyOf": [ + { + "$ref": "#/$defs/LinkOrigin" + }, + { + "type": "null" + } + ] + }, + "delegatedFrom": { + "anyOf": [ + { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "environmentId", + "threadId", + "title" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "lineage": { + "$ref": "#/$defs/Lineage" + }, + "forkedFrom": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "run" + ] + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "node" + ] + }, + "nodeId": { + "type": "string" + } + }, + "required": [ + "type", + "nodeId" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/ForkedFrom" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "archivedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "settledOverride": { + "anyOf": [ + { + "type": "string", + "enum": [ + "settled", + "active" + ] + }, + { + "type": "null" + } + ] + }, + "settledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "unsettledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "snoozedUntil": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "snoozedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastSnoozeWakeAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "limitRecovery": { + "anyOf": [ + { + "$ref": "#/$defs/LimitRecovery" + }, + { + "type": "null" + } + ] + }, + "guestShare": { + "anyOf": [ + { + "$ref": "#/$defs/GuestShare" + }, + { + "type": "null" + } + ] + }, + "pinnedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "autoSettleDisabledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pinOrderKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeOrderKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tags": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "lastVisitedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "opens": { + "anyOf": [ + { + "$ref": "#/$defs/Opens" + }, + { + "type": "null" + } + ] + }, + "titleRegeneration": { + "anyOf": [ + { + "type": "object", + "properties": { + "requestId": { + "type": "string" + }, + "startedAt": { + "type": "string" + } + }, + "required": [ + "requestId", + "startedAt" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "rollbackRequestId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "rollbackFailure": { + "anyOf": [ + { + "type": "object", + "properties": { + "requestId": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "requestId", + "message" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "createdBy", + "creationSource", + "id", + "projectId", + "title", + "providerInstanceId", + "modelSelection", + "runtimeMode", + "interactionMode", + "branch", + "worktreePath", + "activeProviderThreadId", + "lineage", + "forkedFrom", + "createdAt", + "updatedAt", + "archivedAt", + "deletedAt" + ], + "additionalProperties": true + }, + "runs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "ordinal": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "providerInstanceId": { + "type": "string" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "userMessageId": { + "type": "string" + }, + "rootNodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeAttemptId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "preparing", + "queued", + "starting", + "running", + "waiting", + "completed", + "interrupted", + "failed", + "cancelled", + "rolled_back" + ] + }, + "queuePosition": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "queueHeld": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "requestedAt": { + "type": "string" + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "checkpointId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contextHandoffId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "restartContinuationOfRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "delegatedTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workStartedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "restartCancelledBackgroundWork": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "subagent", + "shell", + "monitor", + "task" + ] + }, + "label": { + "type": "string" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "awaited": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind", + "label" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + }, + "sourcePlanRef": { + "anyOf": [ + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "planId": { + "type": "string" + } + }, + "required": [ + "threadId", + "planId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "delegatedCompletion": { + "anyOf": [ + { + "type": "object", + "properties": { + "disposition": { + "type": "string", + "enum": [ + "open", + "stopped", + "disposed" + ] + }, + "nextGeneration": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "delivery": { + "anyOf": [ + { + "type": "object", + "properties": { + "generation": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "messageId": { + "type": "string" + }, + "taskIds": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "generation", + "messageId", + "taskIds" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "disposition", + "nextGeneration", + "delivery" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "workspacePreparation": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "root" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/WorkspaceStrategy" + }, + { + "$ref": "#/$defs/WorkspaceStrategy2" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "ordinal", + "providerInstanceId", + "modelSelection", + "providerThreadId", + "userMessageId", + "rootNodeId", + "activeAttemptId", + "status", + "requestedAt", + "startedAt", + "completedAt", + "checkpointId", + "contextHandoffId" + ], + "additionalProperties": true + } + }, + "attempts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nativeThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "runId": { + "type": "string" + }, + "attemptOrdinal": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "rootNodeId": { + "type": "string" + }, + "providerInstanceId": { + "type": "string" + }, + "providerThreadId": { + "type": "string" + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reason": { + "type": "string", + "enum": [ + "initial", + "steering_restart", + "retry", + "provider_recovery" + ] + }, + "status": { + "type": "string", + "enum": [ + "pending", + "running", + "completed", + "interrupted", + "failed", + "cancelled", + "superseded" + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "runId", + "attemptOrdinal", + "rootNodeId", + "providerInstanceId", + "providerThreadId", + "providerTurnId", + "reason", + "status", + "startedAt", + "completedAt" + ], + "additionalProperties": true + } + }, + "nodes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentNodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "rootNodeId": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "root_turn", + "assistant_message", + "reasoning", + "plan", + "todo_list", + "tool_call", + "approval_request", + "user_input_request", + "subagent", + "hook", + "system" + ] + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "interrupted", + "failed", + "cancelled", + "rolled_back" + ] + }, + "countsForRun": { + "type": "boolean" + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeItemRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "runtimeRequestId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "checkpointScopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "parentNodeId", + "rootNodeId", + "kind", + "status", + "countsForRun", + "providerThreadId", + "providerTurnId", + "nativeItemRef", + "runtimeRequestId", + "checkpointScopeId", + "startedAt", + "completedAt" + ], + "additionalProperties": true + } + }, + "subagents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentNodeId": { + "type": "string" + }, + "origin": { + "type": "string", + "enum": [ + "provider_native", + "app_owned" + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "driver": { + "type": "string" + }, + "providerInstanceId": { + "type": "string" + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "childThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "remoteChild": { + "anyOf": [ + { + "$ref": "#/$defs/RemoteChild" + }, + { + "type": "null" + } + ] + }, + "nativeTaskRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "prompt": { + "type": "string" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "modelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "completionWake": { + "anyOf": [ + { + "type": "string", + "enum": [ + "always", + "settled_only" + ] + }, + { + "type": "null" + } + ] + }, + "completionDelivery": { + "anyOf": [ + { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "pending", + "claimed", + "acknowledged", + "delivered", + "disposed" + ] + }, + "observedByRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "state", + "observedByRunId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "idle", + "pending", + "running", + "waiting", + "completed", + "failed", + "cancelled", + "interrupted" + ] + }, + "progress": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workflow": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "runHandles": { + "anyOf": [ + { + "type": "object", + "properties": { + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "transcriptDir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "scriptPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "phases": { + "type": "array", + "items": { + "type": "object", + "properties": { + "index": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + } + }, + "required": [ + "index", + "title" + ], + "additionalProperties": true + } + }, + "agents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "index": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "label": { + "type": "string" + }, + "agentId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "childThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "state": { + "type": "string", + "enum": [ + "queued", + "running", + "completed", + "failed" + ] + }, + "phaseIndex": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "phaseTitle": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "effort": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "speed": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "attempt": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "totalTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "toolCalls": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "durationMs": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "queuedAt": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "prompt": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ] + }, + "result": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "index", + "label", + "state" + ], + "additionalProperties": true + } + }, + "totalTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "toolCalls": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "durationMs": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "phases", + "agents" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "result": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "promptOmitted": { + "type": "boolean", + "enum": [ + true + ] + }, + "resultOmitted": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "parentNodeId", + "origin", + "createdBy", + "driver", + "providerInstanceId", + "providerThreadId", + "childThreadId", + "nativeTaskRef", + "prompt", + "title", + "model", + "status", + "result", + "startedAt", + "completedAt", + "updatedAt" + ], + "additionalProperties": true + } + }, + "providerSessions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "providerInstanceId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "starting", + "ready", + "running", + "waiting", + "stopped", + "error" + ] + }, + "cwd": { + "type": "string" + }, + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "capabilities": { + "type": "object", + "properties": { + "sessions": { + "type": "object", + "properties": { + "supportsMultipleProviderThreadsPerSession": { + "type": "boolean" + }, + "supportsModelSwitchInSession": { + "type": "boolean" + }, + "supportsProviderSwitchingViaHandoff": { + "type": "boolean" + }, + "supportsRuntimeModeSwitchInSession": { + "type": "boolean" + }, + "pendingRequestsSurviveRestart": { + "type": "boolean" + } + }, + "required": [ + "supportsMultipleProviderThreadsPerSession", + "supportsModelSwitchInSession", + "supportsProviderSwitchingViaHandoff", + "supportsRuntimeModeSwitchInSession", + "pendingRequestsSurviveRestart" + ], + "additionalProperties": true + }, + "threads": { + "type": "object", + "properties": { + "canCreateEmptyThread": { + "type": "boolean" + }, + "canReadThreadSnapshot": { + "type": "boolean" + }, + "canRollbackThread": { + "type": "boolean" + }, + "canForkThread": { + "type": "boolean" + }, + "canForkFromTurn": { + "type": "boolean" + }, + "canForkFromSubagentThread": { + "type": "boolean" + }, + "exposesNativeThreadId": { + "type": "boolean" + } + }, + "required": [ + "canCreateEmptyThread", + "canReadThreadSnapshot", + "canRollbackThread", + "canForkThread", + "canForkFromTurn", + "canForkFromSubagentThread", + "exposesNativeThreadId" + ], + "additionalProperties": true + }, + "turns": { + "type": "object", + "properties": { + "exposesNativeTurnId": { + "type": "boolean" + }, + "emitsTurnStarted": { + "type": "boolean" + }, + "emitsTurnCompleted": { + "type": "boolean" + }, + "supportsInterrupt": { + "type": "boolean" + }, + "supportsActiveSteering": { + "type": "boolean" + }, + "activeSteeringInterruptsTools": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "activeSteeringAcceptsAttachments": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "supportsSteeringByInterruptRestart": { + "type": "boolean" + }, + "supportsQueuedMessages": { + "type": "boolean" + }, + "terminalStatusQuality": { + "type": "string", + "enum": [ + "strong", + "weak", + "none" + ] + } + }, + "required": [ + "exposesNativeTurnId", + "emitsTurnStarted", + "emitsTurnCompleted", + "supportsInterrupt", + "supportsActiveSteering", + "supportsSteeringByInterruptRestart", + "supportsQueuedMessages", + "terminalStatusQuality" + ], + "additionalProperties": true + }, + "streaming": { + "type": "object", + "properties": { + "streamsAssistantText": { + "type": "boolean" + }, + "streamsReasoning": { + "type": "boolean" + }, + "streamsToolOutput": { + "type": "boolean" + }, + "streamsPlanText": { + "type": "boolean" + }, + "emitsMessageCompleted": { + "type": "boolean" + } + }, + "required": [ + "streamsAssistantText", + "streamsReasoning", + "streamsToolOutput", + "streamsPlanText", + "emitsMessageCompleted" + ], + "additionalProperties": true + }, + "tools": { + "type": "object", + "properties": { + "exposesToolItemIds": { + "type": "boolean" + }, + "emitsToolStarted": { + "type": "boolean" + }, + "emitsToolCompleted": { + "type": "boolean" + }, + "emitsToolOutput": { + "type": "boolean" + }, + "supportsMcpTools": { + "type": "boolean" + }, + "supportsDynamicToolCallbacks": { + "type": "boolean" + } + }, + "required": [ + "exposesToolItemIds", + "emitsToolStarted", + "emitsToolCompleted", + "emitsToolOutput", + "supportsMcpTools", + "supportsDynamicToolCallbacks" + ], + "additionalProperties": true + }, + "approvals": { + "type": "object", + "properties": { + "supportsCommandApproval": { + "type": "boolean" + }, + "supportsFileReadApproval": { + "type": "boolean" + }, + "supportsFileChangeApproval": { + "type": "boolean" + }, + "supportsApplyPatchApproval": { + "type": "boolean" + }, + "approvalsHaveNativeRequestIds": { + "type": "boolean" + }, + "approvalCallbacksAreLiveOnly": { + "type": "boolean" + }, + "approvalsCanOriginateFromSubagents": { + "type": "boolean" + } + }, + "required": [ + "supportsCommandApproval", + "supportsFileReadApproval", + "supportsFileChangeApproval", + "supportsApplyPatchApproval", + "approvalsHaveNativeRequestIds", + "approvalCallbacksAreLiveOnly", + "approvalsCanOriginateFromSubagents" + ], + "additionalProperties": true + }, + "planning": { + "type": "object", + "properties": { + "emitsPlanUpdated": { + "type": "boolean" + }, + "emitsTodoList": { + "type": "boolean" + }, + "emitsProposedPlan": { + "type": "boolean" + }, + "supportsStructuredQuestions": { + "type": "boolean" + }, + "planDeltasHaveItemIds": { + "type": "boolean" + } + }, + "required": [ + "emitsPlanUpdated", + "emitsTodoList", + "emitsProposedPlan", + "supportsStructuredQuestions", + "planDeltasHaveItemIds" + ], + "additionalProperties": true + }, + "subagents": { + "type": "object", + "properties": { + "supportsSubagents": { + "type": "boolean" + }, + "exposesSubagentThreadIds": { + "type": "boolean" + }, + "emitsSubagentLifecycle": { + "type": "boolean" + }, + "canWaitForSubagents": { + "type": "boolean" + }, + "canCloseSubagents": { + "type": "boolean" + }, + "canForkSubagentThread": { + "type": "boolean" + } + }, + "required": [ + "supportsSubagents", + "exposesSubagentThreadIds", + "emitsSubagentLifecycle", + "canWaitForSubagents", + "canCloseSubagents", + "canForkSubagentThread" + ], + "additionalProperties": true + }, + "context": { + "type": "object", + "properties": { + "acceptsSystemContext": { + "type": "boolean" + }, + "acceptsDeveloperContext": { + "type": "boolean" + }, + "acceptsSyntheticUserContext": { + "type": "boolean" + }, + "canGenerateSummaries": { + "type": "boolean" + }, + "canConsumeHandoffSummaries": { + "type": "boolean" + }, + "supportsDeltaHandoff": { + "type": "boolean" + }, + "supportsFullThreadHandoff": { + "type": "boolean" + }, + "maxRecommendedHandoffChars": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "acceptsSystemContext", + "acceptsDeveloperContext", + "acceptsSyntheticUserContext", + "canGenerateSummaries", + "canConsumeHandoffSummaries", + "supportsDeltaHandoff", + "supportsFullThreadHandoff", + "maxRecommendedHandoffChars" + ], + "additionalProperties": true + }, + "checkpointing": { + "type": "object", + "properties": { + "appCanCheckpointFilesystem": { + "type": "boolean" + }, + "supportsNestedCheckpointScopes": { + "type": "boolean" + }, + "providerCanRollbackConversation": { + "type": "boolean" + }, + "providerRollbackReturnsSnapshot": { + "type": "boolean" + }, + "providerCanReadConversationSnapshot": { + "type": "boolean" + } + }, + "required": [ + "appCanCheckpointFilesystem", + "supportsNestedCheckpointScopes", + "providerCanRollbackConversation", + "providerRollbackReturnsSnapshot", + "providerCanReadConversationSnapshot" + ], + "additionalProperties": true + }, + "identity": { + "type": "object", + "properties": { + "nativeThreadIds": { + "type": "string", + "enum": [ + "strong", + "weak", + "none" + ] + }, + "nativeTurnIds": { + "type": "string", + "enum": [ + "strong", + "weak", + "none" + ] + }, + "nativeItemIds": { + "type": "string", + "enum": [ + "strong", + "weak", + "none" + ] + }, + "nativeRequestIds": { + "type": "string", + "enum": [ + "strong", + "weak", + "none" + ] + } + }, + "required": [ + "nativeThreadIds", + "nativeTurnIds", + "nativeItemIds", + "nativeRequestIds" + ], + "additionalProperties": true + }, + "runtimePolicy": { + "anyOf": [ + { + "type": "object", + "properties": { + "enforcement": { + "type": "string", + "enum": [ + "native", + "client-boundary" + ] + } + }, + "required": [ + "enforcement" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "sessions", + "threads", + "turns", + "streaming", + "tools", + "approvals", + "planning", + "subagents", + "context", + "checkpointing", + "identity" + ], + "additionalProperties": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "lastError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "driver", + "providerInstanceId", + "status", + "cwd", + "model", + "capabilities", + "createdAt", + "updatedAt", + "lastError" + ], + "additionalProperties": true + } + }, + "providerThreads": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "providerInstanceId": { + "type": "string" + }, + "providerSessionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "appThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ownerNodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeThreadRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "nativeConversationHeadRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "not_loaded", + "idle", + "active", + "archived", + "closed", + "error" + ] + }, + "firstRunOrdinal": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "lastRunOrdinal": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "handoffIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "forkedFrom": { + "anyOf": [ + { + "type": "object", + "properties": { + "providerThreadId": { + "type": "string" + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "checkpointId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "providerThreadId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "pendingBackgroundTasks": { + "anyOf": [ + { + "$ref": "#/$defs/PendingBackgroundTasks" + }, + { + "type": "null" + } + ] + }, + "contextUsage": { + "anyOf": [ + { + "type": "object", + "properties": { + "usedTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalProcessedTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "maxTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "inputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "cachedInputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "outputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reasoningOutputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "lastUsedTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "lastInputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "lastCachedInputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "lastOutputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "lastReasoningOutputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "toolUses": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "durationMs": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "compactsAutomatically": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "autoCompactThreshold": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "cost": { + "anyOf": [ + { + "type": "object", + "properties": { + "amount": { + "type": "number" + }, + "currency": { + "type": "string" + } + }, + "required": [ + "amount", + "currency" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "usedTokens" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "nativeMetadata": { + "anyOf": [ + { + "$ref": "#/$defs/NativeMetadata" + }, + { + "type": "null" + } + ] + }, + "goal": { + "anyOf": [ + { + "$ref": "#/$defs/Goal" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "driver", + "providerInstanceId", + "providerSessionId", + "appThreadId", + "ownerNodeId", + "nativeThreadRef", + "nativeConversationHeadRef", + "status", + "firstRunOrdinal", + "lastRunOrdinal", + "handoffIds", + "forkedFrom", + "createdAt", + "updatedAt" + ], + "additionalProperties": true + } + }, + "providerTurns": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "providerThreadId": { + "type": "string" + }, + "nodeId": { + "type": "string" + }, + "runAttemptId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeTurnRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "ordinal": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "pending", + "running", + "completed", + "interrupted", + "failed", + "cancelled" + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "completedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tokenUsage": { + "anyOf": [ + { + "$ref": "#/$defs/SourceTokenUsage" + }, + { + "type": "null" + } + ] + }, + "turnTokenUsage": { + "anyOf": [ + { + "type": "object", + "properties": { + "usageScope": { + "type": "string", + "enum": [ + "main_agent" + ] + }, + "cachedInputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "cacheCreationTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reasoningTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "hasSubagents": { + "type": "boolean" + }, + "models": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "localCommand": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "otherUsage": { + "anyOf": [ + { + "$ref": "#/$defs/OtherUsage" + }, + { + "type": "null" + } + ] + }, + "usageStatus": { + "type": "string", + "enum": [ + "complete" + ] + }, + "inputTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "outputTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "usageScope", + "hasSubagents", + "usageStatus", + "inputTokens", + "outputTokens" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "usageScope": { + "type": "string", + "enum": [ + "main_agent" + ] + }, + "cachedInputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "cacheCreationTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reasoningTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "hasSubagents": { + "type": "boolean" + }, + "models": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "localCommand": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "otherUsage": { + "anyOf": [ + { + "$ref": "#/$defs/OtherUsage" + }, + { + "type": "null" + } + ] + }, + "usageStatus": { + "type": "string", + "enum": [ + "partial", + "unavailable" + ] + }, + "inputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "outputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "usageScope", + "hasSubagents", + "usageStatus" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "providerThreadId", + "nodeId", + "runAttemptId", + "nativeTurnRef", + "ordinal", + "status", + "startedAt", + "completedAt" + ], + "additionalProperties": true + } + }, + "runtimeRequests": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nodeId": { + "type": "string" + }, + "providerTurnId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nativeRequestRef": { + "anyOf": [ + { + "$ref": "#/$defs/NativeItemRef" + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "command", + "file-read", + "file-change", + "mcp-elicitation", + "permission", + "dynamic_tool_call", + "user_input", + "auth_refresh" + ] + }, + "status": { + "type": "string", + "enum": [ + "pending", + "resolved", + "expired", + "cancelled" + ] + }, + "responseCapability": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "live" + ] + }, + "providerSessionId": { + "type": "string" + }, + "supportsFeedback": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "providerSessionId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "message" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "not_resumable" + ] + }, + "reason": { + "type": "string" + } + }, + "required": [ + "type", + "reason" + ], + "additionalProperties": true + } + ] + }, + "createdAt": { + "type": "string" + }, + "resolvedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "decision": { + "anyOf": [ + { + "type": "string", + "enum": [ + "accept", + "acceptForSession", + "acceptAlways", + "decline", + "cancel" + ] + }, + { + "type": "null" + } + ] + }, + "feedback": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "answers": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "nodeId", + "providerTurnId", + "nativeRequestRef", + "kind", + "status", + "responseCapability", + "createdAt", + "resolvedAt" + ], + "additionalProperties": true + } + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "notification": { + "anyOf": [ + { + "$ref": "#/$defs/Notification" + }, + { + "type": "null" + } + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "scheduledTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "idleCompaction": { + "anyOf": [ + { + "type": "string", + "enum": [ + "beforeCacheExpires", + "onReturn" + ] + }, + { + "type": "null" + } + ] + }, + "workflowScript": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "role": { + "type": "string", + "enum": [ + "user", + "assistant", + "system" + ] + }, + "text": { + "type": "string" + }, + "context": { + "anyOf": [ + { + "$ref": "#/$defs/Context" + }, + { + "type": "null" + } + ] + }, + "attachments": { + "$ref": "#/$defs/Attachments" + }, + "streaming": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "delegatedCompletion": { + "anyOf": [ + { + "$ref": "#/$defs/DelegatedCompletion" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "createdBy", + "creationSource", + "id", + "threadId", + "runId", + "nodeId", + "role", + "text", + "attachments", + "streaming", + "createdAt", + "updatedAt" + ], + "additionalProperties": true + } + }, + "plans": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "completed", + "superseded" + ] + }, + "detailInTurnItem": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "proposed_plan" + ] + }, + "markdown": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "status", + "kind", + "markdown" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "completed", + "superseded" + ] + }, + "detailInTurnItem": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "todo_list" + ] + }, + "steps": { + "$ref": "#/$defs/Steps" + }, + "explanation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "status", + "kind", + "steps" + ], + "additionalProperties": true + } + ] + } + }, + "turnItems": { + "type": "array", + "items": { + "$ref": "#/$defs/Item" + } + }, + "checkpointScopes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "type": "string" + }, + "parentScopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "root_run", + "subagent", + "tool", + "provider_thread", + "manual" + ] + }, + "ordinalWithinParent": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "advancesAppRunCount": { + "type": "boolean" + }, + "cwd": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "runId", + "nodeId", + "parentScopeId", + "providerThreadId", + "kind", + "ordinalWithinParent", + "advancesAppRunCount", + "cwd", + "createdAt" + ], + "additionalProperties": true + } + }, + "checkpoints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "scopeId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nodeId": { + "type": "string" + }, + "parentCheckpointId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ordinalWithinScope": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "appRunOrdinal": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "ref": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "missing", + "error", + "stale" + ] + }, + "files": { + "$ref": "#/$defs/Files" + }, + "filesTotal": { + "$ref": "#/$defs/FilesTotal" + }, + "capturedAt": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "scopeId", + "runId", + "nodeId", + "parentCheckpointId", + "ordinalWithinScope", + "appRunOrdinal", + "ref", + "status", + "files", + "capturedAt" + ], + "additionalProperties": true + } + }, + "contextHandoffs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "transferId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + }, + "targetRunId": { + "type": "string" + }, + "fromProviderThreadIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "toProviderThreadId": { + "type": "string" + }, + "coveredRunOrdinals": { + "type": "object", + "properties": { + "from": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "to": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "from", + "to" + ], + "additionalProperties": true + }, + "strategy": { + "type": "string", + "enum": [ + "delta_since_target_last_seen", + "fork_delta_summary", + "full_thread_summary", + "checkpoint_summary", + "manual_context" + ] + }, + "status": { + "type": "string", + "enum": [ + "pending", + "ready", + "failed", + "superseded" + ] + }, + "summaryMessageId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "summaryText": { + "type": "string" + }, + "history": { + "anyOf": [ + { + "type": "object", + "properties": { + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "role": { + "type": "string", + "enum": [ + "user", + "assistant" + ] + }, + "text": { + "type": "string" + }, + "runStatus": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "itemId": { + "type": "string" + }, + "providerThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string" + }, + "kind": { + "type": "string" + } + }, + "required": [ + "role", + "text", + "threadId", + "runId", + "itemId", + "providerThreadId", + "status", + "kind" + ], + "additionalProperties": true + } + }, + "coverage": { + "type": "string" + }, + "omittedItems": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "omittedItemIds": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "messages", + "coverage", + "omittedItems" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "delivery": { + "anyOf": [ + { + "type": "object", + "properties": { + "nativeThreadId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "injected", + "inline" + ] + }, + "itemIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "omittedItemIds": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "nativeThreadId", + "status", + "itemIds" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "detailInTurnItem": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "createdByProviderInstanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "threadId", + "targetRunId", + "fromProviderThreadIds", + "toProviderThreadId", + "coveredRunOrdinals", + "strategy", + "status", + "summaryMessageId", + "summaryText", + "createdByProviderInstanceId", + "createdAt", + "updatedAt" + ], + "additionalProperties": true + } + }, + "contextTransfers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "fork", + "provider_handoff", + "merge_back", + "subagent_spawn", + "subagent_result" + ] + }, + "sourceThreadId": { + "type": "string" + }, + "targetThreadId": { + "type": "string" + }, + "sourcePoint": { + "$ref": "#/$defs/BasePoint" + }, + "basePoint": { + "anyOf": [ + { + "$ref": "#/$defs/BasePoint" + }, + { + "type": "null" + } + ] + }, + "sourceProviderInstanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceTokenUsage": { + "anyOf": [ + { + "$ref": "#/$defs/SourceTokenUsage" + }, + { + "type": "null" + } + ] + }, + "targetProviderInstanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "targetRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "pending", + "resolved_native", + "resolved_portable", + "failed", + "consumed", + "superseded" + ] + }, + "resolution": { + "anyOf": [ + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "native_fork" + ] + }, + "providerThreadRef": { + "$ref": "#/$defs/NativeItemRef" + } + }, + "required": [ + "strategy", + "providerThreadRef" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "portable_context" + ] + }, + "contextHandoffId": { + "type": "string" + } + }, + "required": [ + "strategy", + "contextHandoffId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "delta_context" + ] + }, + "contextHandoffId": { + "type": "string" + } + }, + "required": [ + "strategy", + "contextHandoffId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "fork_delta_context" + ] + }, + "contextHandoffId": { + "type": "string" + } + }, + "required": [ + "strategy", + "contextHandoffId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "checkpoint_context" + ] + }, + "contextHandoffId": { + "type": "string" + } + }, + "required": [ + "strategy", + "contextHandoffId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "consumedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "type", + "sourceThreadId", + "targetThreadId", + "sourcePoint", + "basePoint", + "sourceProviderInstanceId", + "targetProviderInstanceId", + "targetRunId", + "status", + "resolution", + "createdBy", + "error", + "createdAt", + "updatedAt", + "consumedAt" + ], + "additionalProperties": true + } + }, + "visibleTurnItems": { + "type": "array", + "items": { + "type": "object", + "properties": { + "position": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "visibility": { + "type": "string", + "enum": [ + "local", + "inherited", + "synthetic" + ] + }, + "sourceThreadId": { + "type": "string" + }, + "sourceItemId": { + "type": "string" + }, + "item": { + "$ref": "#/$defs/Item" + } + }, + "required": [ + "position", + "visibility", + "sourceThreadId", + "sourceItemId", + "item" + ], + "additionalProperties": true + } + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "thread", + "runs", + "attempts", + "nodes", + "subagents", + "providerSessions", + "providerThreads", + "providerTurns", + "runtimeRequests", + "messages", + "plans", + "turnItems", + "checkpointScopes", + "checkpoints", + "contextHandoffs", + "contextTransfers", + "visibleTurnItems", + "updatedAt" + ], + "additionalProperties": true + }, + "OrchestrationGetTurnDiffError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationGetTurnDiffErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationGetTurnDiffErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationGetTurnDiffError" + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "OrchestrationGetTurnItemPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "revision": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "itemId" + ], + "additionalProperties": true + }, + "OrchestrationGetTurnItemSuccess": { + "type": "object", + "properties": { + "item": { + "anyOf": [ + { + "$ref": "#/$defs/Item2" + }, + { + "$ref": "#/$defs/Item3" + }, + { + "$ref": "#/$defs/Item4" + }, + { + "$ref": "#/$defs/Item5" + }, + { + "$ref": "#/$defs/Item6" + }, + { + "$ref": "#/$defs/Item7" + }, + { + "$ref": "#/$defs/Item8" + }, + { + "$ref": "#/$defs/Item9" + }, + { + "$ref": "#/$defs/Item10" + }, + { + "$ref": "#/$defs/Item11" + }, + { + "$ref": "#/$defs/Item12" + }, + { + "$ref": "#/$defs/Item13" + }, + { + "$ref": "#/$defs/Item14" + }, + { + "$ref": "#/$defs/Item15" + }, + { + "$ref": "#/$defs/Item16" + }, + { + "$ref": "#/$defs/Item17" + }, + { + "$ref": "#/$defs/Item18" + }, + { + "$ref": "#/$defs/Item19" + }, + { + "$ref": "#/$defs/Item20" + }, + { + "$ref": "#/$defs/Item21" + }, + { + "$ref": "#/$defs/Item22" + }, + { + "$ref": "#/$defs/Item23" + }, + { + "$ref": "#/$defs/Item24" + }, + { + "$ref": "#/$defs/Item25" + }, + { + "$ref": "#/$defs/Item26" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "item" + ], + "additionalProperties": true + }, + "OrchestrationGetWorkflowScriptError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationGetWorkflowScriptErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationGetWorkflowScriptErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationGetWorkflowScriptError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "invalid-path", + "root-unavailable", + "not-found", + "outside-root", + "not-js", + "not-regular-file", + "changed-during-read", + "read-failed" + ] + }, + "scriptPath": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "reason", + "scriptPath" + ], + "additionalProperties": true + }, + "OrchestrationGetWorkflowScriptPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "scriptPath": { + "type": "string" + } + }, + "required": [ + "threadId", + "scriptPath" + ], + "additionalProperties": true + }, + "OrchestrationGetWorkflowScriptSuccess": { + "type": "object", + "properties": { + "scriptPath": { + "type": "string" + }, + "contents": { + "type": "string" + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "scriptPath", + "contents", + "truncated" + ], + "additionalProperties": true + }, + "OrchestrationGetWorktreeThreadIdsPayload": { + "type": "object", + "properties": { + "worktreePath": { + "type": "string" + } + }, + "required": [ + "worktreePath" + ], + "additionalProperties": true + }, + "OrchestrationGetWorktreeThreadIdsSuccess": { + "type": "object", + "properties": { + "threadIds": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "threadIds" + ], + "additionalProperties": true + }, + "OrchestrationLaunchThreadError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2ThreadLaunchErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationLaunchThreadPayload": { + "type": "object", + "properties": { + "commandId": { + "type": "string" + }, + "creationSource": { + "anyOf": [ + { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + { + "type": "null" + } + ] + }, + "threadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reuseExistingThread": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "projectId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "generateTitle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + }, + "workspaceStrategy": { + "$ref": "#/$defs/WorkspaceStrategy3" + }, + "initialMessage": { + "anyOf": [ + { + "type": "object", + "properties": { + "messageId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "text": { + "type": "string" + }, + "context": { + "anyOf": [ + { + "$ref": "#/$defs/Context" + }, + { + "type": "null" + } + ] + }, + "attachments": { + "$ref": "#/$defs/Attachments" + } + }, + "required": [ + "text", + "attachments" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "commandId", + "projectId", + "title", + "modelSelection", + "runtimeMode", + "interactionMode", + "workspaceStrategy" + ], + "additionalProperties": true + }, + "OrchestrationLaunchThreadSuccess": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "projection": { + "$ref": "#/$defs/OrchestrationGetThreadProjectionSuccess" + }, + "resumed": { + "type": "boolean" + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "threadId", + "projection", + "resumed" + ], + "additionalProperties": true + }, + "OrchestrationSearchThreadError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2SearchThreadErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationSearchThreadPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "skills": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 200 + }, + "displayName": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name" + ], + "additionalProperties": true + }, + "maxItems": 1000 + }, + "query": { + "type": "string" + }, + "index": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "offset": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "start": { + "type": "object", + "properties": { + "entryId": { + "type": "string" + }, + "occurrence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "entryId", + "occurrence" + ], + "additionalProperties": true + } + }, + "required": [ + "threadId", + "query" + ], + "additionalProperties": true + }, + "OrchestrationSearchThreadSuccess": { + "type": "object", + "properties": { + "complete": { + "type": "boolean" + }, + "snapshotSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalMatches": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "activeIndex": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "match": { + "anyOf": [ + { + "type": "object", + "properties": { + "entryId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "occurrence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "entryId", + "runId", + "occurrence" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "navigation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entryId": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startIndex": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "count": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "entryId", + "runId", + "startIndex", + "count" + ], + "additionalProperties": true + }, + "maxItems": 17 + } + }, + "required": [ + "snapshotSequence", + "totalMatches", + "activeIndex", + "match" + ], + "additionalProperties": true + }, + "OrchestrationSearchThreadsError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationSearchThreadsErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationSearchThreadsErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationSearchThreadsError" + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "OrchestrationSearchThreadsPayload": { + "type": "object", + "properties": { + "query": { + "type": "string" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 50 + }, + "olderSettledBefore": { + "type": "string" + }, + "projectIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "environmentLabel": { + "type": "string" + }, + "titlesOnly": { + "type": "boolean" + } + }, + "required": [ + "query" + ], + "additionalProperties": true + }, + "OrchestrationSearchThreadsSuccess": { + "type": "object", + "properties": { + "matches": { + "type": "array", + "items": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "user", + "assistant" + ] + }, + "snippet": { + "type": "string", + "maxLength": 240 + }, + "messageCreatedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "quality": { + "type": "number" + }, + "hits": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "messageId": { + "type": "string" + } + }, + "required": [ + "threadId", + "projectId", + "source", + "snippet", + "messageCreatedAt" + ], + "additionalProperties": true + } + }, + "snapshotSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "threads": { + "$ref": "#/$defs/Threads" + } + }, + "required": [ + "matches" + ], + "additionalProperties": true + }, + "OrchestrationStopWorkflowError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2StopWorkflowErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationStopWorkflowPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "subagentId": { + "type": "string" + } + }, + "required": [ + "threadId", + "subagentId" + ], + "additionalProperties": true + }, + "OrchestrationSubscribeCommandOutputError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationV2CommandOutputErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "OrchestrationSubscribeCommandOutputPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "itemId": { + "type": "string" + } + }, + "required": [ + "threadId", + "itemId" + ], + "additionalProperties": true + }, + "OrchestrationSubscribeCommandOutputSuccess": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "replace", + "append" + ] + }, + "text": { + "type": "string" + }, + "truncated": { + "type": "boolean" + }, + "running": { + "type": "boolean" + } + }, + "required": [ + "kind", + "text", + "truncated", + "running" + ], + "additionalProperties": true + }, + "OrchestrationSubscribeShellPayload": { + "type": "object", + "properties": { + "afterSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "requestCompletionMarker": { + "type": "boolean" + }, + "omitIdleSubagents": { + "type": "boolean" + }, + "omitOlderSettled": { + "type": "boolean" + } + }, + "additionalProperties": true + }, + "OrchestrationSubscribeShellSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "synchronized" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "snapshot" + ] + }, + "snapshot": { + "type": "object", + "properties": { + "schemaVersion": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "snapshotSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "threads": { + "$ref": "#/$defs/Threads" + }, + "archivedThreads": { + "$ref": "#/$defs/Threads" + }, + "projects": { + "$ref": "#/$defs/Projects" + }, + "olderSettled": { + "type": "object", + "properties": { + "before": { + "type": "string" + }, + "asOfSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "counts": { + "$ref": "#/$defs/Counts" + } + }, + "required": [ + "before", + "asOfSequence", + "counts" + ], + "additionalProperties": true + } + }, + "required": [ + "schemaVersion", + "snapshotSequence", + "threads", + "archivedThreads", + "projects" + ], + "additionalProperties": true + }, + "resolvedRepositoryIdentityRoots": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "kind", + "snapshot" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "project.updated" + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "project": { + "$ref": "#/$defs/ProjectsEntry" + } + }, + "required": [ + "kind", + "sequence", + "project" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "project.removed" + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "projectId": { + "type": "string" + } + }, + "required": [ + "kind", + "sequence", + "projectId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "thread.updated" + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "location": { + "type": "string", + "enum": [ + "active", + "archive" + ] + }, + "thread": { + "$ref": "#/$defs/ThreadsEntry" + } + }, + "required": [ + "kind", + "sequence", + "location", + "thread" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "thread.removed" + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "location": { + "type": "string", + "enum": [ + "active", + "archive" + ] + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "kind", + "sequence", + "location", + "threadId" + ], + "additionalProperties": true + } + ] + }, + "OrchestrationSubscribeThreadPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "afterSequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "requestCompletionMarker": { + "type": "boolean" + }, + "acceptBoundedSnapshot": { + "type": "boolean" + }, + "acceptDeferredWork": { + "type": "boolean" + }, + "acceptCompactTurnItems": { + "type": "boolean" + }, + "acceptDedupedBodies": { + "type": "boolean" + }, + "acceptCompactIds": { + "type": "boolean" + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "OrchestrationSubscribeThreadSuccess": {}, + "OrchestrationTurnDiffRange": { + "type": "object", + "properties": { + "fromTurnCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "toTurnCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "threadId": { + "type": "string" + }, + "diff": { + "type": "string" + } + }, + "required": [ + "fromTurnCount", + "toTurnCount", + "threadId", + "diff" + ], + "additionalProperties": true + }, + "OrchestrationTurnDiffRange2": { + "type": "object", + "properties": { + "fromTurnCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "toTurnCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "threadId": { + "type": "string" + }, + "ignoreWhitespace": { + "type": "boolean" + } + }, + "required": [ + "fromTurnCount", + "toTurnCount", + "threadId" + ], + "additionalProperties": true + }, + "OrchestrationV2CommandOutputErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2CommandOutputError" + ] + }, + "threadId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "threadId", + "message" + ], + "additionalProperties": true + }, + "OrchestrationV2DispatchCommandErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2DispatchCommandError" + ] + }, + "commandId": { + "type": "string" + }, + "commandType": { + "type": "string" + }, + "message": { + "type": "string" + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "commandId", + "commandType", + "message" + ], + "additionalProperties": true + }, + "OrchestrationV2ForceStopThreadErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2ForceStopThreadError" + ] + }, + "threadId": { + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string", + "enum": [ + "superseded" + ] + }, + { + "type": "null" + } + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "threadId" + ], + "additionalProperties": true + }, + "OrchestrationV2GetNativeChildWorkErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2GetNativeChildWorkError" + ] + }, + "threadId": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "threadId" + ], + "additionalProperties": true + }, + "OrchestrationV2GetShellSnapshotErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2GetShellSnapshotError" + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "OrchestrationV2GetThreadProjectionErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2GetThreadProjectionError" + ] + }, + "threadId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "threadId", + "message" + ], + "additionalProperties": true + }, + "OrchestrationV2SearchThreadErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2SearchThreadError" + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "cause" + ], + "additionalProperties": true + }, + "OrchestrationV2StopWorkflowErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2StopWorkflowError" + ] + }, + "threadId": { + "type": "string" + }, + "subagentId": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "not-running", + "unsupported", + "unavailable", + "stop-failed" + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "threadId", + "subagentId", + "reason" + ], + "additionalProperties": true + }, + "OrchestrationV2ThreadLaunchErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "OrchestrationV2ThreadLaunchError" + ] + }, + "commandId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "commandId", + "projectId", + "message" + ], + "additionalProperties": true + }, + "OtherUsage": { + "type": "object", + "properties": { + "inputTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "outputTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "inputTokens", + "outputTokens" + ], + "additionalProperties": true + }, + "Overrides": { + "type": "object", + "properties": { + "automaticGitFetchInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "providerHealthRefreshInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "hostPowerMonitorActiveInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "hostPowerMonitorIdleInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "pauseWhenHostLocked": { + "type": "boolean" + }, + "pauseWhenHostLowPower": { + "type": "boolean" + }, + "pauseWhenClientLowPower": { + "type": "boolean" + }, + "pauseWhenOnBattery": { + "type": "boolean" + } + }, + "additionalProperties": true + }, + "PairingLinksEntry": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "scopes": { + "$ref": "#/$defs/Scopes" + }, + "permissions": { + "$ref": "#/$defs/Permissions" + }, + "subject": { + "type": "string" + }, + "label": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "expiresAt": { + "type": "string" + } + }, + "required": [ + "id", + "scopes", + "subject", + "createdAt", + "expiresAt" + ], + "additionalProperties": true + }, + "PeerLinkErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PeerLinkError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "unreachable", + "not_a_t3_environment", + "incompatible", + "self", + "pairing_rejected", + "expired", + "unknown_link", + "storage", + "disabled" + ] + }, + "message": { + "type": "string" + }, + "cause": {} + }, + "required": [ + "_tag", + "reason", + "message" + ], + "additionalProperties": true + }, + "PeerLinkRequestErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PeerLinkRequestError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "load_failed", + "not_found", + "already_answered", + "record_failed" + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "PeerLinksAnswerRequestError": { + "anyOf": [ + { + "$ref": "#/$defs/PeerLinkRequestErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "PeerLinksAnswerRequestPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "turnItemId": { + "type": "string" + }, + "answer": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "link" + ] + }, + "environmentId": { + "type": "string" + }, + "label": { + "type": "string" + }, + "urls": { + "type": "array", + "prefixItems": [ + { + "type": "string" + } + ], + "minItems": 1, + "items": { + "type": "string" + } + }, + "access": { + "type": "string", + "enum": [ + "read-only", + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "pairingCode": { + "type": "string" + } + }, + "required": [ + "type", + "urls", + "access", + "pairingCode" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "use-existing" + ] + }, + "environmentId": { + "type": "string" + } + }, + "required": [ + "type", + "environmentId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "decline" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "threadId", + "turnItemId", + "answer" + ], + "additionalProperties": true + }, + "PeerLinksLinkError": { + "anyOf": [ + { + "$ref": "#/$defs/PeerLinkErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "PeerLinksLinkPayload": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "alternateUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "pairingCode": { + "type": "string" + }, + "access": { + "type": "string", + "enum": [ + "read-only", + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "expectedEnvironmentId": { + "type": "string" + }, + "expectedLabel": { + "type": "string" + } + }, + "required": [ + "url", + "pairingCode", + "access" + ], + "additionalProperties": true + }, + "PeerLinksLinkSuccess": { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "label": { + "type": "string" + }, + "urls": { + "type": "array", + "items": { + "type": "string" + } + }, + "access": { + "type": "string", + "enum": [ + "read-only", + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "linkedAt": { + "type": "string" + }, + "expiresAt": { + "type": "string" + }, + "lastReachedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "environmentId", + "label", + "urls", + "access", + "linkedAt", + "expiresAt", + "lastReachedAt", + "lastError" + ], + "additionalProperties": true + }, + "PeerLinksListSuccess": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "label": { + "type": "string" + }, + "urls": { + "type": "array", + "items": { + "type": "string" + } + }, + "access": { + "type": "string", + "enum": [ + "read-only", + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "linkedAt": { + "type": "string" + }, + "expiresAt": { + "type": "string" + }, + "lastReachedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "reachable", + "unreachable", + "expired" + ] + } + }, + "required": [ + "environmentId", + "label", + "urls", + "access", + "linkedAt", + "expiresAt", + "lastReachedAt", + "lastError", + "status" + ], + "additionalProperties": true + } + } + }, + "required": [ + "links" + ], + "additionalProperties": true + }, + "PeerLinksUnlinkPayload": { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + } + }, + "required": [ + "environmentId" + ], + "additionalProperties": true + }, + "PeerLinksUnlinkSuccess": { + "type": "object", + "properties": { + "removed": { + "type": "boolean" + } + }, + "required": [ + "removed" + ], + "additionalProperties": true + }, + "PendingBackgroundTasks": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "taskId": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "awaited": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "subagent" + ] + }, + "providerInstanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "childThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "taskId", + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "taskId": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "awaited": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "command" + ] + } + }, + "required": [ + "taskId", + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "taskId": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "awaited": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "monitor" + ] + } + }, + "required": [ + "taskId", + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "taskId": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "awaited": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "kind": { + "type": "string", + "enum": [ + "background_task" + ] + } + }, + "required": [ + "taskId", + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "taskId": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentTaskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "awaited": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + }, + "kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "taskId" + ], + "additionalProperties": true + } + ] + } + }, + "Permissions": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/PermissionsEntry" + }, + { + "type": "null" + } + ] + } + }, + "PermissionsEntry": { + "type": "string", + "enum": [ + "orchestration:read", + "orchestration:operate", + "settings:write", + "providers:manage", + "environment:maintain", + "preview:operate", + "diagnostics:read", + "terminal:read", + "terminal:operate", + "filesystem:read", + "filesystem:write", + "review:write", + "source-control:write", + "access:read", + "access:write", + "relay:read", + "relay:write" + ] + }, + "PersistChatAttachmentsErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PersistChatAttachmentsError" + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "Platforms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + }, + "available": { + "type": "boolean" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "platform", + "available" + ], + "additionalProperties": true + } + }, + "Position": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "added" + ] + }, + "newLine": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "kind", + "newLine" + ], + "additionalProperties": true + }, + "Position2": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "deleted" + ] + }, + "oldLine": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "kind", + "oldLine" + ], + "additionalProperties": true + }, + "Position3": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "context" + ] + }, + "oldLine": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "newLine": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "side": { + "type": "string", + "enum": [ + "left", + "right" + ] + } + }, + "required": [ + "kind", + "oldLine", + "newLine", + "side" + ], + "additionalProperties": true + }, + "Pr": { + "type": "object", + "properties": { + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "baseRef": { + "type": "string" + }, + "headRef": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "isDraft": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "number", + "title", + "url", + "baseRef", + "headRef", + "state" + ], + "additionalProperties": true + }, + "PreviewAdjustError": { + "anyOf": [ + { + "$ref": "#/$defs/PreviewSessionLookupErrorEncoded" + }, + { + "$ref": "#/$defs/PreviewInvalidUrlErrorEncoded" + }, + { + "$ref": "#/$defs/PreviewControlRequiredErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "PreviewAdjustPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "colorScheme": { + "anyOf": [ + { + "type": "string", + "enum": [ + "system", + "light", + "dark" + ] + }, + { + "type": "null" + } + ] + }, + "zoomFactor": { + "anyOf": [ + { + "type": "number", + "enum": [ + 0.25, + 0.33, + 0.5, + 0.67, + 0.75, + 0.8, + 0.9, + 1, + 1.1, + 1.25, + 1.5, + 1.75, + 2, + 2.5, + 3, + 4, + 5 + ] + }, + { + "type": "null" + } + ] + }, + "hardReload": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "clear": { + "anyOf": [ + { + "type": "string", + "enum": [ + "cookies", + "cache" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "tabId" + ], + "additionalProperties": true + }, + "PreviewAdjustSuccess": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "navStatus": { + "$ref": "#/$defs/NavStatus" + }, + "canGoBack": { + "type": "boolean" + }, + "canGoForward": { + "type": "boolean" + }, + "viewport": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "fill" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/Viewport" + }, + { + "$ref": "#/$defs/Viewport2" + }, + { + "type": "null" + } + ] + }, + "colorScheme": { + "anyOf": [ + { + "type": "string", + "enum": [ + "system", + "light", + "dark" + ] + }, + { + "type": "null" + } + ] + }, + "zoomFactor": { + "anyOf": [ + { + "type": "number", + "enum": [ + 0.25, + 0.33, + 0.5, + 0.67, + 0.75, + 0.8, + 0.9, + 1, + 1.1, + 1.25, + 1.5, + 1.75, + 2, + 2.5, + 3, + 4, + 5 + ] + }, + { + "type": "null" + } + ] + }, + "profileId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "runtime": { + "anyOf": [ + { + "type": "string", + "enum": [ + "desktop", + "server" + ] + }, + { + "type": "null" + } + ] + }, + "headless": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "automationOwner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reveal": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "revealRequest": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "force": { + "type": "boolean" + } + }, + "required": [ + "id", + "force" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "threadId", + "tabId", + "navStatus", + "canGoBack", + "canGoForward", + "updatedAt" + ], + "additionalProperties": true + }, + "PreviewClearProfileError": { + "anyOf": [ + { + "$ref": "#/$defs/PreviewClearProfileErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "PreviewClearProfileErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PreviewClearProfileError" + ] + }, + "profileId": { + "type": "string" + }, + "cause": {} + }, + "required": [ + "_tag", + "profileId", + "cause" + ], + "additionalProperties": true + }, + "PreviewClearProfilePayload": { + "type": "object", + "properties": { + "profileId": { + "type": "string" + } + }, + "required": [ + "profileId" + ], + "additionalProperties": true + }, + "PreviewClosePayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "PreviewControlRequiredErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PreviewControlRequiredError" + ] + }, + "tabId": { + "type": "string" + } + }, + "required": [ + "_tag", + "tabId" + ], + "additionalProperties": true + }, + "PreviewInvalidUrlErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PreviewInvalidUrlError" + ] + }, + "inputLength": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "reason": { + "type": "string", + "enum": [ + "empty", + "parse", + "unsupported-protocol", + "unexpected" + ] + }, + "protocol": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "inputLength", + "reason", + "cause" + ], + "additionalProperties": true + }, + "PreviewListSuccess": { + "type": "object", + "properties": { + "sessions": { + "type": "array", + "items": { + "$ref": "#/$defs/PreviewAdjustSuccess" + } + }, + "serverEpoch": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "sessions", + "serverEpoch", + "revision" + ], + "additionalProperties": true + }, + "PreviewOpenPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "viewport": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "fill" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/Viewport" + }, + { + "$ref": "#/$defs/Viewport2" + }, + { + "type": "null" + } + ] + }, + "profileId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "runtime": { + "anyOf": [ + { + "type": "string", + "enum": [ + "desktop", + "server" + ] + }, + { + "type": "null" + } + ] + }, + "reveal": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "PreviewReportProfilesPayload": { + "type": "object", + "properties": { + "profiles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "persistent", + "incognito" + ] + } + }, + "required": [ + "id", + "name", + "kind" + ], + "additionalProperties": true + }, + "maxItems": 24 + }, + "defaultProfileId": { + "type": "string" + }, + "recordingFrameRate": { + "anyOf": [ + { + "type": "number", + "enum": [ + 30, + 60 + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "profiles", + "defaultProfileId" + ], + "additionalProperties": true + }, + "PreviewReportStatusPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "navStatus": { + "$ref": "#/$defs/NavStatus" + }, + "canGoBack": { + "type": "boolean" + }, + "canGoForward": { + "type": "boolean" + } + }, + "required": [ + "threadId", + "tabId", + "navStatus", + "canGoBack", + "canGoForward" + ], + "additionalProperties": true + }, + "PreviewResizePayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "viewport": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "fill" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/Viewport" + }, + { + "$ref": "#/$defs/Viewport2" + } + ] + } + }, + "required": [ + "threadId", + "tabId", + "viewport" + ], + "additionalProperties": true + }, + "PreviewSessionLookupErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PreviewSessionLookupError" + ] + }, + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + } + }, + "required": [ + "_tag", + "threadId", + "tabId" + ], + "additionalProperties": true + }, + "PreviewStopLocalServerError": { + "anyOf": [ + { + "$ref": "#/$defs/PreviewStopLocalServerErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "PreviewStopLocalServerErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PreviewStopLocalServerError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "not-found", + "shared-group", + "unsupported", + "signal-failed" + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "PreviewStopLocalServerPayload": { + "type": "object", + "properties": { + "pid": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "port": { + "type": "integer", + "exclusiveMinimum": 0, + "exclusiveMaximum": 65536 + } + }, + "required": [ + "pid", + "port" + ], + "additionalProperties": true + }, + "Pricing": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "fresh", + "cached", + "unavailable" + ] + }, + "source": { + "type": "string" + }, + "fetchedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "knownModels": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "status", + "source", + "fetchedAt", + "knownModels" + ], + "additionalProperties": true + }, + "Profile": { + "type": "object", + "properties": { + "registration": { + "$ref": "#/$defs/Registration" + }, + "credentials": { + "type": "object", + "properties": { + "clientId": { + "type": "string" + }, + "accessToken": { + "type": "string", + "minLength": 1, + "maxLength": 16384 + }, + "refreshToken": { + "anyOf": [ + { + "type": "string", + "maxLength": 16384 + }, + { + "type": "null" + } + ] + }, + "idToken": { + "type": "string", + "minLength": 1, + "maxLength": 16384 + }, + "issuer": { + "type": "string" + }, + "expiresAt": { + "type": "number" + }, + "earliestRefreshAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "subject": { + "type": "string" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "clientId", + "accessToken", + "refreshToken", + "idToken", + "issuer", + "expiresAt", + "earliestRefreshAt", + "scopes", + "subject", + "email" + ], + "additionalProperties": true + } + }, + "required": [ + "registration", + "credentials" + ], + "additionalProperties": true + }, + "ProjectCloneCancelPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + } + }, + "required": [ + "projectId" + ], + "additionalProperties": true + }, + "ProjectCloneCancelSuccess": { + "type": "object", + "properties": { + "applied": { + "type": "boolean" + } + }, + "required": [ + "applied" + ], + "additionalProperties": true + }, + "ProjectCloneRetryError": { + "anyOf": [ + { + "$ref": "#/$defs/SourceControlRepositoryErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectCloneStartError": { + "anyOf": [ + { + "$ref": "#/$defs/SourceControlRepositoryErrorEncoded" + }, + { + "$ref": "#/$defs/OrchestrationDispatchCommandErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectCloneStartPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "repository": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "remoteUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "destinationPath": { + "type": "string" + }, + "protocol": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto", + "ssh", + "https" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "projectId", + "title", + "createdAt", + "destinationPath" + ], + "additionalProperties": true + }, + "ProjectCloneStartSuccess": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "remoteUrl": { + "type": "string" + }, + "repository": { + "anyOf": [ + { + "$ref": "#/$defs/Repository" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "projectId", + "cwd", + "remoteUrl", + "repository" + ], + "additionalProperties": true + }, + "ProjectIcon": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "lucide" + ] + }, + "name": { + "type": "string" + }, + "color": { + "type": "string", + "enum": [ + "gray", + "red", + "orange", + "amber", + "yellow", + "lime", + "green", + "emerald", + "teal", + "cyan", + "sky", + "blue", + "indigo", + "violet", + "purple", + "fuchsia", + "pink", + "rose" + ] + }, + "monogramText": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "monogram": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind", + "name", + "color" + ], + "additionalProperties": true + }, + "ProjectIcon2": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "monogram" + ] + }, + "text": { + "type": "string" + }, + "color": { + "type": "string", + "enum": [ + "gray", + "red", + "orange", + "amber", + "yellow", + "lime", + "green", + "emerald", + "teal", + "cyan", + "sky", + "blue", + "indigo", + "violet", + "purple", + "fuchsia", + "pink", + "rose" + ] + } + }, + "required": [ + "kind", + "text", + "color" + ], + "additionalProperties": true + }, + "ProjectListEntriesErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProjectListEntriesError" + ] + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "failure": { + "anyOf": [ + { + "$ref": "#/$defs/Failure2" + }, + { + "type": "null" + } + ] + }, + "normalizedCwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "ProjectMutationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProjectMutationError" + ] + }, + "commandId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "commandId", + "message" + ], + "additionalProperties": true + }, + "ProjectReadFileErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProjectReadFileError" + ] + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "relativePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "failure": { + "anyOf": [ + { + "type": "string", + "enum": [ + "workspace_path_outside_root", + "resolved_path_outside_root", + "path_not_file", + "binary_file", + "operation_failed" + ] + }, + { + "type": "null" + } + ] + }, + "resolvedPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resolvedWorkspaceRoot": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "operation": { + "anyOf": [ + { + "type": "string", + "enum": [ + "realpath-workspace-root", + "realpath-target", + "open", + "stat", + "read", + "close", + "make-directory", + "write-file" + ] + }, + { + "type": "null" + } + ] + }, + "operationPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "ProjectScriptOverrides": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/$defs/DefaultProjectScripts" + }, + { + "type": "null" + } + ] + } + }, + "ProjectSearchContentsErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProjectSearchContentsError" + ] + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "queryLength": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "limit": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "failure": { + "anyOf": [ + { + "$ref": "#/$defs/Failure2" + }, + { + "type": "null" + } + ] + }, + "normalizedCwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "ProjectSearchEntriesErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProjectSearchEntriesError" + ] + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "queryLength": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "limit": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "failure": { + "anyOf": [ + { + "$ref": "#/$defs/Failure2" + }, + { + "type": "null" + } + ] + }, + "normalizedCwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "timeout": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "ProjectWriteFileErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProjectWriteFileError" + ] + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "relativePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "failure": { + "anyOf": [ + { + "type": "string", + "enum": [ + "workspace_path_outside_root", + "resolved_path_outside_root", + "path_not_file", + "binary_file", + "operation_failed" + ] + }, + { + "type": "null" + } + ] + }, + "resolvedPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resolvedWorkspaceRoot": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "operation": { + "anyOf": [ + { + "type": "string", + "enum": [ + "realpath-workspace-root", + "realpath-target", + "open", + "stat", + "read", + "close", + "make-directory", + "write-file" + ] + }, + { + "type": "null" + } + ] + }, + "operationPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "Projects": { + "type": "array", + "items": { + "$ref": "#/$defs/ProjectsEntry" + } + }, + "ProjectsCreateFromThreadError": { + "anyOf": [ + { + "$ref": "#/$defs/OrchestrationDispatchCommandErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectsCreateNewPayload": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": true + }, + "ProjectsCreateNewSuccess": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "workspaceRoot": { + "type": "string" + }, + "commitError": { + "type": "string" + } + }, + "required": [ + "projectId", + "workspaceRoot" + ], + "additionalProperties": true + }, + "ProjectsEntry": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "workspaceRoot": { + "type": "string" + }, + "repositoryIdentity": { + "anyOf": [ + { + "$ref": "#/$defs/RepositoryIdentity" + }, + { + "type": "null" + } + ] + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "defaultThreadEnvMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "local", + "worktree" + ] + }, + { + "type": "null" + } + ] + }, + "autoPull": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "faviconPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "projectIcon": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectIcon" + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "emoji" + ] + }, + "emoji": { + "type": "string" + } + }, + "required": [ + "kind", + "emoji" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/ProjectIcon2" + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string" + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "scripts": { + "$ref": "#/$defs/DefaultProjectScripts" + }, + "pinnedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "archivedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "title", + "workspaceRoot", + "defaultModelSelection", + "scripts", + "createdAt", + "updatedAt" + ], + "additionalProperties": true + }, + "ProjectsListEntriesError": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectListEntriesErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectsListEntriesPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "directoryPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "ProjectsListEntriesSuccess": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "file", + "directory" + ] + }, + "ignored": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "path", + "kind" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "entries", + "truncated" + ], + "additionalProperties": true + }, + "ProjectsListIgnoredFilesPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "ProjectsListIgnoredFilesSuccess": { + "type": "object", + "properties": { + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "byteLength": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "path", + "byteLength" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "files", + "truncated" + ], + "additionalProperties": true + }, + "ProjectsMutateError": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectMutationErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectsMutatePayload": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "project.create" + ] + }, + "commandId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "workspaceRoot": { + "type": "string" + }, + "createWorkspaceRootIfMissing": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "scripts": { + "anyOf": [ + { + "$ref": "#/$defs/DefaultProjectScripts" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "projectId", + "title", + "workspaceRoot" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "project.update" + ] + }, + "commandId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspaceRoot": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "autoPull": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "projectIcon": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "lucide" + ] + }, + "name": { + "type": "string" + }, + "color": { + "type": "string", + "enum": [ + "gray", + "red", + "orange", + "amber", + "yellow", + "lime", + "green", + "emerald", + "teal", + "cyan", + "sky", + "blue", + "indigo", + "violet", + "purple", + "fuchsia", + "pink", + "rose" + ] + } + }, + "required": [ + "kind", + "name", + "color" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "emoji" + ] + }, + "emoji": { + "type": "string" + } + }, + "required": [ + "kind", + "emoji" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/ProjectIcon2" + }, + { + "type": "null" + } + ] + }, + "faviconPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "defaultThreadEnvMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "local", + "worktree" + ] + }, + { + "type": "null" + } + ] + }, + "scripts": { + "anyOf": [ + { + "$ref": "#/$defs/DefaultProjectScripts" + }, + { + "type": "null" + } + ] + }, + "pinned": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "archived": { + "anyOf": [ + { + "type": "boolean", + "description": "Threads and files are kept." + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "projectId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "project.delete" + ] + }, + "commandId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "force": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "commandId", + "projectId" + ], + "additionalProperties": true + } + ] + }, + "ProjectsMutateSuccess": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "workspaceRoot": { + "type": "string" + }, + "repositoryIdentity": { + "anyOf": [ + { + "$ref": "#/$defs/RepositoryIdentity" + }, + { + "type": "null" + } + ] + }, + "faviconPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "projectIcon": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectIcon" + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "emoji" + ] + }, + "emoji": { + "type": "string" + } + }, + "required": [ + "kind", + "emoji" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/ProjectIcon2" + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string" + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "defaultThreadEnvMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "local", + "worktree" + ] + }, + { + "type": "null" + } + ] + }, + "autoPull": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "scripts": { + "$ref": "#/$defs/DefaultProjectScripts" + }, + "pinnedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "archivedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "title", + "workspaceRoot", + "defaultModelSelection", + "scripts", + "createdAt", + "updatedAt", + "deletedAt" + ], + "additionalProperties": true + }, + "ProjectsReadFileError": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectReadFileErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectsReadFilePayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "relativePath": { + "type": "string" + }, + "allowMissing": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "relativePath" + ], + "additionalProperties": true + }, + "ProjectsReadFileSuccess": { + "type": "object", + "properties": { + "relativePath": { + "type": "string" + }, + "contents": { + "type": "string" + }, + "byteLength": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "truncated": { + "type": "boolean" + }, + "exists": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "relativePath", + "contents", + "byteLength", + "truncated" + ], + "additionalProperties": true + }, + "ProjectsSearchContentsError": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectSearchContentsErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectsSearchContentsPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "query": { + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + "caseSensitive": { + "type": "boolean" + }, + "wholeWord": { + "type": "boolean" + }, + "useRegex": { + "type": "boolean" + } + }, + "required": [ + "cwd", + "query", + "limit", + "caseSensitive", + "wholeWord", + "useRegex" + ], + "additionalProperties": true + }, + "ProjectsSearchContentsSuccess": { + "type": "object", + "properties": { + "matches": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "lineNumber": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "lineContent": { + "type": "string" + }, + "matchRanges": { + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "end": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "start", + "end" + ], + "additionalProperties": true + } + } + }, + "required": [ + "path", + "lineNumber", + "lineContent", + "matchRanges" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + }, + "regexFallbackError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "matches", + "truncated" + ], + "additionalProperties": true + }, + "ProjectsSearchEntriesError": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectSearchEntriesErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectsSearchEntriesPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "query": { + "type": "string" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 200 + }, + "kind": { + "anyOf": [ + { + "type": "string", + "enum": [ + "file", + "directory" + ] + }, + { + "type": "null" + } + ] + }, + "imageOnly": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "query", + "limit" + ], + "additionalProperties": true + }, + "ProjectsWriteFileError": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectWriteFileErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProjectsWriteFilePayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "relativePath": { + "type": "string" + }, + "contents": { + "type": "string" + } + }, + "required": [ + "cwd", + "relativePath", + "contents" + ], + "additionalProperties": true + }, + "ProjectsWriteFileSuccess": { + "type": "object", + "properties": { + "relativePath": { + "type": "string" + } + }, + "required": [ + "relativePath" + ], + "additionalProperties": true + }, + "ProviderAuthCancelError": { + "anyOf": [ + { + "$ref": "#/$defs/ProviderSetupErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProviderAuthCancelPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "flowId": { + "type": "string" + } + }, + "required": [ + "instanceId", + "flowId" + ], + "additionalProperties": true + }, + "ProviderAuthCancelSuccess": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "phase": { + "type": "string", + "enum": [ + "idle", + "starting", + "waiting", + "verifying", + "succeeded", + "failed", + "cancelled" + ] + }, + "flowId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "authorizationUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "methods": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "accountEmail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "agent", + "terminal", + "credentials" + ] + } + }, + "required": [ + "id", + "name", + "description", + "type" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "interaction": {}, + "credentialOwner": {} + }, + "required": [ + "instanceId", + "phase", + "flowId", + "authorizationUrl", + "expiresAt", + "message" + ], + "additionalProperties": true + }, + "ProviderAuthCompletePayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "flowId": { + "type": "string" + }, + "callbackUrl": { + "type": "string" + } + }, + "required": [ + "instanceId", + "flowId", + "callbackUrl" + ], + "additionalProperties": true + }, + "ProviderAuthLogoutPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + } + }, + "required": [ + "instanceId" + ], + "additionalProperties": true + }, + "ProviderAuthRespondPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "flowId": { + "type": "string" + }, + "interactionId": { + "type": "string" + }, + "response": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "browser" + ] + }, + "action": { + "type": "string", + "enum": [ + "accept", + "decline" + ] + } + }, + "required": [ + "type", + "action" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "terminal" + ] + }, + "data": { + "type": "string", + "maxLength": 4096 + }, + "size": { + "type": "object", + "properties": { + "cols": { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + "rows": { + "type": "integer", + "minimum": 1, + "maximum": 200 + } + }, + "required": [ + "cols", + "rows" + ], + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "credentials" + ] + }, + "values": { + "type": "object", + "additionalProperties": { + "type": "string", + "maxLength": 16384 + }, + "maxProperties": 16 + } + }, + "required": [ + "type", + "values" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "instanceId", + "flowId", + "interactionId", + "response" + ], + "additionalProperties": true + }, + "ProviderAuthStartPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "methodId": { + "type": "string" + }, + "returnUrl": { + "type": "string" + }, + "callbackMode": { + "type": "string", + "enum": [ + "server", + "client" + ] + } + }, + "required": [ + "instanceId" + ], + "additionalProperties": true + }, + "ProviderChatgptHandoffSubscribePayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "environmentId": { + "type": "string" + }, + "attemptId": { + "type": "string", + "maxLength": 128 + }, + "returnUrl": { + "type": "string", + "maxLength": 4096 + }, + "profile": { + "anyOf": [ + { + "$ref": "#/$defs/Registration" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "instanceId", + "environmentId", + "attemptId", + "returnUrl", + "profile" + ], + "additionalProperties": true + }, + "ProviderChatgptHandoffSubscribeSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "phase": { + "type": "string", + "enum": [ + "auth" + ] + }, + "state": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + } + }, + "required": [ + "phase", + "state" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "phase": { + "type": "string", + "enum": [ + "finished" + ] + }, + "profile": { + "$ref": "#/$defs/Profile" + } + }, + "required": [ + "phase", + "profile" + ], + "additionalProperties": true + } + ] + }, + "ProviderChatgptImportProfilePayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "profile": { + "$ref": "#/$defs/Profile" + } + }, + "required": [ + "instanceId", + "profile" + ], + "additionalProperties": true + }, + "ProviderChatgptReconnectProfilePayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "methodId": { + "type": "string" + } + }, + "required": [ + "instanceId", + "methodId" + ], + "additionalProperties": true + }, + "ProviderChatgptReconnectProfileSuccess": { + "anyOf": [ + { + "$ref": "#/$defs/Registration" + }, + { + "type": "null" + } + ] + }, + "ProviderConsumeResetCreditError": { + "anyOf": [ + { + "$ref": "#/$defs/ProviderSetupErrorEncoded" + }, + { + "$ref": "#/$defs/UsageLimitSourceErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProviderConsumeResetCreditPayload": { + "anyOf": [ + { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + } + }, + "required": [ + "instanceId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "sourceId": { + "type": "string" + }, + "accountId": { + "type": "string" + }, + "creditId": { + "type": "string" + } + }, + "required": [ + "sourceId", + "accountId", + "creditId" + ], + "additionalProperties": true + } + ] + }, + "ProviderConsumeResetCreditSuccess": { + "type": "object", + "properties": { + "outcome": { + "type": "string", + "enum": [ + "reset", + "nothingToReset", + "noCredit", + "alreadyRedeemed" + ] + }, + "warning": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "outcome" + ], + "additionalProperties": true + }, + "ProviderInstallCancelPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "operationId": { + "type": "string" + } + }, + "required": [ + "instanceId", + "operationId" + ], + "additionalProperties": true + }, + "ProviderInstallCancelSuccess": { + "type": "object", + "properties": { + "driver": { + "type": "string" + }, + "operationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "phase": { + "type": "string", + "enum": [ + "idle", + "downloading", + "extracting", + "verifying", + "succeeded", + "failed", + "cancelled" + ] + }, + "downloadedBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalBytes": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "installedVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "executablePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "source": { + "anyOf": [ + { + "type": "string", + "enum": [ + "managed", + "local" + ] + }, + { + "type": "null" + } + ] + }, + "canRemove": { + "type": "boolean" + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "driver", + "operationId", + "phase", + "downloadedBytes", + "totalBytes", + "version", + "installedVersion", + "canRemove", + "message" + ], + "additionalProperties": true + }, + "ProviderInstances": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/ProviderInstances2" + } + }, + "ProviderInstances2": { + "type": "object", + "properties": { + "driver": { + "type": "string" + }, + "displayName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "accentColor": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "environment": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sensitive": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "valueRedacted": { + "type": "boolean" + }, + "secret": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "reference": { + "type": "string" + } + }, + "required": [ + "provider", + "reference" + ], + "additionalProperties": true + } + }, + "required": [ + "name" + ], + "additionalProperties": true + } + }, + "enabled": { + "type": "boolean" + }, + "config": {} + }, + "required": [ + "driver" + ], + "additionalProperties": true + }, + "ProviderModelPreferences": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "hiddenModels": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "modelOrder": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + } + }, + "ProviderSetupErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProviderSetupError" + ] + }, + "instanceId": { + "type": "string" + }, + "operation": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "instanceId", + "operation", + "detail" + ], + "additionalProperties": true + }, + "ProviderUploadFeedbackError": { + "anyOf": [ + { + "$ref": "#/$defs/ProviderUploadFeedbackErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ProviderUploadFeedbackErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProviderUploadFeedbackError" + ] + }, + "threadId": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "threadId" + ], + "additionalProperties": true + }, + "ProviderUploadFeedbackPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "ProviderUploadFeedbackSuccess": { + "type": "object", + "properties": { + "feedbackId": { + "type": "string" + } + }, + "required": [ + "feedbackId" + ], + "additionalProperties": true + }, + "Providers": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "displayName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "accentColor": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "iconUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "badgeLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "continuation": { + "anyOf": [ + { + "type": "object", + "properties": { + "groupKey": { + "type": "string" + } + }, + "required": [ + "groupKey" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "showInteractionModeToggle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "reportsContextWindow": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "supportedRuntimeModes": { + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + { + "type": "null" + } + ] + } + }, + { + "type": "null" + } + ] + }, + "supportsConversationRollback": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "supportsTextGeneration": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "setup": { + "anyOf": [ + { + "type": "object", + "properties": { + "canAuthenticate": { + "type": "boolean" + }, + "canInstall": { + "type": "boolean" + }, + "documentationUrl": { + "type": "string" + } + }, + "required": [ + "canAuthenticate", + "canInstall" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "nativeSessions": { + "anyOf": [ + { + "type": "object", + "properties": { + "canList": { + "type": "boolean" + }, + "canLoad": { + "type": "boolean" + }, + "canResume": { + "type": "boolean" + }, + "canDelete": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "canList", + "canLoad", + "canResume" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "configurableProviders": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "runtimePaths": { + "type": "object", + "properties": { + "homePath": { + "type": "string" + }, + "shadowHomePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "homePath", + "shadowHomePath" + ], + "additionalProperties": true + }, + "enabled": { + "type": "boolean" + }, + "installed": { + "type": "boolean" + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "installedVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "ready", + "warning", + "error", + "disabled" + ] + }, + "auth": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "authenticated", + "unauthenticated", + "unknown" + ] + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "action": { + "anyOf": [ + { + "$ref": "#/$defs/Action" + }, + { + "type": "null" + } + ] + }, + "canLogout": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "subscriptionSharing": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "profileId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspaceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": true + }, + "checkedAt": { + "type": "string" + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "availability": { + "anyOf": [ + { + "type": "string", + "enum": [ + "available", + "unavailable" + ] + }, + { + "type": "null" + } + ] + }, + "unavailableReason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "models": { + "type": "array", + "items": { + "type": "object", + "properties": { + "slug": { + "type": "string" + }, + "name": { + "type": "string" + }, + "shortName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "subProvider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "aliases": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "badge": { + "anyOf": [ + { + "type": "string", + "enum": [ + "new" + ] + }, + { + "type": "null" + } + ] + }, + "isCustom": { + "type": "boolean" + }, + "isDefault": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "isLegacy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "capabilities": { + "anyOf": [ + { + "type": "object", + "properties": { + "optionDescriptors": { + "anyOf": [ + { + "$ref": "#/$defs/OptionDescriptors" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "slug", + "name", + "isCustom", + "capabilities" + ], + "additionalProperties": true + } + }, + "updateRequiredModels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "slug": { + "type": "string" + }, + "name": { + "type": "string" + }, + "badge": { + "anyOf": [ + { + "type": "string", + "enum": [ + "new" + ] + }, + { + "type": "null" + } + ] + }, + "minVersion": { + "type": "string" + } + }, + "required": [ + "slug", + "name", + "minVersion" + ], + "additionalProperties": true + } + }, + "slashCommands": { + "anyOf": [ + { + "$ref": "#/$defs/SlashCommands" + }, + { + "type": "null" + } + ] + }, + "skills": { + "anyOf": [ + { + "$ref": "#/$defs/Skills2" + }, + { + "type": "null" + } + ] + }, + "workspaceSnapshots": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "checkedAt": { + "type": "string" + }, + "slashCommands": { + "$ref": "#/$defs/SlashCommands" + }, + "slashCommandsPending": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "skills": { + "$ref": "#/$defs/Skills2" + }, + "optionDescriptors": { + "anyOf": [ + { + "$ref": "#/$defs/OptionDescriptors" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "checkedAt", + "slashCommands", + "skills" + ], + "additionalProperties": true + } + }, + "usageLimits": { + "anyOf": [ + { + "$ref": "#/$defs/UsageLimits" + }, + { + "type": "null" + } + ] + }, + "versionAdvisory": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "unknown", + "current", + "behind_latest" + ] + }, + "currentVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "latestVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updateCommand": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "canUpdate": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "canInstallVersion": { + "type": "boolean" + }, + "checkedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status", + "currentVersion", + "latestVersion", + "updateCommand", + "checkedAt", + "message" + ], + "additionalProperties": true + }, + "compatibilityAdvisory": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "unknown", + "supported", + "graceful", + "unsupported", + "broken" + ] + }, + "latestVersionStatus": { + "type": "string", + "enum": [ + "unknown", + "supported", + "graceful", + "unsupported", + "broken" + ] + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "recommendedVersion": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "recommendedRange": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status", + "message", + "recommendedVersion", + "recommendedRange" + ], + "additionalProperties": true + }, + "updateState": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "idle", + "queued", + "running", + "succeeded", + "failed", + "unchanged" + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "finishedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "output": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status", + "startedAt", + "finishedAt", + "message", + "output" + ], + "additionalProperties": true + } + }, + "required": [ + "instanceId", + "driver", + "enabled", + "installed", + "version", + "status", + "auth", + "checkedAt", + "models" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "PullRequest": { + "type": "object", + "properties": { + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "baseBranch": { + "type": "string" + }, + "headBranch": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + } + }, + "required": [ + "number", + "title", + "url", + "baseBranch", + "headBranch", + "state" + ], + "additionalProperties": true + }, + "PullRequestOperationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PullRequestOperationError" + ] + }, + "operation": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string", + "enum": [ + "not-found" + ] + }, + { + "type": "null" + } + ] + }, + "refusal": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge-blocked", + "merge-conflict", + "already-merged" + ] + }, + { + "type": "null" + } + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "detail" + ], + "additionalProperties": true + }, + "PullRequestUnavailableErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PullRequestUnavailableError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "cli-missing", + "cli-unauthenticated", + "provider-unsupported" + ] + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "requirement": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "PullRequests": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "url": { + "type": "string" + }, + "repositoryWebUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "source": { + "type": "string", + "enum": [ + "manual", + "created", + "agent", + "stack", + "stack-dismissed" + ] + }, + "linkedAt": { + "type": "string" + }, + "snapshot": { + "anyOf": [ + { + "$ref": "#/$defs/Snapshot" + }, + { + "type": "null" + } + ] + }, + "stack": { + "anyOf": [ + { + "$ref": "#/$defs/Stack" + }, + { + "type": "null" + } + ] + }, + "watch": { + "anyOf": [ + { + "$ref": "#/$defs/EndedWatch" + }, + { + "type": "null" + } + ] + }, + "endedWatch": { + "anyOf": [ + { + "$ref": "#/$defs/EndedWatch" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "host", + "repository", + "number", + "url", + "source", + "linkedAt", + "snapshot", + "stack" + ], + "additionalProperties": true + } + }, + "PullRequestsActivityError": { + "anyOf": [ + { + "$ref": "#/$defs/PullRequestUnavailableErrorEncoded" + }, + { + "$ref": "#/$defs/PullRequestOperationErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "PullRequestsActivityPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "projectId", + "repository", + "number" + ], + "additionalProperties": true + }, + "PullRequestsActivitySuccess": { + "type": "object", + "properties": { + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "reviewers": { + "anyOf": [ + { + "$ref": "#/$defs/Authors" + }, + { + "type": "null" + } + ] + }, + "comments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "issue-comment", + "review-comment", + "review" + ] + }, + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "body": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "editedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reviewState": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reactions": { + "anyOf": [ + { + "$ref": "#/$defs/Reactions" + }, + { + "type": "null" + } + ] + }, + "canEdit": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "kind", + "author", + "body", + "createdAt", + "url", + "path", + "reviewState" + ], + "additionalProperties": true + } + }, + "commentCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "commentsTruncated": { + "type": "boolean" + }, + "reviewThreadsTruncated": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "reviewThreads": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "path": { + "type": "string" + }, + "line": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "side": { + "type": "string", + "enum": [ + "left", + "right" + ] + }, + "isResolved": { + "type": "boolean" + }, + "isOutdated": { + "type": "boolean" + }, + "comments": { + "$ref": "#/$defs/Comments" + }, + "commentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "nextCommentsCursor": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "canResolve": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "path", + "line", + "side", + "isResolved", + "isOutdated", + "comments" + ], + "additionalProperties": true + } + }, + "commits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "oid": { + "type": "string" + }, + "messageHeadline": { + "type": "string" + }, + "committedDate": { + "type": "string" + }, + "additions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "deletions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "authors": { + "anyOf": [ + { + "$ref": "#/$defs/Authors" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "oid", + "messageHeadline", + "committedDate" + ], + "additionalProperties": true + } + }, + "reactions": { + "anyOf": [ + { + "$ref": "#/$defs/Reactions" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "comments", + "commentCount", + "commentsTruncated", + "reviewThreads", + "commits" + ], + "additionalProperties": true + }, + "PullRequestsChangeBreakdownSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "code": { + "$ref": "#/$defs/Code" + }, + "tests": { + "$ref": "#/$defs/Code" + }, + "mocks": { + "$ref": "#/$defs/Code" + }, + "generated": { + "$ref": "#/$defs/Code" + }, + "docs": { + "anyOf": [ + { + "$ref": "#/$defs/Code" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "code", + "tests", + "mocks", + "generated" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "PullRequestsChecksSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "observedAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "checks": { + "$ref": "#/$defs/Checks" + } + }, + "required": [ + "state", + "checks" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "PullRequestsCommentPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "body": { + "type": "string", + "minLength": 1, + "maxLength": 65536 + } + }, + "required": [ + "projectId", + "repository", + "number", + "body" + ], + "additionalProperties": true + }, + "PullRequestsDetailSuccess": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "capabilities": { + "type": "object", + "properties": { + "diff": { + "type": "boolean" + }, + "comment": { + "type": "boolean" + }, + "actions": { + "$ref": "#/$defs/Actions" + }, + "mergeMethods": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "squash", + "rebase" + ] + }, + { + "type": "null" + } + ] + } + }, + "deleteBranch": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "mergeBypass": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "updateMethods": { + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "rebase" + ] + }, + { + "type": "null" + } + ] + } + }, + { + "type": "null" + } + ] + }, + "search": { + "type": "boolean" + }, + "reactions": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "viewedFiles": { + "anyOf": [ + { + "type": "string", + "enum": [ + "host", + "environment" + ] + }, + { + "type": "null" + } + ] + }, + "review": { + "type": "object", + "properties": { + "inlineComment": { + "type": "boolean" + }, + "rangeComment": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "reply": { + "type": "boolean" + }, + "resolve": { + "type": "boolean" + }, + "verdicts": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "enum": [ + "comment", + "approve", + "request-changes" + ] + }, + { + "type": "null" + } + ] + } + } + }, + "required": [ + "inlineComment", + "reply", + "resolve", + "verdicts" + ], + "additionalProperties": true + }, + "reviewers": { + "type": "object", + "properties": { + "request": { + "type": "boolean" + }, + "listCandidates": { + "type": "boolean" + } + }, + "required": [ + "request", + "listCandidates" + ], + "additionalProperties": true + }, + "edit": { + "anyOf": [ + { + "type": "object", + "properties": { + "changeRequest": { + "type": "boolean" + }, + "comment": { + "type": "boolean" + }, + "maintainerEdits": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "changeRequest", + "comment" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "stacks": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "stackActions": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "checksAlwaysReport": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "diff", + "comment", + "actions", + "mergeMethods", + "search", + "review", + "reviewers" + ], + "additionalProperties": true + }, + "viewerPermissions": { + "type": "object", + "properties": { + "stackRebase": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "actions": { + "$ref": "#/$defs/Actions" + }, + "comment": { + "type": "boolean" + }, + "resolve": { + "type": "boolean" + }, + "verdicts": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "enum": [ + "comment", + "approve", + "request-changes" + ] + }, + { + "type": "null" + } + ] + } + }, + "requestReviewers": { + "type": "boolean" + }, + "updateMethods": { + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "rebase" + ] + }, + { + "type": "null" + } + ] + } + }, + { + "type": "null" + } + ] + }, + "mergeBypass": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "editChangeRequest": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "maintainerEdits": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "actions", + "comment", + "resolve", + "verdicts", + "requestReviewers" + ], + "additionalProperties": true + }, + "projectId": { + "type": "string" + }, + "projectTitle": { + "type": "string" + }, + "workspaceRoot": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + }, + "body": { + "type": "string" + }, + "url": { + "type": "string" + }, + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "isDraft": { + "type": "boolean" + }, + "mergeability": { + "type": "string", + "enum": [ + "mergeable", + "conflicting", + "unknown" + ] + }, + "additions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "changedFiles": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "headBranch": { + "type": "string" + }, + "headRepositoryNameWithOwner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "headSha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "baseBranch": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "observedAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "mergedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "closedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reviewers": { + "$ref": "#/$defs/Authors" + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "color": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name", + "color" + ], + "additionalProperties": true + } + }, + "checks": { + "$ref": "#/$defs/Checks" + }, + "mergeCapabilities": { + "type": "object", + "properties": { + "merge": { + "type": "boolean" + }, + "squash": { + "type": "boolean" + }, + "rebase": { + "type": "boolean" + } + }, + "required": [ + "merge", + "squash", + "rebase" + ], + "additionalProperties": true + }, + "viewer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "baseComparison": { + "anyOf": [ + { + "type": "string", + "enum": [ + "up-to-date", + "behind", + "unknown" + ] + }, + { + "type": "null" + } + ] + }, + "behindBy": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "autoMergeEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "autoMergeMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "squash", + "rebase" + ] + }, + { + "type": "null" + } + ] + }, + "workflowApprovalsRequired": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "maintainerCanModify": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "provider", + "capabilities", + "viewerPermissions", + "projectId", + "projectTitle", + "workspaceRoot", + "repository", + "number", + "title", + "body", + "url", + "author", + "state", + "isDraft", + "mergeability", + "additions", + "deletions", + "changedFiles", + "headBranch", + "baseBranch", + "createdAt", + "updatedAt", + "mergedAt", + "closedAt", + "reviewers", + "labels", + "checks", + "mergeCapabilities" + ], + "additionalProperties": true + }, + "PullRequestsDiffFileContentsPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "commit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "changeType": { + "type": "string", + "enum": [ + "change", + "rename-pure", + "rename-changed", + "new", + "deleted" + ] + }, + "oldPath": { + "type": "string" + }, + "newPath": { + "type": "string" + } + }, + "required": [ + "projectId", + "repository", + "number", + "changeType", + "oldPath", + "newPath" + ], + "additionalProperties": true + }, + "PullRequestsDiffFileContentsSuccess": { + "type": "object", + "properties": { + "oldContents": { + "type": "string" + }, + "newContents": { + "type": "string" + } + }, + "required": [ + "oldContents", + "newContents" + ], + "additionalProperties": true + }, + "PullRequestsFilesViewedSuccess": { + "type": "object", + "properties": { + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "minLength": 1, + "maxLength": 4096 + }, + "state": { + "type": "string", + "enum": [ + "unviewed", + "viewed", + "dismissed" + ] + } + }, + "required": [ + "path", + "state" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "files", + "truncated" + ], + "additionalProperties": true + }, + "PullRequestsFilterOptionsPayload": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "labels", + "authors" + ] + }, + "projectId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "projectIds": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 100 + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "query": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + "PullRequestsFilterOptionsSuccess": { + "type": "object", + "properties": { + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "color": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name", + "color" + ], + "additionalProperties": true + } + }, + "authors": { + "$ref": "#/$defs/Authors" + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "labels", + "authors", + "truncated" + ], + "additionalProperties": true + }, + "PullRequestsGenerateReviewGuideSuccess": { + "type": "object", + "properties": { + "guide": { + "anyOf": [ + { + "type": "object", + "properties": { + "headSha": { + "type": "string" + }, + "generatedAt": { + "type": "string" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "attention": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tradeoff", + "contract", + "product", + "risk", + "accessibility" + ] + }, + "note": { + "type": "string" + } + }, + "required": [ + "kind", + "note" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "excerpts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "side": { + "type": "string", + "enum": [ + "new", + "old" + ] + }, + "start": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "end": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "caption": { + "type": "string" + } + }, + "required": [ + "path", + "side", + "start", + "end", + "caption" + ], + "additionalProperties": true + } + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "note": { + "type": "string" + } + }, + "required": [ + "path" + ], + "additionalProperties": true + } + }, + "notes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "line": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + }, + "body": { + "type": "string" + } + }, + "required": [ + "path", + "line", + "title", + "body" + ], + "additionalProperties": true + } + }, + "questions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "title", + "summary", + "attention", + "excerpts", + "files", + "notes", + "questions" + ], + "additionalProperties": true + } + } + }, + "required": [ + "headSha", + "generatedAt", + "modelSelection", + "stops" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "run": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Running" + ] + }, + "headSha": { + "type": "string" + }, + "startedAt": { + "type": "string" + } + }, + "required": [ + "_tag", + "headSha", + "startedAt" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Failed" + ] + }, + "headSha": { + "type": "string" + }, + "failedAt": { + "type": "string" + }, + "detail": { + "type": "string" + } + }, + "required": [ + "_tag", + "headSha", + "failedAt", + "detail" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "currentHeadSha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "guide", + "run", + "currentHeadSha" + ], + "additionalProperties": true + }, + "PullRequestsInvalidatePayload": { + "type": "object", + "properties": { + "reference": { + "anyOf": [ + { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + { + "type": "null" + } + ] + }, + "filesViewedOnly": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "PullRequestsLabelCandidatesSuccess": { + "type": "object", + "properties": { + "candidates": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "color": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "isApplied": { + "type": "boolean" + } + }, + "required": [ + "name", + "color", + "description", + "isApplied" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "candidates", + "truncated" + ], + "additionalProperties": true + }, + "PullRequestsLinkedThreadsSuccess": { + "type": "object", + "properties": { + "threads": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "archivedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "projectId", + "title", + "archivedAt" + ], + "additionalProperties": true + } + } + }, + "required": [ + "threads" + ], + "additionalProperties": true + }, + "PullRequestsListPayload": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "all", + "open", + "closed", + "merged" + ] + }, + "involvement": { + "anyOf": [ + { + "type": "string", + "enum": [ + "all", + "reviewing", + "authored" + ] + }, + { + "type": "null" + } + ] + }, + "filters": { + "anyOf": [ + { + "type": "object", + "properties": { + "draft": { + "anyOf": [ + { + "type": "string", + "enum": [ + "only", + "hide" + ] + }, + { + "type": "null" + } + ] + }, + "review": { + "anyOf": [ + { + "type": "string", + "enum": [ + "approved", + "changes-requested", + "review-required", + "none" + ] + }, + { + "type": "null" + } + ] + }, + "checks": { + "anyOf": [ + { + "type": "string", + "enum": [ + "passing", + "failing" + ] + }, + { + "type": "null" + } + ] + }, + "labels": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 10 + }, + "maxItems": 10 + }, + { + "type": "null" + } + ] + }, + "excludedLabels": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 10 + }, + { + "type": "null" + } + ] + }, + "author": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "projectId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "projectIds": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 100 + }, + { + "type": "null" + } + ] + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "limit": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + { + "type": "null" + } + ] + }, + "cursors": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "query": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "state" + ], + "additionalProperties": true + }, + "PullRequestsListStatsPayload": { + "type": "object", + "properties": { + "refs": { + "type": "array", + "items": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "maxItems": 500 + } + }, + "required": [ + "refs" + ], + "additionalProperties": true + }, + "PullRequestsListStatsSuccess": { + "type": "object", + "properties": { + "stats": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "additions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "projectId", + "repository", + "number", + "additions", + "deletions" + ], + "additionalProperties": true + } + } + }, + "required": [ + "stats" + ], + "additionalProperties": true + }, + "PullRequestsListSuccess": { + "type": "object", + "properties": { + "viewers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "providers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "searchesOnHost": { + "type": "boolean" + }, + "actions": { + "anyOf": [ + { + "$ref": "#/$defs/Actions" + }, + { + "type": "null" + } + ] + }, + "projectCount": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "configured": { + "type": "boolean" + }, + "detail": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "host", + "kind", + "searchesOnHost", + "projectCount", + "configured", + "detail" + ], + "additionalProperties": true + } + }, + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "stack": { + "anyOf": [ + { + "$ref": "#/$defs/Stack2" + }, + { + "type": "null" + } + ] + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectTitle": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "headBranch": { + "type": "string" + }, + "baseBranch": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "isDraft": { + "type": "boolean" + }, + "mergeability": { + "type": "string", + "enum": [ + "mergeable", + "conflicting", + "unknown" + ] + }, + "additions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "observedAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "viewerReviewRequested": { + "type": "boolean" + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "color": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name", + "color" + ], + "additionalProperties": true + } + }, + "reviewDecision": { + "anyOf": [ + { + "type": "string", + "enum": [ + "approved", + "changes-requested", + "review-required" + ] + }, + { + "type": "null" + } + ] + }, + "checksState": { + "anyOf": [ + { + "type": "string", + "enum": [ + "passing", + "failing", + "pending" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "provider", + "host", + "projectId", + "projectTitle", + "repository", + "number", + "title", + "url", + "author", + "headBranch", + "baseBranch", + "state", + "isDraft", + "mergeability", + "additions", + "deletions", + "createdAt", + "updatedAt", + "viewerReviewRequested", + "labels" + ], + "additionalProperties": true + } + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "projectTitle": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "projectId", + "projectTitle", + "message" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + }, + "nextCursors": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "viewers", + "providers", + "entries", + "errors", + "truncated", + "nextCursors" + ], + "additionalProperties": true + }, + "PullRequestsPreviewSuccess": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "isDraft": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "projectId", + "repository", + "number", + "title", + "url", + "author", + "state", + "isDraft", + "createdAt" + ], + "additionalProperties": true + }, + "PullRequestsReplyToThreadPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "threadId": { + "type": "string" + }, + "body": { + "type": "string", + "minLength": 1, + "maxLength": 65536 + } + }, + "required": [ + "projectId", + "repository", + "number", + "threadId", + "body" + ], + "additionalProperties": true + }, + "PullRequestsReportStatePayload": { + "type": "object", + "properties": { + "reference": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "isDraft": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "reference", + "state" + ], + "additionalProperties": true + }, + "PullRequestsRequestReviewersPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "reviewers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "user", + "team" + ] + } + }, + "required": [ + "id", + "kind" + ], + "additionalProperties": true + }, + "minItems": 1, + "maxItems": 25 + }, + "requested": { + "type": "boolean" + } + }, + "required": [ + "projectId", + "repository", + "number", + "reviewers", + "requested" + ], + "additionalProperties": true + }, + "PullRequestsReviewerCandidatesSuccess": { + "type": "object", + "properties": { + "candidates": { + "type": "array", + "items": { + "type": "object", + "properties": { + "isBot": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "login": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "avatarUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "user", + "team" + ] + }, + "isRequested": { + "type": "boolean" + } + }, + "required": [ + "login", + "name", + "avatarUrl", + "id", + "kind", + "isRequested" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "candidates", + "truncated" + ], + "additionalProperties": true + }, + "PullRequestsRoutingIdentityPayload": { + "type": "object", + "properties": { + "host": { + "type": "string" + } + }, + "required": [ + "host" + ], + "additionalProperties": true + }, + "PullRequestsRoutingIdentitySuccess": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "host": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "viewer": { + "type": "string" + } + }, + "required": [ + "accountId", + "host", + "provider", + "viewer" + ], + "additionalProperties": true + }, + "PullRequestsRoutingSuccess": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "host": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "viewer": { + "type": "string" + }, + "projectTitle": { + "type": "string" + }, + "workspaceRoot": { + "type": "string" + } + }, + "required": [ + "accountId", + "host", + "provider", + "viewer", + "projectTitle", + "workspaceRoot" + ], + "additionalProperties": true + }, + "PullRequestsRunActionPayload": { + "type": "object", + "properties": { + "stackNumber": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "expectedStackHeads": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "headSha": { + "type": "string" + } + }, + "required": [ + "number", + "headSha" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + }, + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "action": { + "type": "string", + "enum": [ + "merge", + "ready", + "draft", + "close", + "reopen", + "delete-branch", + "update-branch", + "enable-auto-merge", + "disable-auto-merge", + "revert", + "approve-workflows" + ] + }, + "mergeMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "squash", + "rebase" + ] + }, + { + "type": "null" + } + ] + }, + "updateMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "rebase" + ] + }, + { + "type": "null" + } + ] + }, + "bypassRules": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "projectId", + "repository", + "number", + "action" + ], + "additionalProperties": true + }, + "PullRequestsSetFilesViewedPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "minLength": 1, + "maxLength": 4096 + }, + "viewed": { + "type": "boolean" + } + }, + "required": [ + "path", + "viewed" + ], + "additionalProperties": true + }, + "maxItems": 500 + } + }, + "required": [ + "projectId", + "repository", + "number", + "files" + ], + "additionalProperties": true + }, + "PullRequestsSetLabelsPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "labels": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 25 + }, + "applied": { + "type": "boolean" + } + }, + "required": [ + "projectId", + "repository", + "number", + "labels", + "applied" + ], + "additionalProperties": true + }, + "PullRequestsSetReactionPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "subjectId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "content": { + "type": "string", + "enum": [ + "thumbs-up", + "thumbs-down", + "laugh", + "hooray", + "confused", + "heart", + "rocket", + "eyes" + ] + }, + "reacted": { + "type": "boolean" + } + }, + "required": [ + "projectId", + "repository", + "number", + "content", + "reacted" + ], + "additionalProperties": true + }, + "PullRequestsSetThreadResolutionPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "threadId": { + "type": "string" + }, + "resolved": { + "type": "boolean" + } + }, + "required": [ + "projectId", + "repository", + "number", + "threadId", + "resolved" + ], + "additionalProperties": true + }, + "PullRequestsStackSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "url": { + "type": "string" + }, + "base": { + "type": "string" + }, + "layers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "isDraft": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "headSha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "headBranch": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + } + }, + "required": [ + "number", + "headBranch", + "state" + ], + "additionalProperties": true + } + } + }, + "required": [ + "id", + "number", + "url", + "base", + "layers" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "PullRequestsSubmitReviewPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "verdict": { + "type": "string", + "enum": [ + "comment", + "approve", + "request-changes" + ] + }, + "body": { + "type": "string", + "maxLength": 65536 + }, + "comments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "oldPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "position": { + "anyOf": [ + { + "$ref": "#/$defs/Position" + }, + { + "$ref": "#/$defs/Position2" + }, + { + "$ref": "#/$defs/Position3" + } + ] + }, + "startPosition": { + "anyOf": [ + { + "$ref": "#/$defs/Position" + }, + { + "$ref": "#/$defs/Position2" + }, + { + "$ref": "#/$defs/Position3" + }, + { + "type": "null" + } + ] + }, + "body": { + "type": "string", + "minLength": 1, + "maxLength": 65536 + } + }, + "required": [ + "path", + "position", + "body" + ], + "additionalProperties": true + } + } + }, + "required": [ + "projectId", + "repository", + "number", + "verdict", + "body", + "comments" + ], + "additionalProperties": true + }, + "PullRequestsSubscribeRefreshesSuccess": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "PullRequestsSummarySuccess": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "isDraft": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "headBranch": { + "type": "string" + }, + "baseBranch": { + "type": "string" + }, + "closedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "mergedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + }, + "observedAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "additions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "deletions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "changedFiles": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reviewDecision": { + "anyOf": [ + { + "type": "string", + "enum": [ + "approved", + "changes-requested", + "review-required" + ] + }, + { + "type": "null" + } + ] + }, + "checksState": { + "anyOf": [ + { + "type": "string", + "enum": [ + "passing", + "failing", + "pending" + ] + }, + { + "type": "null" + } + ] + }, + "mergeability": { + "anyOf": [ + { + "type": "string", + "enum": [ + "mergeable", + "conflicting", + "unknown" + ] + }, + { + "type": "null" + } + ] + }, + "stack": { + "anyOf": [ + { + "$ref": "#/$defs/Stack2" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "provider", + "projectId", + "repository", + "number", + "title", + "url", + "state", + "headBranch", + "baseBranch", + "updatedAt" + ], + "additionalProperties": true + }, + "PullRequestsThreadCommentsPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "threadId": { + "type": "string" + }, + "cursor": { + "type": "string" + } + }, + "required": [ + "projectId", + "repository", + "number", + "threadId", + "cursor" + ], + "additionalProperties": true + }, + "PullRequestsThreadCommentsSuccess": { + "type": "object", + "properties": { + "comments": { + "$ref": "#/$defs/Comments" + }, + "nextCursor": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "comments", + "nextCursor" + ], + "additionalProperties": true + }, + "PullRequestsUpdateCommentPayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "commentId": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "issue-comment", + "review-comment" + ] + }, + "body": { + "type": "string", + "minLength": 1, + "maxLength": 65536 + } + }, + "required": [ + "projectId", + "repository", + "number", + "commentId", + "kind", + "body" + ], + "additionalProperties": true + }, + "PullRequestsUpdatePayload": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "expectedAccountId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "allowStale": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "repository": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "body": { + "anyOf": [ + { + "type": "string", + "maxLength": 65536 + }, + { + "type": "null" + } + ] + }, + "maintainerCanModify": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "projectId", + "repository", + "number" + ], + "additionalProperties": true + }, + "Reactions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "content": { + "type": "string", + "enum": [ + "thumbs-up", + "thumbs-down", + "laugh", + "hooray", + "confused", + "heart", + "rocket", + "eyes" + ] + }, + "count": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "actors": { + "type": "array", + "items": { + "type": "string" + } + }, + "viewerHasReacted": { + "type": "boolean" + } + }, + "required": [ + "content", + "count", + "actors", + "viewerHasReacted" + ], + "additionalProperties": true + } + }, + "Registration": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "pattern": "^oaiapp_[\\w-]+$" + }, + "subject": { + "type": "string" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "redirectUri": { + "type": "string", + "pattern": "^http:\\/\\/(?:127\\.0\\.0\\.1|localhost):[1-9]\\d{0,4}\\/auth\\/callback$" + }, + "connectionLabel": { + "type": "string" + }, + "sharingEnabled": { + "type": "boolean" + }, + "idTokenHint": { + "type": "string", + "maxLength": 16384 + } + }, + "required": [ + "clientId" + ], + "additionalProperties": true + }, + "RelayClientInstallFailedErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "RelayClientInstallFailedError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "download_failed", + "invalid_checksum", + "install_locked", + "managed_by_policy", + "override_missing", + "unsupported_platform", + "validation_failed", + "write_failed" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "reason", + "message" + ], + "additionalProperties": true + }, + "Remote": { + "type": "object", + "properties": { + "hasUpstream": { + "type": "boolean" + }, + "aheadCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "behindCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "aheadOfDefaultCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "pr": { + "anyOf": [ + { + "$ref": "#/$defs/Pr" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "hasUpstream", + "aheadCount", + "behindCount", + "pr" + ], + "additionalProperties": true + }, + "RemoteChild": { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "label": { + "type": "string" + }, + "runId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "stoppedThere": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "stopCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "environmentId", + "threadId", + "label" + ], + "additionalProperties": true + }, + "Repository": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "nameWithOwner": { + "type": "string" + }, + "url": { + "type": "string" + }, + "sshUrl": { + "type": "string" + } + }, + "required": [ + "provider", + "nameWithOwner", + "url", + "sshUrl" + ], + "additionalProperties": true + }, + "RepositoryIdentity": { + "type": "object", + "properties": { + "canonicalKey": { + "type": "string" + }, + "locator": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": [ + "git-remote" + ] + }, + "remoteName": { + "type": "string" + }, + "remoteUrl": { + "type": "string" + } + }, + "required": [ + "source", + "remoteName", + "remoteUrl" + ], + "additionalProperties": true + }, + "webUrl": { + "type": "string" + }, + "rootPath": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "name": { + "type": "string" + }, + "origin": { + "type": "object", + "properties": { + "canonicalKey": { + "type": "string" + }, + "remoteUrl": { + "type": "string" + }, + "displayName": { + "type": "string" + } + }, + "required": [ + "canonicalKey" + ], + "additionalProperties": true + } + }, + "required": [ + "canonicalKey", + "locator" + ], + "additionalProperties": true + }, + "Resource": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "workspace-file" + ] + }, + "threadId": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "_tag", + "threadId", + "path" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "media-file" + ] + }, + "threadId": { + "type": "string" + }, + "path": { + "type": "string" + }, + "disposition": { + "type": "string", + "enum": [ + "attachment" + ] + } + }, + "required": [ + "_tag", + "threadId", + "path" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "draft-workspace-file" + ] + }, + "cwd": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "_tag", + "cwd", + "path" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "attachment" + ] + }, + "attachmentId": { + "type": "string" + }, + "fileName": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "disposition": { + "type": "string", + "enum": [ + "inline", + "attachment" + ] + }, + "renderIntent": { + "type": "string", + "enum": [ + "mcp-app" + ] + } + }, + "required": [ + "_tag", + "attachmentId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "tool-output-image" + ] + }, + "threadId": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "index": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "_tag", + "threadId", + "itemId", + "index" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "project-favicon" + ] + }, + "cwd": { + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "cwd" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "native-app-icon" + ] + }, + "app": { + "$ref": "#/$defs/App" + } + }, + "required": [ + "_tag", + "app" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "github-media" + ] + }, + "cwd": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "_tag", + "cwd", + "url" + ], + "additionalProperties": true + } + ] + }, + "ReviewGetBranchCommitsError": { + "anyOf": [ + { + "$ref": "#/$defs/VcsProcessSpawnErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessExitErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessTimeoutErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessStdinWriteErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessOutputReadErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessOutputLimitErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessMissingExitCodeErrorEncoded" + }, + { + "$ref": "#/$defs/VcsRepositoryDetectionErrorEncoded" + }, + { + "$ref": "#/$defs/VcsUnsupportedOperationErrorEncoded" + }, + { + "$ref": "#/$defs/GitCommandErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ReviewGetBranchCommitsPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "baseRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "ReviewGetBranchCommitsSuccess": { + "type": "object", + "properties": { + "commits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sha": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "committedAt": { + "type": "string" + } + }, + "required": [ + "sha", + "subject", + "committedAt" + ], + "additionalProperties": true + } + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "commits", + "truncated" + ], + "additionalProperties": true + }, + "ReviewGetDiffFileContentsPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "sourceKind": { + "type": "string", + "enum": [ + "working-tree", + "branch-range", + "commit" + ] + }, + "changeType": { + "type": "string", + "enum": [ + "change", + "rename-pure", + "rename-changed", + "new", + "deleted" + ] + }, + "baseRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "headRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "oldPath": { + "type": "string" + }, + "newPath": { + "type": "string" + } + }, + "required": [ + "cwd", + "sourceKind", + "changeType", + "baseRef", + "headRef", + "oldPath", + "newPath" + ], + "additionalProperties": true + }, + "ReviewGetDiffPreviewPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "baseRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "commitSha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ignoreWhitespace": { + "type": "boolean" + }, + "sourceKind": { + "type": "string", + "enum": [ + "working-tree", + "branch-range" + ] + }, + "trackedOnly": { + "type": "boolean" + }, + "metadataOnly": { + "type": "boolean" + }, + "file": { + "type": "object", + "properties": { + "path": { + "type": "string", + "minLength": 1 + }, + "previousPath": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ] + }, + "sourceKind": { + "type": "string", + "enum": [ + "working-tree", + "branch-range", + "commit" + ] + } + }, + "required": [ + "path", + "previousPath", + "sourceKind" + ], + "additionalProperties": true + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "ReviewGetDiffPreviewSuccess": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "generatedAt": { + "type": "string" + }, + "sources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "working-tree", + "branch-range", + "commit" + ] + }, + "title": { + "type": "string" + }, + "commitSubject": { + "type": "string" + }, + "baseRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "headRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "diff": { + "type": "string" + }, + "diffHash": { + "type": "string" + }, + "truncated": { + "type": "boolean" + }, + "patchesDeferred": { + "type": "boolean" + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "previousPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "additions": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "deletions": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "path", + "previousPath", + "additions", + "deletions" + ], + "additionalProperties": true + } + } + }, + "required": [ + "id", + "kind", + "title", + "baseRef", + "headRef", + "diff", + "diffHash", + "truncated" + ], + "additionalProperties": true + } + }, + "incomplete": { + "type": "boolean" + } + }, + "required": [ + "cwd", + "generatedAt", + "sources" + ], + "additionalProperties": true + }, + "Schedule3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "fixed_time" + ] + }, + "timeOfDay": { + "type": "string" + }, + "weekdays": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "integer", + "minimum": 0, + "maximum": 6, + "description": "Weekday. 0 is Sunday, 6 is Saturday." + }, + "description": "Default: every day." + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "timeOfDay" + ], + "additionalProperties": true + }, + "ScheduledTaskErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ScheduledTaskError" + ] + }, + "message": { + "type": "string" + }, + "taskId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": true + }, + "ScheduledTasksDeleteError": { + "anyOf": [ + { + "$ref": "#/$defs/ScheduledTaskErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ScheduledTasksDeletePayload": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": true + }, + "ScheduledTasksGetWebhookDeliveryPayload": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "deliveryId": { + "type": "string" + }, + "acceptAnyOutcome": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "deliveryId" + ], + "additionalProperties": true + }, + "ScheduledTasksGetWebhookDeliverySuccess": { + "type": "object", + "properties": { + "delivery": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "taskId": { + "type": "string" + }, + "receivedAt": { + "type": "string" + }, + "method": { + "type": "string" + }, + "contentType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "bodyBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "outcome": { + "anyOf": [ + { + "type": "string", + "enum": [ + "accepted", + "dispatch_failed", + "duplicate", + "coalesced", + "rejected_signature", + "disabled", + "rate_limited", + "expired", + "filtered" + ] + }, + { + "type": "string" + } + ] + }, + "signatureVerified": { + "type": "boolean" + }, + "pendingWindow": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "missingFields": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "query": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "body": { + "type": "string" + }, + "bodyTruncated": { + "type": "boolean" + }, + "renderedPrompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "taskId", + "receivedAt", + "method", + "contentType", + "bodyBytes", + "outcome", + "signatureVerified", + "missingFields", + "error", + "query", + "headers", + "body", + "bodyTruncated", + "renderedPrompt" + ], + "additionalProperties": true + } + }, + "required": [ + "delivery" + ], + "additionalProperties": true + }, + "ScheduledTasksListWebhookDeliveriesPayload": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "acceptAnyOutcome": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id" + ], + "additionalProperties": true + }, + "ScheduledTasksListWebhookDeliveriesSuccess": { + "type": "object", + "properties": { + "deliveries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "taskId": { + "type": "string" + }, + "receivedAt": { + "type": "string" + }, + "method": { + "type": "string" + }, + "contentType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "bodyBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "outcome": { + "anyOf": [ + { + "type": "string", + "enum": [ + "accepted", + "dispatch_failed", + "duplicate", + "coalesced", + "rejected_signature", + "disabled", + "rate_limited", + "expired", + "filtered" + ] + }, + { + "type": "string" + } + ] + }, + "signatureVerified": { + "type": "boolean" + }, + "pendingWindow": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "missingFields": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "taskId", + "receivedAt", + "method", + "contentType", + "bodyBytes", + "outcome", + "signatureVerified", + "missingFields", + "error" + ], + "additionalProperties": true + } + } + }, + "required": [ + "deliveries" + ], + "additionalProperties": true + }, + "ScheduledTasksRotateWebhookTokenSuccess": { + "type": "object", + "properties": { + "task": { + "$ref": "#/$defs/Task" + } + }, + "required": [ + "task" + ], + "additionalProperties": true + }, + "ScheduledTasksSetEnabledPayload": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "id", + "enabled" + ], + "additionalProperties": true + }, + "ScheduledTasksSubscribeSuccess": { + "type": "object", + "properties": { + "tasks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/Task" + }, + { + "type": "null" + } + ] + } + } + }, + "required": [ + "tasks" + ], + "additionalProperties": true + }, + "ScheduledTasksUpsertPayload": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "requireExisting": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Reject the save if the task no longer exists, for edits from a client form." + }, + "commandId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "title": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "schedule": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "interval" + ] + }, + "everyMs": { + "type": "integer", + "maximum": 9007199254740991, + "minimum": 60000, + "description": "Min 60000." + } + }, + "required": [ + "type", + "everyMs" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/Schedule3" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "once" + ] + }, + "runAt": { + "type": "string" + } + }, + "required": [ + "type", + "runAt" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "signature": { + "anyOf": [ + { + "type": "object", + "properties": { + "header": { + "type": "string" + }, + "encoding": { + "type": "string", + "enum": [ + "hex", + "base64" + ] + }, + "prefix": { + "type": "string", + "description": "Text before the digest, such as 'sha256='. Empty for none." + }, + "secret": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Default: stored secret." + }, + "secretRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "From `request_secret`. Instead of `secret`. Spent by this save." + } + }, + "required": [ + "header", + "encoding", + "prefix" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ], + "description": "Omit for URL token only." + }, + "maxDeliveryAgeMinutes": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 1440, + "description": "Skip requests held offline over this many minutes. Omit to run all." + }, + { + "type": "null" + } + ] + }, + "dedupe": { + "anyOf": [ + { + "$ref": "#/$defs/Dedupe" + }, + { + "type": "null" + } + ] + }, + "filters": { + "anyOf": [ + { + "$ref": "#/$defs/Filters" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true, + "description": "Run per webhook request. Prompt can use {{body.path}}, {{headers.name}}, {{query.name}}, {{body}}, {{request}}." + } + ] + }, + "projectId": { + "type": "string" + }, + "threadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspaceStrategy": { + "$ref": "#/$defs/WorkspaceStrategy3" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + }, + "createdBy": { + "anyOf": [ + { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + { + "type": "null" + } + ] + }, + "creationSource": { + "anyOf": [ + { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "title", + "prompt", + "enabled", + "schedule", + "projectId", + "workspaceStrategy", + "modelSelection", + "runtimeMode", + "interactionMode" + ], + "additionalProperties": true + }, + "Scopes": { + "type": "array", + "items": { + "$ref": "#/$defs/PermissionsEntry" + } + }, + "SecretRequestErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "SecretRequestError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "load_failed", + "not_found", + "already_answered", + "agent_stopped", + "store_failed", + "record_failed", + "invalid_ref", + "read_failed", + "ref_unavailable", + "ref_expired", + "consume_failed", + "not_a_file_request" + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "SecretsAnswerRequestError": { + "anyOf": [ + { + "$ref": "#/$defs/SecretRequestErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "SecretsAnswerRequestPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "turnItemId": { + "type": "string" + }, + "answer": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "save" + ] + }, + "secret": { + "type": "string" + } + }, + "required": [ + "type", + "secret" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "decline" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "threadId", + "turnItemId", + "answer" + ], + "additionalProperties": true + }, + "ServerAcceptAcpRegistryUrlAuthPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "elicitationId": { + "type": "string" + } + }, + "required": [ + "instanceId", + "elicitationId" + ], + "additionalProperties": true + }, + "ServerAcceptAcpRegistryUrlAuthSuccess": { + "type": "object", + "properties": { + "accepted": { + "type": "boolean" + } + }, + "required": [ + "accepted" + ], + "additionalProperties": true + }, + "ServerCheckOtlpEndpointPayload": { + "type": "object", + "properties": { + "signal": { + "type": "string", + "enum": [ + "traces", + "metrics", + "logs" + ] + }, + "url": { + "type": "string" + } + }, + "required": [ + "signal", + "url" + ], + "additionalProperties": true + }, + "ServerCheckOtlpEndpointSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Accepted" + ] + }, + "latencyMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "_tag", + "latencyMs" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Rejected" + ] + }, + "status": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "_tag", + "status" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Unreachable" + ] + }, + "timedOut": { + "type": "boolean" + } + }, + "required": [ + "_tag", + "timedOut" + ], + "additionalProperties": true + } + ] + }, + "ServerCheckSecretSourcePayload": { + "type": "object", + "properties": { + "source": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "reference": { + "type": "string" + } + }, + "required": [ + "provider", + "reference" + ], + "additionalProperties": true + } + }, + "required": [ + "source" + ], + "additionalProperties": true + }, + "ServerCheckSecretSourceSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Resolved" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Failed" + ] + }, + "detail": { + "type": "string" + } + }, + "required": [ + "_tag", + "detail" + ], + "additionalProperties": true + } + ] + }, + "ServerCommitDesktopUpdateError": { + "anyOf": [ + { + "$ref": "#/$defs/ServerSelfUpdateErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerCommitDesktopUpdatePayload": { + "type": "object", + "properties": { + "requestId": { + "type": "string" + } + }, + "required": [ + "requestId" + ], + "additionalProperties": true + }, + "ServerCommitDesktopUpdateSuccess": { + "type": "object", + "properties": { + "targetVersion": { + "type": "string" + }, + "method": { + "type": "string", + "enum": [ + "boot-service", + "respawn", + "desktop-app" + ] + }, + "updateId": { + "type": "string" + }, + "desktopUpdateToken": { + "type": "string" + } + }, + "required": [ + "targetVersion", + "method" + ], + "additionalProperties": true + }, + "ServerDeleteAcpRegistrySessionError": { + "anyOf": [ + { + "$ref": "#/$defs/AcpRegistryOperationErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerDeleteAcpRegistrySessionPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "sessionId": { + "type": "string" + } + }, + "required": [ + "instanceId", + "projectId", + "sessionId" + ], + "additionalProperties": true + }, + "ServerDeleteAcpRegistrySessionSuccess": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "deleted" + ], + "additionalProperties": true + }, + "ServerDisableAcpRegistryProviderPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "providerId": { + "type": "string" + } + }, + "required": [ + "instanceId", + "projectId", + "providerId" + ], + "additionalProperties": true + }, + "ServerDisableAcpRegistryProviderSuccess": { + "type": "object", + "properties": { + "disabled": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "disabled" + ], + "additionalProperties": true + }, + "ServerDiscoverSourceControlSuccess": { + "type": "object", + "properties": { + "versionControlSystems": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "git", + "jj", + "unknown" + ] + }, + "implemented": { + "type": "boolean" + }, + "label": { + "type": "string" + }, + "executable": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "available", + "missing" + ] + }, + "version": { + "$ref": "#/$defs/LastError" + }, + "installHint": { + "type": "string" + }, + "detail": { + "$ref": "#/$defs/LastError" + } + }, + "required": [ + "kind", + "implemented", + "label", + "status", + "version", + "installHint", + "detail" + ], + "additionalProperties": true + } + }, + "sourceControlProviders": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "label": { + "type": "string" + }, + "executable": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "available", + "missing" + ] + }, + "version": { + "$ref": "#/$defs/LastError" + }, + "installHint": { + "type": "string" + }, + "detail": { + "$ref": "#/$defs/LastError" + }, + "auth": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "authenticated", + "unauthenticated", + "unknown" + ] + }, + "account": { + "$ref": "#/$defs/LastError" + }, + "host": { + "$ref": "#/$defs/LastError" + }, + "detail": { + "$ref": "#/$defs/LastError" + }, + "accounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "account": { + "type": "string" + }, + "active": { + "type": "boolean" + }, + "authenticated": { + "type": "boolean" + }, + "error": { + "type": "string" + }, + "environmentVariable": { + "type": "string" + } + }, + "required": [ + "host", + "account", + "active", + "authenticated" + ], + "additionalProperties": true + } + } + }, + "required": [ + "status", + "account", + "host", + "detail" + ], + "additionalProperties": true + }, + "appSignIn": { + "type": "boolean" + } + }, + "required": [ + "kind", + "label", + "status", + "version", + "installHint", + "detail", + "auth" + ], + "additionalProperties": true + } + } + }, + "required": [ + "versionControlSystems", + "sourceControlProviders" + ], + "additionalProperties": true + }, + "ServerGetConfigError": { + "anyOf": [ + { + "$ref": "#/$defs/KeybindingsConfigParseErrorEncoded" + }, + { + "$ref": "#/$defs/ServerSettingsErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerGetConfigSuccess": { + "type": "object", + "properties": { + "environment": { + "$ref": "#/$defs/Environment" + }, + "auth": { + "type": "object", + "properties": { + "policy": { + "type": "string", + "enum": [ + "desktop-managed-local", + "loopback-browser", + "remote-reachable", + "unsafe-no-auth" + ] + }, + "bootstrapMethods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "desktop-bootstrap", + "one-time-token" + ] + } + }, + "sessionMethods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "browser-session-cookie", + "bearer-access-token", + "dpop-access-token" + ] + } + }, + "sessionCookieName": { + "type": "string" + }, + "serverUpdateScope": { + "type": "string", + "enum": [ + "environment:maintain" + ] + } + }, + "required": [ + "policy", + "bootstrapMethods", + "sessionMethods", + "sessionCookieName" + ], + "additionalProperties": true + }, + "cwd": { + "type": "string" + }, + "keybindingsConfigPath": { + "type": "string" + }, + "keybindings": { + "$ref": "#/$defs/Keybindings" + }, + "issues": { + "$ref": "#/$defs/Issues" + }, + "providers": { + "$ref": "#/$defs/Providers" + }, + "availableEditors": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/Editor" + }, + { + "type": "null" + } + ] + } + }, + "remoteOpenTargets": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tailscale", + "mdns" + ] + }, + "host": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "required": [ + "kind", + "host" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "directEndpoints": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "lan", + "tailnet" + ] + }, + "httpBaseUrl": { + "type": "string" + } + }, + "required": [ + "kind", + "httpBaseUrl" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "observability": { + "type": "object", + "properties": { + "logsDirectoryPath": { + "type": "string" + }, + "localTracingEnabled": { + "type": "boolean" + }, + "otlpTracesUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpTracesSource": { + "anyOf": [ + { + "type": "string", + "enum": [ + "environment", + "bootstrap", + "settings" + ] + }, + { + "type": "null" + } + ] + }, + "otlpTracesVariable": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpTracesEnabled": { + "type": "boolean" + }, + "otlpMetricsUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpMetricsSource": { + "anyOf": [ + { + "type": "string", + "enum": [ + "environment", + "bootstrap", + "settings" + ] + }, + { + "type": "null" + } + ] + }, + "otlpMetricsVariable": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpMetricsEnabled": { + "type": "boolean" + }, + "otlpLogsUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpLogsSource": { + "anyOf": [ + { + "type": "string", + "enum": [ + "environment", + "bootstrap", + "settings" + ] + }, + { + "type": "null" + } + ] + }, + "otlpLogsVariable": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpLogsEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "logsDirectoryPath", + "localTracingEnabled", + "otlpTracesEnabled", + "otlpMetricsEnabled" + ], + "additionalProperties": true + }, + "settings": { + "$ref": "#/$defs/Settings2" + }, + "managedPolicy": { + "type": "object", + "properties": { + "organization": { + "type": "string" + }, + "supportUrl": { + "type": "string" + }, + "lockedSettings": { + "type": "array", + "items": { + "type": "string" + } + }, + "limits": { + "type": "object", + "properties": { + "maxRuntimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "clients": { + "type": "array", + "prefixItems": [ + { + "type": "string", + "enum": [ + "desktop", + "local-browser", + "remote" + ] + } + ], + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "desktop", + "local-browser", + "remote" + ] + } + }, + "connect": { + "type": "boolean" + }, + "webhookTasks": { + "type": "boolean" + }, + "telemetry": { + "type": "boolean" + }, + "browserCookieImport": { + "type": "boolean" + }, + "providers": { + "type": "object", + "properties": { + "allowedDrivers": { + "type": "array", + "prefixItems": [ + { + "type": "string" + } + ], + "minItems": 1, + "items": { + "type": "string" + } + }, + "lockedInstanceFields": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "additionalProperties": true + }, + "updates": { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "auto", + "off" + ] + }, + "allowNightly": { + "type": "boolean" + } + }, + "required": [ + "mode" + ], + "additionalProperties": true + } + }, + "additionalProperties": true + } + }, + "required": [ + "organization", + "lockedSettings" + ], + "additionalProperties": true + }, + "shellResumeCompletionMarker": { + "type": "boolean" + }, + "shellThreadFamilies": { + "type": "boolean" + }, + "shellOlderSettled": { + "type": "boolean" + }, + "shellRevealInFileManager": { + "type": "boolean" + }, + "shellRevealInFileManagerKind": { + "type": "string", + "enum": [ + "finder", + "file-explorer", + "files" + ] + }, + "threadResumeCompletionMarker": { + "type": "boolean" + }, + "threadSnapshotPagination": { + "type": "boolean" + }, + "threadFind": { + "type": "boolean" + }, + "threadFindProgressive": { + "type": "boolean" + }, + "secretProviderSettings": { + "type": "boolean" + }, + "secretReferences": { + "type": "boolean" + }, + "scratchWorkspaceRoot": { + "type": "string" + }, + "newProjectsRoot": { + "type": "string" + }, + "lastEventLoopStall": { + "type": "object", + "properties": { + "detectedAt": { + "type": "string" + }, + "durationMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "loadAverage": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cpuCount": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "required": [ + "detectedAt", + "durationMs", + "cpuCount" + ], + "additionalProperties": true + }, + "environmentThemes": { + "anyOf": [ + { + "$ref": "#/$defs/EnvironmentThemes" + }, + { + "type": "null" + } + ] + }, + "usageLimitSources": { + "anyOf": [ + { + "$ref": "#/$defs/UsageLimitSources3" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "environment", + "auth", + "cwd", + "keybindingsConfigPath", + "keybindings", + "issues", + "providers", + "availableEditors", + "observability", + "settings" + ], + "additionalProperties": true + }, + "ServerGetDatabaseStorageSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "databaseBytes": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "reclaimableBytes": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "incrementalVacuumEnabled": { + "type": "boolean" + }, + "compactCommand": { + "type": "string" + } + }, + "required": [ + "databaseBytes", + "reclaimableBytes", + "incrementalVacuumEnabled", + "compactCommand" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "ServerGetHostResourcesSuccess": { + "type": "object", + "properties": { + "sampledAt": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "cpuUtilization": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "cpuCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "availableMemoryBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalMemoryBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "sampledAt", + "cpuUtilization", + "cpuCount", + "availableMemoryBytes", + "totalMemoryBytes" + ], + "additionalProperties": true + }, + "ServerGetNpmReleaseError": { + "anyOf": [ + { + "$ref": "#/$defs/ServerNpmReleaseErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerGetNpmReleasePayload": { + "type": "object", + "properties": { + "spec": { + "type": "string", + "maxLength": 64 + } + }, + "required": [ + "spec" + ], + "additionalProperties": true + }, + "ServerGetNpmReleaseSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "databaseMigration": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + } + }, + "required": [ + "version" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "ServerGetProcessDiagnosticsSuccess": { + "type": "object", + "properties": { + "serverPid": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "readAt": { + "type": "string" + }, + "processCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "processes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "pid": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "startTimeMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ppid": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "pgid": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + }, + "status": { + "type": "string" + }, + "cpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "rssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "elapsed": { + "type": "string" + }, + "command": { + "type": "string" + }, + "depth": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "childPids": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + } + }, + "required": [ + "pid", + "startTimeMs", + "ppid", + "pgid", + "status", + "cpuPercent", + "rssBytes", + "elapsed", + "command", + "depth", + "childPids" + ], + "additionalProperties": true + } + }, + "error": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": true + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "serverPid", + "readAt", + "processCount", + "totalRssBytes", + "totalCpuPercent", + "processes", + "error" + ], + "additionalProperties": true + }, + "ServerGetProcessResourceHistoryPayload": { + "type": "object", + "properties": { + "windowMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "bucketMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "windowMs", + "bucketMs" + ], + "additionalProperties": true + }, + "ServerGetProcessResourceHistorySuccess": { + "type": "object", + "properties": { + "readAt": { + "type": "string" + }, + "windowMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "bucketMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "sampleIntervalMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "retainedSampleCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalCpuSecondsApprox": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "buckets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "startedAt": { + "type": "string" + }, + "endedAt": { + "type": "string" + }, + "avgCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maxCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maxRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "maxProcessCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "startedAt", + "endedAt", + "avgCpuPercent", + "maxCpuPercent", + "maxRssBytes", + "maxProcessCount" + ], + "additionalProperties": true + } + }, + "topProcesses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "processKey": { + "type": "string" + }, + "pid": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "ppid": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "command": { + "type": "string" + }, + "depth": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "isServerRoot": { + "type": "boolean" + }, + "firstSeenAt": { + "type": "string" + }, + "lastSeenAt": { + "type": "string" + }, + "currentCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "avgCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maxCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cpuSecondsApprox": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "currentRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "maxRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "sampleCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "processKey", + "pid", + "ppid", + "command", + "depth", + "isServerRoot", + "firstSeenAt", + "lastSeenAt", + "currentCpuPercent", + "avgCpuPercent", + "maxCpuPercent", + "cpuSecondsApprox", + "currentRssBytes", + "maxRssBytes", + "sampleCount" + ], + "additionalProperties": true + } + }, + "error": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "type": "object", + "properties": { + "failureTag": { + "type": "string", + "enum": [ + "ProcessDiagnosticsQueryTimeoutError", + "ProcessDiagnosticsQueryFailedError", + "ProcessDiagnosticsServerProcessSignalError", + "ProcessDiagnosticsNotDescendantError", + "ProcessDiagnosticsSignalFailedError" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "failureTag", + "message" + ], + "additionalProperties": true + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "readAt", + "windowMs", + "bucketMs", + "sampleIntervalMs", + "retainedSampleCount", + "totalCpuSecondsApprox", + "buckets", + "topProcesses", + "error" + ], + "additionalProperties": true + }, + "ServerGetResourceTelemetryHistorySuccess": { + "type": "object", + "properties": { + "readAt": { + "type": "string" + }, + "windowMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "bucketMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "sampleIntervalMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "retainedSampleCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "buckets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "startedAt": { + "type": "string" + }, + "endedAt": { + "type": "string" + }, + "avgCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maxCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maxRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioReadBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioWriteBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "maxProcessCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "startedAt", + "endedAt", + "avgCpuPercent", + "maxCpuPercent", + "maxRssBytes", + "ioReadBytes", + "ioWriteBytes", + "maxProcessCount" + ], + "additionalProperties": true + } + }, + "topProcesses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/$defs/Identity2" + }, + "ppid": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "depth": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "name": { + "type": "string" + }, + "command": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "server", + "server-child", + "provider-root", + "terminal-root", + "electron-main", + "electron-renderer", + "electron-gpu", + "electron-utility", + "resource-monitor", + "unknown-t3" + ] + }, + "firstSeenAt": { + "type": "string" + }, + "lastSeenAt": { + "type": "string" + }, + "currentCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "avgCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maxCpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cpuTimeMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "currentRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "peakRssBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioReadBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioWriteBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioSemantics": { + "type": "string", + "enum": [ + "storage", + "logical", + "all-io", + "unavailable" + ] + }, + "sampleCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "identity", + "ppid", + "depth", + "name", + "command", + "category", + "firstSeenAt", + "lastSeenAt", + "currentCpuPercent", + "avgCpuPercent", + "maxCpuPercent", + "cpuTimeMs", + "currentRssBytes", + "peakRssBytes", + "ioReadBytes", + "ioWriteBytes", + "ioSemantics", + "sampleCount" + ], + "additionalProperties": true + } + }, + "health": { + "$ref": "#/$defs/Health" + } + }, + "required": [ + "readAt", + "windowMs", + "bucketMs", + "sampleIntervalMs", + "retainedSampleCount", + "buckets", + "topProcesses", + "health" + ], + "additionalProperties": true + }, + "ServerGetStorageCleanupReportSuccess": { + "anyOf": [ + { + "$ref": "#/$defs/Shape" + }, + { + "type": "null" + } + ] + }, + "ServerGetTraceDiagnosticsSuccess": { + "type": "object", + "properties": { + "traceFilePath": { + "type": "string" + }, + "scannedFilePaths": { + "type": "array", + "items": { + "type": "string" + } + }, + "readAt": { + "type": "string" + }, + "recordCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "parseErrorCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "firstSpanAt": { + "$ref": "#/$defs/LastError" + }, + "lastSpanAt": { + "$ref": "#/$defs/LastError" + }, + "failureCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "interruptionCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "slowSpanThresholdMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "slowSpanCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "logLevelCounts": { + "type": "object", + "additionalProperties": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "topSpansByCount": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "count": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "failureCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "totalDurationMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "averageDurationMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maxDurationMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "name", + "count", + "failureCount", + "totalDurationMs", + "averageDurationMs", + "maxDurationMs" + ], + "additionalProperties": true + } + }, + "slowestSpans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "durationMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "endedAt": { + "type": "string" + }, + "traceId": { + "type": "string" + }, + "spanId": { + "type": "string" + } + }, + "required": [ + "name", + "durationMs", + "endedAt", + "traceId", + "spanId" + ], + "additionalProperties": true + } + }, + "commonFailures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "cause": { + "type": "string" + }, + "count": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "lastSeenAt": { + "type": "string" + }, + "traceId": { + "type": "string" + }, + "spanId": { + "type": "string" + } + }, + "required": [ + "name", + "cause", + "count", + "lastSeenAt", + "traceId", + "spanId" + ], + "additionalProperties": true + } + }, + "latestFailures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "cause": { + "type": "string" + }, + "durationMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "endedAt": { + "type": "string" + }, + "traceId": { + "type": "string" + }, + "spanId": { + "type": "string" + } + }, + "required": [ + "name", + "cause", + "durationMs", + "endedAt", + "traceId", + "spanId" + ], + "additionalProperties": true + } + }, + "latestWarningAndErrorLogs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "spanName": { + "type": "string" + }, + "level": { + "type": "string" + }, + "message": { + "type": "string" + }, + "seenAt": { + "type": "string" + }, + "traceId": { + "type": "string" + }, + "spanId": { + "type": "string" + } + }, + "required": [ + "spanName", + "level", + "message", + "seenAt", + "traceId", + "spanId" + ], + "additionalProperties": true + } + }, + "partialFailure": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "type": "boolean" + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + }, + "error": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "trace-file-not-found", + "trace-file-read-failed" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "kind", + "message" + ], + "additionalProperties": true + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "traceFilePath", + "scannedFilePaths", + "readAt", + "recordCount", + "parseErrorCount", + "firstSpanAt", + "lastSpanAt", + "failureCount", + "interruptionCount", + "slowSpanThresholdMs", + "slowSpanCount", + "logLevelCounts", + "topSpansByCount", + "slowestSpans", + "commonFailures", + "latestFailures", + "latestWarningAndErrorLogs", + "partialFailure", + "error" + ], + "additionalProperties": true + }, + "ServerGetUsageSummaryError": { + "anyOf": [ + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + }, + { + "$ref": "#/$defs/UsageReadErrorEncoded" + } + ] + }, + "ServerGetUsageSummaryPayload": { + "type": "object", + "properties": { + "sinceDay": { + "type": "string" + }, + "untilDay": { + "type": "string" + }, + "timeZone": { + "type": "string" + }, + "resolution": { + "anyOf": [ + { + "type": "string", + "enum": [ + "day", + "hour" + ] + }, + { + "type": "null" + } + ] + }, + "sinceTime": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "untilTime": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "awaitRefresh": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "modelFilter": { + "anyOf": [ + { + "$ref": "#/$defs/ModelFilter" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "sinceDay", + "untilDay", + "timeZone" + ], + "additionalProperties": true + }, + "ServerGetUsageSummarySuccess": { + "type": "object", + "properties": { + "contractVersion": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "readAt": { + "type": "string" + }, + "timeZone": { + "type": "string" + }, + "sinceDay": { + "type": "string" + }, + "untilDay": { + "type": "string" + }, + "buckets": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "hourStart": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "provider": { + "type": "string", + "enum": [ + "claude", + "codex", + "grok", + "cursor", + "opencode", + "antigravity", + "pi", + "muse" + ] + }, + "model": { + "type": "string" + }, + "sourcePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "totals": { + "type": "object", + "properties": { + "uncachedInputTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "cachedInputTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "cacheCreationTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "outputTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "reasoningTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "uncachedInputTokens", + "cachedInputTokens", + "cacheCreationTokens", + "outputTokens", + "reasoningTokens" + ], + "additionalProperties": true + }, + "costUsd": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cacheSavingsUsd": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "categoryCostUsd": { + "anyOf": [ + { + "type": "object", + "properties": { + "input": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cacheRead": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cacheWrite": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "output": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "input", + "cacheRead", + "cacheWrite", + "output" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "fastCostUsd": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "ultrafastCostUsd": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "speedPremiumUsd": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "costSource": { + "type": "string", + "enum": [ + "providerReported", + "modelPriced", + "unpriced" + ] + }, + "records": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "unpricedRecords": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "sessions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "day", + "provider", + "model", + "totals", + "costUsd", + "cacheSavingsUsd", + "costSource", + "records", + "unpricedRecords", + "sessions" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "sources": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "fingerprint": { + "type": "object", + "properties": { + "hostId": { + "type": "string" + }, + "provider": { + "type": "string", + "enum": [ + "claude", + "codex", + "grok", + "cursor", + "opencode", + "antigravity", + "pi", + "muse" + ] + }, + "resolvedHomePath": { + "type": "string" + }, + "volumeId": { + "type": "string" + } + }, + "required": [ + "hostId", + "provider", + "resolvedHomePath", + "volumeId" + ], + "additionalProperties": true + }, + "status": { + "type": "string", + "enum": [ + "ok", + "missing", + "partial", + "failed" + ] + }, + "scannedFiles": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "skippedFiles": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "malformedRecords": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "distinctSessions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "action": { + "type": "string", + "enum": [ + "enableCursorKeychain" + ] + }, + "refreshing": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "fingerprint", + "status", + "scannedFiles", + "skippedFiles", + "malformedRecords", + "distinctSessions", + "message" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "pricing": { + "$ref": "#/$defs/Pricing" + }, + "scanDurationMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "modelFilter": { + "anyOf": [ + { + "$ref": "#/$defs/ModelFilter" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "contractVersion", + "readAt", + "timeZone", + "sinceDay", + "untilDay", + "buckets", + "sources", + "pricing", + "scanDurationMs" + ], + "additionalProperties": true + }, + "ServerImportAcpRegistrySessionPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "sessionId": { + "type": "string" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "instanceId", + "projectId", + "sessionId" + ], + "additionalProperties": true + }, + "ServerImportAcpRegistrySessionSuccess": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "imported": { + "type": "boolean" + } + }, + "required": [ + "threadId", + "imported" + ], + "additionalProperties": true + }, + "ServerListAcpRegistryProvidersPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "projectId": { + "type": "string" + } + }, + "required": [ + "instanceId", + "projectId" + ], + "additionalProperties": true + }, + "ServerListAcpRegistryProvidersSuccess": { + "type": "object", + "properties": { + "providers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "providerId": { + "type": "string" + }, + "supported": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 16 + }, + "required": { + "type": "boolean" + }, + "current": { + "anyOf": [ + { + "type": "object", + "properties": { + "apiType": { + "type": "string" + }, + "baseUrl": { + "type": "string", + "maxLength": 2048 + } + }, + "required": [ + "apiType", + "baseUrl" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "providerId", + "supported", + "required", + "current" + ], + "additionalProperties": true + }, + "maxItems": 64 + } + }, + "required": [ + "providers" + ], + "additionalProperties": true + }, + "ServerListAcpRegistrySessionsPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "cursor": { + "type": "string", + "maxLength": 2048 + } + }, + "required": [ + "instanceId", + "projectId" + ], + "additionalProperties": true + }, + "ServerListAcpRegistrySessionsSuccess": { + "type": "object", + "properties": { + "sessions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sessionId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "additionalDirectories": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 32 + }, + "title": { + "anyOf": [ + { + "type": "string", + "maxLength": 1024 + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "anyOf": [ + { + "type": "string", + "maxLength": 128 + }, + { + "type": "null" + } + ] + }, + "importedThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "sessionId", + "cwd", + "additionalDirectories", + "title", + "updatedAt", + "importedThreadId" + ], + "additionalProperties": true + }, + "maxItems": 256 + }, + "nextCursor": { + "anyOf": [ + { + "type": "string", + "maxLength": 2048 + }, + { + "type": "null" + } + ] + }, + "canLoad": { + "type": "boolean" + }, + "canResume": { + "type": "boolean" + }, + "canDelete": { + "type": "boolean" + } + }, + "required": [ + "sessions", + "nextCursor", + "canLoad", + "canResume", + "canDelete" + ], + "additionalProperties": true + }, + "ServerListOnePasswordAccountsSuccess": { + "type": "array", + "items": { + "type": "object", + "properties": { + "account": { + "type": "string" + }, + "email": { + "type": "string" + } + }, + "required": [ + "account", + "email" + ], + "additionalProperties": true + } + }, + "ServerLogoutAcpRegistrySuccess": { + "type": "object", + "properties": { + "loggedOut": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "loggedOut" + ], + "additionalProperties": true + }, + "ServerNpmReleaseErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ServerNpmReleaseError" + ] + }, + "reason": { + "type": "string" + } + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "ServerPrepareAcpRegistryAgentPayload": { + "type": "object", + "properties": { + "agentId": { + "type": "string" + } + }, + "required": [ + "agentId" + ], + "additionalProperties": true + }, + "ServerPrepareAcpRegistryAgentSuccess": { + "type": "object", + "properties": { + "agentId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "distribution": { + "type": "string", + "enum": [ + "binary", + "npx", + "uvx" + ] + }, + "prepared": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "agentId", + "version", + "distribution", + "prepared" + ], + "additionalProperties": true + }, + "ServerPrepareTriageError": { + "anyOf": [ + { + "$ref": "#/$defs/ServerPrepareTriageErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerPrepareTriageErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ServerPrepareTriageError" + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + }, + "ServerPrepareTriagePayload": { + "type": "object", + "properties": { + "client": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "deviceType": { + "type": "string", + "enum": [ + "desktop", + "mobile", + "tablet", + "bot", + "unknown" + ] + }, + "os": { + "type": "string" + }, + "osMajorVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "deviceModel": { + "type": "string" + }, + "surface": { + "type": "string", + "enum": [ + "web", + "desktop", + "mobile", + "cli" + ] + }, + "webDeployment": { + "type": "string", + "enum": [ + "hosted", + "server" + ] + }, + "browser": { + "type": "string" + }, + "appVersion": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "required": [ + "client" + ], + "additionalProperties": true + }, + "ServerPrepareTriageSuccess": { + "type": "object", + "properties": { + "prompt": { + "type": "string" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": true + }, + "ServerProviderUpdateErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ServerProviderUpdateError" + ] + }, + "provider": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "provider", + "reason" + ], + "additionalProperties": true + }, + "ServerRefreshProvidersError": { + "anyOf": [ + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + }, + { + "$ref": "#/$defs/ProviderSetupErrorEncoded" + } + ] + }, + "ServerRefreshProvidersPayload": { + "type": "object", + "properties": { + "instanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "fresh": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "refreshModels": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "reloadSecrets": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "workspaceSnapshotsOnDemand": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "usageLimitsCommand": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "ServerRefreshProvidersSuccess": { + "type": "object", + "properties": { + "providers": { + "$ref": "#/$defs/Providers" + } + }, + "required": [ + "providers" + ], + "additionalProperties": true + }, + "ServerRemoveKeybindingError": { + "anyOf": [ + { + "$ref": "#/$defs/KeybindingsConfigParseErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerRemoveKeybindingPayload": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "command": { + "$ref": "#/$defs/Command" + }, + "when": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "key", + "command" + ], + "additionalProperties": true + }, + "ServerRemoveKeybindingSuccess": { + "type": "object", + "properties": { + "keybindings": { + "$ref": "#/$defs/Keybindings" + }, + "issues": { + "$ref": "#/$defs/Issues" + } + }, + "required": [ + "keybindings", + "issues" + ], + "additionalProperties": true + }, + "ServerReportClientActivityPayload": { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "clientKind": { + "type": "string", + "enum": [ + "web", + "desktop-renderer", + "mobile", + "unknown" + ] + }, + "visible": { + "type": "boolean" + }, + "focused": { + "type": "boolean" + }, + "recentlyInteracted": { + "type": "boolean" + }, + "appState": { + "type": "string", + "enum": [ + "active", + "inactive", + "background", + "unknown" + ] + }, + "lowPowerMode": { + "type": "string", + "enum": [ + "true", + "false", + "unknown" + ] + }, + "scopes": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "server-config" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "provider-status" + ] + }, + "instanceId": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "vcs-status" + ] + }, + "cwd": { + "type": "string" + } + }, + "required": [ + "type", + "cwd" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "git-refs" + ] + }, + "cwd": { + "type": "string" + } + }, + "required": [ + "type", + "cwd" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "diagnostics" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "thread" + ] + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "type", + "threadId" + ], + "additionalProperties": true + } + ] + } + }, + "ttlMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "observedAt": { + "type": "string" + } + }, + "required": [ + "clientId", + "clientKind", + "visible", + "focused", + "recentlyInteracted", + "scopes", + "observedAt" + ], + "additionalProperties": true + }, + "ServerRetryResourceTelemetrySuccess": { + "type": "object", + "properties": { + "accepted": { + "type": "boolean" + }, + "snapshot": { + "$ref": "#/$defs/Snapshot2" + } + }, + "required": [ + "accepted", + "snapshot" + ], + "additionalProperties": true + }, + "ServerRunStorageCleanupError": { + "anyOf": [ + { + "$ref": "#/$defs/ServerSettingsErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerSearchAcpRegistryPayload": { + "type": "object", + "properties": { + "query": { + "type": "string" + }, + "limit": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 512 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "query" + ], + "additionalProperties": true + }, + "ServerSearchAcpRegistrySuccess": { + "type": "object", + "properties": { + "agents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string", + "maxLength": 1024 + }, + "authors": { + "type": "array", + "items": { + "type": "string", + "maxLength": 256 + }, + "maxItems": 16 + }, + "license": { + "anyOf": [ + { + "type": "string", + "maxLength": 128 + }, + { + "type": "null" + } + ] + }, + "website": { + "anyOf": [ + { + "type": "string", + "maxLength": 2048 + }, + { + "type": "null" + } + ] + }, + "repository": { + "anyOf": [ + { + "type": "string", + "maxLength": 2048 + }, + { + "type": "null" + } + ] + }, + "icon": { + "anyOf": [ + { + "type": "string", + "maxLength": 2048 + }, + { + "type": "null" + } + ] + }, + "distribution": { + "type": "string", + "enum": [ + "binary", + "npx", + "uvx" + ] + }, + "integrity": { + "type": "string", + "enum": [ + "sha256", + "registry" + ] + } + }, + "required": [ + "id", + "name", + "version", + "description", + "authors", + "license", + "website", + "repository", + "icon", + "distribution", + "integrity" + ], + "additionalProperties": true + }, + "maxItems": 512 + } + }, + "required": [ + "agents" + ], + "additionalProperties": true + }, + "ServerSelfUpdateErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ServerSelfUpdateError" + ] + }, + "reason": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "reason" + ], + "additionalProperties": true + }, + "ServerSetAcpRegistryProviderPayload": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "providerId": { + "type": "string" + }, + "apiType": { + "type": "string" + }, + "baseUrl": { + "type": "string", + "maxLength": 2048 + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string", + "maxLength": 8192 + } + } + }, + "required": [ + "instanceId", + "projectId", + "providerId", + "apiType", + "baseUrl" + ], + "additionalProperties": true + }, + "ServerSetAcpRegistryProviderSuccess": { + "type": "object", + "properties": { + "configured": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "configured" + ], + "additionalProperties": true + }, + "ServerSettingsErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ServerSettingsError" + ] + }, + "settingsPath": { + "type": "string" + }, + "operation": { + "type": "string", + "enum": [ + "normalize", + "check-exists", + "create-provider-instance", + "read-file", + "read-provider-history", + "read-project-settings", + "read-secret", + "remove-secret", + "remove-stale-secret", + "write-secret", + "write-secret-provider", + "write-file", + "prepare-directory", + "write-managed-setting", + "use-disallowed-provider" + ] + }, + "providerInstanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "environmentVariable": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "managedSetting": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "managedBy": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "settingsPath", + "operation" + ], + "additionalProperties": true + }, + "ServerSignalProcessPayload": { + "type": "object", + "properties": { + "pid": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "startTimeMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "signal": { + "type": "string", + "enum": [ + "SIGINT", + "SIGKILL" + ] + } + }, + "required": [ + "pid", + "startTimeMs", + "signal" + ], + "additionalProperties": true + }, + "ServerSignalProcessSuccess": { + "type": "object", + "properties": { + "pid": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "signal": { + "type": "string", + "enum": [ + "SIGINT", + "SIGKILL" + ] + }, + "signaled": { + "type": "boolean" + }, + "message": { + "$ref": "#/$defs/LastError" + } + }, + "required": [ + "pid", + "signal", + "signaled", + "message" + ], + "additionalProperties": true + }, + "ServerUninstallAcpRegistryManagedBinarySuccess": { + "type": "object", + "properties": { + "agentId": { + "type": "string" + }, + "removed": { + "type": "boolean" + } + }, + "required": [ + "agentId", + "removed" + ], + "additionalProperties": true + }, + "ServerUpdateProviderError": { + "anyOf": [ + { + "$ref": "#/$defs/ServerProviderUpdateErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ServerUpdateProviderPayload": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "targetVersion": { + "type": "string" + }, + "instanceId": { + "type": "string" + } + }, + "required": [ + "provider" + ], + "additionalProperties": true + }, + "ServerUpdateServerPayload": { + "type": "object", + "properties": { + "targetVersion": { + "type": "string" + }, + "channel": { + "type": "string", + "enum": [ + "latest", + "nightly" + ] + }, + "targetDatabaseMigration": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + } + }, + "required": [ + "targetVersion" + ], + "additionalProperties": true + }, + "ServerUpdateServerWithProgressSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "progress" + ] + }, + "stage": { + "type": "string", + "enum": [ + "downloading", + "installing" + ] + } + }, + "required": [ + "type", + "stage" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "complete" + ] + }, + "result": { + "$ref": "#/$defs/ServerCommitDesktopUpdateSuccess" + } + }, + "required": [ + "type", + "result" + ], + "additionalProperties": true + } + ] + }, + "ServerUpdateSettingsPayload": { + "type": "object", + "properties": { + "patch": { + "type": "object", + "properties": { + "setModelFavorites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "model": { + "type": "string" + }, + "favorite": { + "type": "boolean" + } + }, + "required": [ + "provider", + "model", + "favorite" + ], + "additionalProperties": true + } + }, + "setModelsHidden": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "model": { + "type": "string" + }, + "hidden": { + "type": "boolean" + } + }, + "required": [ + "provider", + "model", + "hidden" + ], + "additionalProperties": true + } + }, + "setProviderModelOrder": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "modelOrder": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "provider", + "modelOrder" + ], + "additionalProperties": true + }, + "favorites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "model": { + "type": "string" + } + }, + "required": [ + "provider", + "model" + ], + "additionalProperties": true + } + }, + "providerModelPreferences": { + "$ref": "#/$defs/ProviderModelPreferences" + }, + "migrateModelPreferences": { + "type": "object", + "properties": { + "favorites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "model": { + "type": "string" + } + }, + "required": [ + "provider", + "model" + ], + "additionalProperties": true + } + }, + "providerModelPreferences": { + "$ref": "#/$defs/ProviderModelPreferences" + } + }, + "additionalProperties": true + }, + "worktreeCleanup": { + "anyOf": [ + { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "off" + ] + } + }, + "required": [ + "mode" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "custom" + ] + }, + "rules": { + "type": "object", + "properties": { + "worktreeKeepWhen": { + "type": "string", + "enum": [ + "any-local-files", + "uncommitted-changes", + "tracked-changes" + ] + }, + "worktreeAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeSettledAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeOnMerge": { + "type": "boolean" + }, + "worktreeOnDelete": { + "type": "boolean" + }, + "worktreeUnchanged": { + "type": "boolean" + } + }, + "additionalProperties": true + } + }, + "required": [ + "mode", + "rules" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "storageCleanup": { + "type": "object", + "properties": { + "worktreeKeepWhen": { + "type": "string", + "enum": [ + "any-local-files", + "uncommitted-changes", + "tracked-changes" + ] + }, + "worktreeAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeSettledAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeOnMerge": { + "type": "boolean" + }, + "worktreeOnDelete": { + "type": "boolean" + }, + "worktreeUnchanged": { + "type": "boolean" + }, + "browserArtifactsAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "logsAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "worktreesDirectory": { + "type": "string" + }, + "responseStreamingMode": { + "type": "string", + "enum": [ + "turn", + "paragraph" + ] + }, + "enableProviderUpdateChecks": { + "type": "boolean" + }, + "continueThreadsAfterServerUpdate": { + "type": "boolean" + }, + "enableAgentBrowserAccess": { + "type": "boolean" + }, + "projectAgentBrowserAccessOverrides": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "defaultAutoPull": { + "type": "boolean" + }, + "defaultProjectScripts": { + "$ref": "#/$defs/DefaultProjectScripts" + }, + "projectScriptOverrides": { + "$ref": "#/$defs/ProjectScriptOverrides" + }, + "projectAutoPullOverrides": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "defaultRuntimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "fallbackModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "projectSettingsOverrides": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "properties": { + "worktreeCleanup": { + "anyOf": [ + { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "off" + ] + } + }, + "required": [ + "mode" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/WorktreeCleanup" + }, + { + "type": "null" + } + ] + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "defaultRuntimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "defaultThreadEnvMode": { + "type": "string", + "enum": [ + "local", + "worktree" + ] + }, + "newWorktreesStartFromOrigin": { + "type": "boolean" + }, + "worktreeSubmodules": {}, + "defaultAutoPull": { + "type": "boolean" + }, + "defaultProjectScripts": { + "$ref": "#/$defs/DefaultProjectScripts" + }, + "enableAgentBrowserAccess": { + "type": "boolean" + }, + "enableAgentDeviceAccess": { + "type": "boolean" + }, + "textGenerationModelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "threadTitleInstructions": { + "type": "string" + }, + "sourceControlWriterModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuideModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuideInstructions": { + "type": "string" + }, + "sourceControlWritingStyle": { + "$ref": "#/$defs/SourceControlWritingStyle2" + }, + "removeAgentCreditsOnMerge": { + "type": "boolean" + }, + "branchNamingMode": { + "type": "string", + "enum": [ + "static", + "semantic", + "custom" + ] + }, + "branchNamePrefix": { + "type": "string" + }, + "branchNameInstructions": { + "type": "string" + }, + "pullRequestMergeMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "squash", + "rebase" + ] + }, + { + "type": "null" + } + ] + }, + "sidebarAutoSettleOnMerge": { + "type": "boolean" + }, + "sidebarAutoSettleAfterDays": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "sidebarAutoSettleKeepPullRequests": { + "type": "boolean" + }, + "continueThreadsAfterServerUpdate": { + "type": "boolean" + }, + "responseStreamingMode": { + "type": "string", + "enum": [ + "turn", + "paragraph" + ] + }, + "providerInstances": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": true + } + } + }, + "allOf": [ + { + "type": "object", + "additionalProperties": {} + } + ] + }, + { + "type": "null" + } + ] + } + }, + "enableAgentDeviceAccess": { + "type": "boolean" + }, + "enableDeviceSupport": { + "type": "boolean" + }, + "deviceOnboardingCompleted": { + "type": "boolean" + }, + "deviceHosts": { + "$ref": "#/$defs/DeviceHosts" + }, + "easDeviceHosts": { + "$ref": "#/$defs/EasDeviceHosts" + }, + "simlockDeviceHosts": { + "$ref": "#/$defs/SimlockDeviceHosts" + }, + "simlockDeviceHostsEnabled": { + "type": "boolean" + }, + "sidebarAutoSettleAfterDays": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "compactIdleThreads": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string", + "enum": [ + "onReturn", + "beforeCacheExpires", + "off" + ] + } + ] + }, + "idleCompaction": { + "type": "string", + "enum": [ + "onReturn", + "beforeCacheExpires", + "off" + ] + }, + "sidebarAutoSettleKeepPullRequests": { + "type": "boolean" + }, + "sidebarAutoSettleOnMerge": { + "type": "boolean" + }, + "autoResumeLimitedThreads": { + "type": "boolean" + }, + "snoozeLimitedThreads": { + "type": "boolean" + }, + "snoozePresets": { + "$ref": "#/$defs/SnoozePresets" + }, + "hiddenThreadTags": { + "type": "array", + "items": { + "type": "string" + } + }, + "hideTaggedThreads": { + "type": "boolean" + }, + "guestSharingEnabled": { + "type": "boolean" + }, + "linkedEnvironmentsEnabled": { + "type": "boolean" + }, + "backgroundActivity": { + "type": "object", + "properties": { + "schemaVersion": { + "type": "number", + "enum": [ + 1 + ] + }, + "profile": { + "type": "string", + "enum": [ + "balanced", + "performance", + "battery-saver", + "custom" + ] + }, + "baseProfile": { + "type": "string", + "enum": [ + "balanced", + "performance", + "battery-saver" + ] + }, + "overrides": { + "$ref": "#/$defs/Overrides" + } + }, + "additionalProperties": true + }, + "automaticGitFetchInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "providerHealthRefreshInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "backgroundActivityProfile": { + "type": "string", + "enum": [ + "balanced", + "performance", + "battery-saver" + ] + }, + "environmentIcon": { + "anyOf": [ + { + "type": "string", + "enum": [ + "server", + "cloud", + "linux", + "desktop", + "laptop", + "mac-mini", + "mac-studio" + ] + }, + { + "type": "null" + } + ] + }, + "defaultThreadEnvMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "local", + "worktree" + ] + }, + { + "type": "null" + } + ] + }, + "newWorktreesStartFromOrigin": { + "type": "boolean" + }, + "worktreeSubmodules": { + "anyOf": [ + { + "type": "string", + "enum": [ + "recursive", + "top-level", + "none" + ] + }, + { + "type": "null" + } + ] + }, + "addProjectBaseDirectory": { + "type": "string" + }, + "textGenerationModelSelection": { + "type": "object", + "properties": { + "instanceId": { + "type": "string" + }, + "model": { + "type": "string" + }, + "options": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "id", + "value" + ], + "additionalProperties": true + } + }, + { + "type": "object" + } + ] + } + }, + "additionalProperties": true + }, + "threadTitleInstructions": { + "type": "string" + }, + "branchNamingMode": { + "type": "string", + "enum": [ + "static", + "semantic", + "custom" + ] + }, + "branchNamePrefix": { + "type": "string" + }, + "branchNameInstructions": { + "type": "string" + }, + "removeAgentCreditsOnMerge": { + "type": "boolean" + }, + "sourceControlWritingStyle": { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "repo_conventions", + "conventional_commits", + "custom" + ] + }, + "customInstructions": { + "type": "string" + }, + "followChangeRequestTemplates": { + "type": "boolean" + } + }, + "additionalProperties": true + }, + "sourceControlWriterModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuidesEnabled": { + "type": "boolean" + }, + "reviewGuideModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuideInstructions": { + "type": "string" + }, + "pullRequestMergeMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "squash", + "rebase" + ] + }, + { + "type": "null" + } + ] + }, + "observability": { + "type": "object", + "properties": { + "otlpTracesUrl": { + "type": "string" + }, + "otlpMetricsUrl": { + "type": "string" + }, + "otlpLogsUrl": { + "type": "string" + } + }, + "additionalProperties": true + }, + "sourceControlHosts": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "providerInstances": { + "$ref": "#/$defs/ProviderInstances" + }, + "usageLimitSources": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/$defs/UsageLimitSources2" + }, + { + "type": "null" + } + ] + } + }, + "secretProviders": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "label": { + "type": "string" + }, + "config": {} + }, + "required": [ + "kind", + "label" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "cursorKeychainUsageEnabled": { + "type": "boolean" + }, + "usagePriceOverrides": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/$defs/UsagePriceOverrides2" + }, + { + "type": "null" + } + ] + } + }, + "usageModelAliases": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + "additionalProperties": true + }, + "providerInstanceMutation": { + "anyOf": [ + { + "type": "object", + "properties": { + "operation": { + "type": "string", + "enum": [ + "create" + ] + }, + "instanceId": { + "type": "string" + }, + "instance": { + "$ref": "#/$defs/ProviderInstances2" + } + }, + "required": [ + "operation", + "instanceId", + "instance" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "operation": { + "type": "string", + "enum": [ + "upsert" + ] + }, + "instanceId": { + "type": "string" + }, + "instance": { + "$ref": "#/$defs/ProviderInstances2" + } + }, + "required": [ + "operation", + "instanceId", + "instance" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "operation": { + "type": "string", + "enum": [ + "remove" + ] + }, + "instanceId": { + "type": "string" + } + }, + "required": [ + "operation", + "instanceId" + ], + "additionalProperties": true + } + ] + } + }, + "required": [ + "patch" + ], + "additionalProperties": true + }, + "ServerUpsertKeybindingPayload": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "command": { + "$ref": "#/$defs/Command" + }, + "when": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "replace": { + "anyOf": [ + { + "$ref": "#/$defs/ServerRemoveKeybindingPayload" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "key", + "command" + ], + "additionalProperties": true + }, + "SessionsEntry": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "hostId": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "ios", + "android" + ] + }, + "openedAt": { + "type": "string" + } + }, + "required": [ + "threadId", + "hostId", + "deviceId", + "platform", + "openedAt" + ], + "additionalProperties": true + }, + "Settings2": { + "type": "object", + "properties": { + "favorites": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "model": { + "type": "string" + } + }, + "required": [ + "provider", + "model" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + }, + "providerModelPreferences": { + "anyOf": [ + { + "$ref": "#/$defs/ProviderModelPreferences" + }, + { + "type": "null" + } + ] + }, + "worktreeCleanup": { + "anyOf": [ + { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "off" + ] + } + }, + "required": [ + "mode" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/WorktreeCleanup" + }, + { + "type": "null" + } + ] + }, + "storageCleanup": { + "anyOf": [ + { + "type": "object", + "properties": { + "worktreeKeepWhen": { + "anyOf": [ + { + "type": "string", + "enum": [ + "any-local-files", + "uncommitted-changes", + "tracked-changes" + ] + }, + { + "type": "null" + } + ] + }, + "worktreeAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeSettledAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeOnMerge": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "worktreeOnDelete": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "worktreeUnchanged": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "browserArtifactsAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "logsAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "worktreesDirectory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "previousWorktreesDirectories": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "responseStreamingMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "turn", + "paragraph" + ] + }, + { + "type": "null" + } + ] + }, + "enableProviderUpdateChecks": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "continueThreadsAfterServerUpdate": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "enableAgentBrowserAccess": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "projectAgentBrowserAccessOverrides": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + { + "type": "null" + } + ] + }, + "defaultAutoPull": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "defaultProjectScripts": { + "anyOf": [ + { + "$ref": "#/$defs/DefaultProjectScripts" + }, + { + "type": "null" + } + ] + }, + "projectScriptOverrides": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectScriptOverrides" + }, + { + "type": "null" + } + ] + }, + "projectAutoPullOverrides": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + { + "type": "null" + } + ] + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "fallbackModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "defaultRuntimeMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + { + "type": "null" + } + ] + }, + "projectSettingsOverrides": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "worktreeCleanup": { + "anyOf": [ + { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "off" + ] + } + }, + "required": [ + "mode" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/WorktreeCleanup" + }, + { + "type": "null" + } + ] + }, + "defaultModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "defaultRuntimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "defaultThreadEnvMode": { + "type": "string", + "enum": [ + "local", + "worktree" + ] + }, + "newWorktreesStartFromOrigin": { + "type": "boolean" + }, + "worktreeSubmodules": {}, + "defaultAutoPull": { + "type": "boolean" + }, + "defaultProjectScripts": { + "$ref": "#/$defs/DefaultProjectScripts" + }, + "enableAgentBrowserAccess": { + "type": "boolean" + }, + "enableAgentDeviceAccess": { + "type": "boolean" + }, + "textGenerationModelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "threadTitleInstructions": { + "type": "string" + }, + "sourceControlWriterModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuideModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuideInstructions": { + "type": "string" + }, + "sourceControlWritingStyle": { + "anyOf": [ + { + "$ref": "#/$defs/SourceControlWritingStyle2" + }, + { + "type": "string" + } + ] + }, + "removeAgentCreditsOnMerge": { + "type": "boolean" + }, + "branchNamingMode": { + "type": "string", + "enum": [ + "static", + "semantic", + "custom" + ] + }, + "branchNamePrefix": { + "type": "string" + }, + "branchNameInstructions": { + "type": "string" + }, + "pullRequestMergeMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "squash", + "rebase" + ] + }, + { + "type": "null" + } + ] + }, + "sidebarAutoSettleOnMerge": { + "type": "boolean" + }, + "sidebarAutoSettleAfterDays": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "sidebarAutoSettleKeepPullRequests": { + "type": "boolean" + }, + "continueThreadsAfterServerUpdate": { + "type": "boolean" + }, + "responseStreamingMode": { + "type": "string", + "enum": [ + "turn", + "paragraph" + ] + }, + "providerInstances": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": true + } + } + }, + "allOf": [ + { + "type": "object", + "additionalProperties": {} + } + ] + } + }, + { + "type": "null" + } + ] + }, + "projectSettingsFolded": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "enableAgentDeviceAccess": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "enableDeviceSupport": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "deviceOnboardingCompleted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "deviceHosts": { + "anyOf": [ + { + "$ref": "#/$defs/DeviceHosts" + }, + { + "type": "null" + } + ] + }, + "easDeviceHosts": { + "anyOf": [ + { + "$ref": "#/$defs/EasDeviceHosts" + }, + { + "type": "null" + } + ] + }, + "simlockDeviceHosts": { + "anyOf": [ + { + "$ref": "#/$defs/SimlockDeviceHosts" + }, + { + "type": "null" + } + ] + }, + "simlockDeviceHostsEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "sidebarAutoSettleAfterDays": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "compactIdleThreads": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string", + "enum": [ + "onReturn", + "beforeCacheExpires", + "off" + ] + }, + { + "type": "null" + } + ] + }, + "idleCompaction": { + "type": "string", + "enum": [ + "onReturn", + "beforeCacheExpires", + "off" + ] + }, + "snoozeLimitedThreads": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "autoResumeLimitedThreads": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "snoozePresets": { + "anyOf": [ + { + "$ref": "#/$defs/SnoozePresets" + }, + { + "type": "null" + } + ] + }, + "hiddenThreadTags": { + "anyOf": [ + { + "type": "array" + }, + { + "type": "null" + } + ] + }, + "hideTaggedThreads": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "guestSharingEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "linkedEnvironmentsEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "sidebarAutoSettleKeepPullRequests": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "sidebarAutoSettleOnMerge": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "backgroundActivity": { + "anyOf": [ + { + "type": "object", + "properties": { + "schemaVersion": { + "anyOf": [ + { + "type": "number", + "enum": [ + 1 + ] + }, + { + "type": "null" + } + ] + }, + "profile": { + "anyOf": [ + { + "type": "string", + "enum": [ + "balanced", + "performance", + "battery-saver", + "custom" + ] + }, + { + "type": "null" + } + ] + }, + "baseProfile": { + "type": "string", + "enum": [ + "balanced", + "performance", + "battery-saver" + ] + }, + "overrides": { + "anyOf": [ + { + "$ref": "#/$defs/Overrides" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "automaticGitFetchInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "providerHealthRefreshInterval": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "backgroundActivityProfile": { + "anyOf": [ + { + "type": "string", + "enum": [ + "balanced", + "performance", + "battery-saver" + ] + }, + { + "type": "null" + } + ] + }, + "defaultTheme": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ] + }, + "defaultThemeSetAt": { + "anyOf": [ + { + "type": "string", + "maxLength": 64 + }, + { + "type": "null" + } + ] + }, + "environmentIcon": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "defaultThreadEnvMode": {}, + "newWorktreesStartFromOrigin": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "worktreeSubmodules": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "addProjectBaseDirectory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "textGenerationModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "threadTitleInstructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "branchNamingMode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "static", + "semantic", + "custom" + ] + }, + { + "type": "null" + } + ] + }, + "branchNamePrefix": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "branchNameInstructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "removeAgentCreditsOnMerge": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "sourceControlWritingStyle": { + "anyOf": [ + { + "$ref": "#/$defs/SourceControlWritingStyle2" + }, + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceControlWriterModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuidesEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "reviewGuideModelSelection": { + "anyOf": [ + { + "$ref": "#/$defs/ModelSelection" + }, + { + "type": "null" + } + ] + }, + "reviewGuideInstructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pullRequestMergeMethod": { + "anyOf": [ + { + "type": "string", + "enum": [ + "merge", + "squash", + "rebase" + ] + }, + { + "type": "null" + } + ] + }, + "providerInstances": { + "anyOf": [ + { + "$ref": "#/$defs/ProviderInstances" + }, + { + "type": "null" + } + ] + }, + "observability": { + "anyOf": [ + { + "type": "object", + "properties": { + "otlpTracesUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpMetricsUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "otlpLogsUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "sourceControlHosts": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + { + "type": "null" + } + ] + }, + "usageLimitSources": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/UsageLimitSources2" + } + }, + { + "type": "null" + } + ] + }, + "secretProviders": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "label": { + "type": "string" + }, + "config": {} + }, + "required": [ + "kind", + "label" + ], + "additionalProperties": true + } + }, + { + "type": "null" + } + ] + }, + "cursorKeychainUsageEnabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "usagePriceOverrides": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/UsagePriceOverrides2" + } + }, + { + "type": "null" + } + ] + }, + "usageModelAliases": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "usageModelFilter": {} + }, + "additionalProperties": true + }, + "Shape": { + "type": "object", + "properties": { + "trigger": { + "type": "string", + "enum": [ + "automatic", + "manual" + ] + }, + "startedAt": { + "type": "string" + }, + "finishedAt": { + "type": "string" + }, + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "worktree", + "browser-artifacts", + "logs" + ] + }, + "outcome": { + "type": "string", + "enum": [ + "removed", + "kept", + "failed" + ] + }, + "reason": { + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "projectPath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "threadTitle": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "bytes": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "files": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind", + "outcome", + "reason", + "path", + "threadId", + "threadTitle" + ], + "additionalProperties": true + } + }, + "counts": { + "type": "object", + "properties": { + "removed": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "kept": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "failed": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "removed", + "kept", + "failed" + ], + "additionalProperties": true + }, + "omittedCount": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "bytesFreed": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "trigger", + "startedAt", + "finishedAt", + "entries", + "counts", + "omittedCount" + ], + "additionalProperties": true + }, + "ShellOpenInEditorError": { + "anyOf": [ + { + "$ref": "#/$defs/ExternalLauncherUnknownEditorErrorEncoded" + }, + { + "$ref": "#/$defs/ExternalLauncherUnsupportedEditorErrorEncoded" + }, + { + "$ref": "#/$defs/ExternalLauncherCommandNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/ExternalLauncherUnsupportedTargetErrorEncoded" + }, + { + "$ref": "#/$defs/ExternalLauncherBrowserSpawnErrorEncoded" + }, + { + "$ref": "#/$defs/ExternalLauncherEditorSpawnErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "ShellOpenInEditorPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "editor": { + "$ref": "#/$defs/Editor" + }, + "reveal": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "editor" + ], + "additionalProperties": true + }, + "SimlockDeviceHosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "simlock" + ] + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "gatewayUrl": { + "type": "string" + }, + "agentToken": { + "type": "string" + }, + "maxLeases": { + "type": "integer", + "minimum": 1, + "maximum": 20 + }, + "maxLeaseMinutes": { + "type": "integer", + "minimum": 5, + "maximum": 240 + } + }, + "required": [ + "kind", + "id", + "label", + "gatewayUrl" + ], + "additionalProperties": true + } + }, + "SimlockHostsEntry": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "platforms": { + "$ref": "#/$defs/Platforms" + }, + "tools": { + "anyOf": [ + { + "$ref": "#/$defs/Tools" + }, + { + "type": "null" + } + ] + }, + "toolInspectionError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hubInstalled": { + "type": "boolean" + }, + "agentDeviceInstalled": { + "type": "boolean" + }, + "kind": { + "type": "string", + "enum": [ + "simlock" + ] + } + }, + "required": [ + "id", + "label", + "platforms", + "hubInstalled", + "agentDeviceInstalled", + "kind" + ], + "additionalProperties": true + }, + "Skills2": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "path": { + "type": "string" + }, + "scope": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enabled": { + "type": "boolean" + }, + "displayName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "shortDescription": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "userInvocationOnly": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "userInvocable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name", + "path", + "enabled" + ], + "additionalProperties": true + } + }, + "SlashCommands": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "input": { + "anyOf": [ + { + "type": "object", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name" + ], + "additionalProperties": true + } + }, + "Snapshot": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + }, + "title": { + "type": "string" + }, + "headBranch": { + "type": "string" + }, + "baseBranch": { + "type": "string" + }, + "isDraft": { + "type": "boolean" + }, + "updatedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "syncedAt": { + "type": "string" + }, + "closedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "mergedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "author": { + "anyOf": [ + { + "$ref": "#/$defs/Author" + }, + { + "type": "null" + } + ] + }, + "additions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "deletions": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "changedFiles": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reviewDecision": { + "anyOf": [ + { + "type": "string", + "enum": [ + "approved", + "changes-requested", + "review-required" + ] + }, + { + "type": "null" + } + ] + }, + "checksState": { + "anyOf": [ + { + "type": "string", + "enum": [ + "passing", + "failing", + "pending" + ] + }, + { + "type": "null" + } + ] + }, + "mergeability": { + "anyOf": [ + { + "type": "string", + "enum": [ + "mergeable", + "conflicting", + "unknown" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "state", + "title", + "headBranch", + "baseBranch", + "isDraft", + "updatedAt", + "syncedAt" + ], + "additionalProperties": true + }, + "Snapshot2": { + "type": "object", + "properties": { + "readAt": { + "type": "string" + }, + "sampleIntervalMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "processes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/$defs/Identity2" + }, + "ppid": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "childPids": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + "depth": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "name": { + "type": "string" + }, + "command": { + "type": "string" + }, + "status": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "server", + "server-child", + "provider-root", + "terminal-root", + "electron-main", + "electron-renderer", + "electron-gpu", + "electron-utility", + "resource-monitor", + "unknown-t3" + ] + }, + "electronType": { + "type": "string", + "enum": [ + "Browser", + "Tab", + "Utility", + "Zygote", + "Sandbox helper", + "GPU", + "Pepper Plugin", + "Pepper Plugin Broker", + "Unknown" + ] + }, + "electronServiceName": { + "type": "string" + }, + "cpuPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cpuTimeMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "residentBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "peakResidentBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "virtualBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioReadBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioWriteBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "ioReadBytesPerSecond": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "ioWriteBytesPerSecond": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "ioSemantics": { + "type": "string", + "enum": [ + "storage", + "logical", + "all-io", + "unavailable" + ] + }, + "idleWakeupsPerSecond": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "runTimeMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "firstSeenAt": { + "type": "string" + }, + "lastSeenAt": { + "type": "string" + } + }, + "required": [ + "identity", + "ppid", + "childPids", + "depth", + "name", + "command", + "status", + "category", + "cpuPercent", + "cpuTimeMs", + "residentBytes", + "peakResidentBytes", + "virtualBytes", + "ioReadBytes", + "ioWriteBytes", + "ioReadBytesPerSecond", + "ioWriteBytesPerSecond", + "ioSemantics", + "runTimeMs", + "firstSeenAt", + "lastSeenAt" + ], + "additionalProperties": true + } + }, + "groups": { + "type": "object", + "properties": { + "backend": { + "$ref": "#/$defs/AllT3" + }, + "electron": { + "$ref": "#/$defs/AllT3" + }, + "monitor": { + "$ref": "#/$defs/AllT3" + }, + "allT3": { + "$ref": "#/$defs/AllT3" + } + }, + "required": [ + "backend", + "electron", + "monitor", + "allT3" + ], + "additionalProperties": true + }, + "power": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": [ + "unknown", + "node-macos-shell", + "node-macos-native", + "node-linux", + "node-windows", + "electron-main" + ] + }, + "idle": { + "type": "string", + "enum": [ + "true", + "false", + "unknown" + ] + }, + "idleSeconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + }, + { + "type": "null" + } + ] + }, + "locked": { + "type": "string", + "enum": [ + "true", + "false", + "unknown" + ] + }, + "suspended": { + "type": "boolean" + }, + "onBattery": { + "type": "string", + "enum": [ + "true", + "false", + "unknown" + ] + }, + "lowPowerMode": { + "type": "string", + "enum": [ + "true", + "false", + "unknown" + ] + }, + "thermalState": { + "type": "string", + "enum": [ + "unknown", + "nominal", + "fair", + "serious", + "critical" + ] + }, + "stale": { + "type": "boolean" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "source", + "idle", + "idleSeconds", + "locked", + "suspended", + "onBattery", + "lowPowerMode", + "thermalState", + "stale", + "updatedAt" + ], + "additionalProperties": true + }, + "speedLimitPercent": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Some" + ] + }, + "value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "_tag", + "value" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "None" + ] + } + }, + "required": [ + "_tag" + ], + "additionalProperties": true + } + ] + }, + "attribution": { + "type": "object", + "properties": { + "readAt": { + "type": "string" + }, + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "component": { + "type": "string" + }, + "operation": { + "type": "string" + }, + "logicalReadBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "logicalWriteBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "count": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "durationMs": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "component", + "operation", + "logicalReadBytes", + "logicalWriteBytes", + "count", + "durationMs" + ], + "additionalProperties": true + } + } + }, + "required": [ + "readAt", + "entries" + ], + "additionalProperties": true + }, + "health": { + "$ref": "#/$defs/Health" + } + }, + "required": [ + "readAt", + "sampleIntervalMs", + "processes", + "groups", + "power", + "speedLimitPercent", + "attribution", + "health" + ], + "additionalProperties": true + }, + "Snapshot3": { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "cwd": { + "type": "string", + "minLength": 1 + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "starting", + "running", + "exited", + "error" + ] + }, + "pid": { + "anyOf": [ + { + "type": "integer", + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + { + "type": "null" + } + ] + }, + "history": { + "type": "string" + }, + "exitCode": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "exitSignal": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "label": { + "type": "string", + "maxLength": 128 + }, + "updatedAt": { + "type": "string" + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "cols": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 1000 + }, + { + "type": "null" + } + ] + }, + "rows": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "terminalId", + "cwd", + "worktreePath", + "status", + "pid", + "history", + "exitCode", + "exitSignal", + "label", + "updatedAt" + ], + "additionalProperties": true + }, + "SnoozePresets": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "after" + ] + }, + "minutes": { + "type": "integer", + "minimum": 1, + "maximum": 43200 + } + }, + "required": [ + "kind", + "minutes" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "at" + ] + }, + "day": { + "type": "string", + "enum": [ + "today", + "tomorrow", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" + ] + }, + "time": { + "type": "string" + } + }, + "required": [ + "kind", + "day", + "time" + ], + "additionalProperties": true + } + ] + }, + "maxItems": 8 + }, + "Source": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "snap-shot" + ] + }, + "capturedAt": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "windowTitle": { + "type": "string" + }, + "accessibleText": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "accessibility": { + "anyOf": [ + { + "type": "object", + "properties": { + "format": { + "type": "string", + "enum": [ + "flat-text" + ] + }, + "text": { + "type": "string" + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "format", + "text", + "truncated" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "format": { + "type": "string", + "enum": [ + "element-tree" + ] + }, + "coordinateSpace": { + "type": "string", + "enum": [ + "captured-image" + ] + }, + "imageSize": { + "$ref": "#/$defs/ImageSize" + }, + "truncated": { + "type": "boolean" + }, + "root": { + "$ref": "#/$defs/Objects_" + } + }, + "required": [ + "format", + "coordinateSpace", + "imageSize", + "truncated", + "root" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "appIdentifier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "appIconDataUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind", + "capturedAt", + "appName", + "windowTitle" + ], + "additionalProperties": true + }, + "Source2": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "delegated_task" + ] + }, + "taskIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "childThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind", + "taskIds" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "background_task" + ] + }, + "work": { + "type": "string", + "enum": [ + "subagent" + ] + }, + "childThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind", + "work" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "background_command" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "subagent" + ] + }, + "childThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "command" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "monitor" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "background_task" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "system" + ] + } + }, + "required": [ + "kind" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + } + ] + }, + "SourceControlAppAccessPayload": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "repository": { + "type": "string" + } + }, + "required": [ + "provider", + "host", + "repository" + ], + "additionalProperties": true + }, + "SourceControlAppAccessSuccess": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "covered", + "not-covered", + "signed-out" + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": true + }, + "SourceControlCloneRepositoryPayload": { + "type": "object", + "properties": { + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "repository": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "remoteUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "destinationPath": { + "type": "string" + }, + "protocol": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto", + "ssh", + "https" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "destinationPath" + ], + "additionalProperties": true + }, + "SourceControlCloneRepositorySuccess": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "remoteUrl": { + "type": "string" + }, + "repository": { + "anyOf": [ + { + "$ref": "#/$defs/Repository" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "remoteUrl", + "repository" + ], + "additionalProperties": true + }, + "SourceControlLookupRepositoryPayload": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "provider", + "repository" + ], + "additionalProperties": true + }, + "SourceControlProviderErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "SourceControlProviderError" + ] + }, + "provider": { + "type": "string" + }, + "operation": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "repository": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "reference": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "provider", + "operation", + "cwd", + "detail" + ], + "additionalProperties": true + }, + "SourceControlPublishRepositoryPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "visibility": { + "type": "string", + "enum": [ + "private", + "public" + ] + }, + "remoteName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "protocol": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto", + "ssh", + "https" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "provider", + "repository", + "visibility" + ], + "additionalProperties": true + }, + "SourceControlPublishRepositorySuccess": { + "type": "object", + "properties": { + "repository": { + "$ref": "#/$defs/Repository" + }, + "remoteName": { + "type": "string" + }, + "remoteUrl": { + "type": "string" + }, + "branch": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pushed", + "remote_added" + ] + } + }, + "required": [ + "repository", + "remoteName", + "remoteUrl", + "branch", + "status" + ], + "additionalProperties": true + }, + "SourceControlRepositoryErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "SourceControlRepositoryError" + ] + }, + "provider": { + "type": "string" + }, + "operation": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "provider", + "operation", + "detail" + ], + "additionalProperties": true + }, + "SourceControlSignInCancelError": { + "anyOf": [ + { + "$ref": "#/$defs/SourceControlSignInErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "SourceControlSignInCancelPayload": { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "host": { + "type": "string" + } + }, + "required": [ + "provider", + "host" + ], + "additionalProperties": true + }, + "SourceControlSignInErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "SourceControlSignInError" + ] + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "unsupported", + "not-started", + "cancelled", + "denied", + "expired", + "failed" + ] + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "provider", + "host", + "reason", + "detail" + ], + "additionalProperties": true + }, + "SourceControlSignInStartSuccess": { + "type": "object", + "properties": { + "userCode": { + "type": "string" + }, + "verificationUri": { + "type": "string" + }, + "expiresAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "userCode", + "verificationUri", + "expiresAt" + ], + "additionalProperties": true + }, + "SourceControlSignInWaitSuccess": { + "type": "object", + "properties": { + "account": { + "type": "string" + } + }, + "required": [ + "account" + ], + "additionalProperties": true + }, + "SourceControlStarSuccess": { + "type": "boolean" + }, + "SourceControlWritingStyle2": { + "type": "object", + "properties": { + "mode": { + "anyOf": [ + { + "type": "string", + "enum": [ + "repo_conventions", + "conventional_commits", + "custom" + ] + }, + { + "type": "null" + } + ] + }, + "customInstructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "followChangeRequestTemplates": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "SourcePoint": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "latest_stable" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "run" + ] + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "checkpoint" + ] + }, + "checkpointId": { + "type": "string" + } + }, + "required": [ + "type", + "checkpointId" + ], + "additionalProperties": true + } + ] + }, + "SourceTokenUsage": { + "type": "object", + "properties": { + "usedTokens": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "maxTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "inputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "cachedInputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "outputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reasoningOutputTokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "promptCacheTtlMs": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "autoCompactThreshold": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "promptCacheExpiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "usedTokens", + "updatedAt" + ], + "additionalProperties": true + }, + "Stack": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "native" + ] + }, + "id": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "url": { + "type": "string" + }, + "base": { + "type": "string" + }, + "layers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "headBranch": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "merged" + ] + } + }, + "required": [ + "number", + "headBranch", + "state" + ], + "additionalProperties": true + } + } + }, + "required": [ + "kind", + "id", + "number", + "url", + "base", + "layers" + ], + "additionalProperties": true + }, + "Stack2": { + "type": "object", + "properties": { + "number": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "position": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "size": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "base": { + "type": "string" + } + }, + "required": [ + "number", + "position", + "size", + "base" + ], + "additionalProperties": true + }, + "Steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "running", + "completed" + ] + }, + "durationAnchorAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "durationMs": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "text", + "status" + ], + "additionalProperties": true + } + }, + "SubscribeAuthAccessError": { + "anyOf": [ + { + "$ref": "#/$defs/AuthAccessStreamErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "SubscribeAuthAccessSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "revision": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "type": { + "type": "string", + "enum": [ + "snapshot" + ] + }, + "payload": { + "type": "object", + "properties": { + "pairingLinks": { + "type": "array", + "items": { + "$ref": "#/$defs/PairingLinksEntry" + } + }, + "clientSessions": { + "type": "array", + "items": { + "$ref": "#/$defs/ClientSessionsEntry" + } + } + }, + "required": [ + "pairingLinks", + "clientSessions" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "revision", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "revision": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "type": { + "type": "string", + "enum": [ + "pairingLinkUpserted" + ] + }, + "payload": { + "$ref": "#/$defs/PairingLinksEntry" + } + }, + "required": [ + "version", + "revision", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "revision": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "type": { + "type": "string", + "enum": [ + "pairingLinkRemoved" + ] + }, + "payload": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "revision", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "revision": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "type": { + "type": "string", + "enum": [ + "clientUpserted" + ] + }, + "payload": { + "$ref": "#/$defs/ClientSessionsEntry" + } + }, + "required": [ + "version", + "revision", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "revision": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "type": { + "type": "string", + "enum": [ + "clientRemoved" + ] + }, + "payload": { + "type": "object", + "properties": { + "sessionId": { + "type": "string" + } + }, + "required": [ + "sessionId" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "revision", + "type", + "payload" + ], + "additionalProperties": true + } + ] + }, + "SubscribeDiscoveredLocalServersPayload": { + "type": "object", + "properties": { + "configuredUrls": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 32 + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "SubscribeDiscoveredLocalServersSuccess": { + "type": "object", + "properties": { + "servers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "port": { + "type": "integer", + "exclusiveMinimum": 0, + "exclusiveMaximum": 65536 + }, + "url": { + "type": "string" + }, + "processName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pid": { + "anyOf": [ + { + "type": "integer", + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + { + "type": "null" + } + ] + }, + "terminal": { + "anyOf": [ + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + } + }, + "required": [ + "threadId", + "terminalId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "agent": { + "anyOf": [ + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "host", + "port", + "url", + "processName", + "pid", + "terminal" + ], + "additionalProperties": true + } + }, + "scannedAt": { + "type": "string" + }, + "configuredUrlProbing": { + "anyOf": [ + { + "type": "boolean", + "enum": [ + true + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "servers", + "scannedAt" + ], + "additionalProperties": true + }, + "SubscribePreviewEventsSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "serverEpoch": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "opened" + ] + }, + "snapshot": { + "$ref": "#/$defs/PreviewAdjustSuccess" + } + }, + "required": [ + "threadId", + "tabId", + "createdAt", + "serverEpoch", + "revision", + "type", + "snapshot" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "serverEpoch": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "navigated" + ] + }, + "snapshot": { + "$ref": "#/$defs/PreviewAdjustSuccess" + } + }, + "required": [ + "threadId", + "tabId", + "createdAt", + "serverEpoch", + "revision", + "type", + "snapshot" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "serverEpoch": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "resized" + ] + }, + "snapshot": { + "$ref": "#/$defs/PreviewAdjustSuccess" + }, + "request": { + "anyOf": [ + { + "type": "object", + "properties": { + "hardReload": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "clear": { + "anyOf": [ + { + "type": "string", + "enum": [ + "cookies", + "cache" + ] + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "tabId", + "createdAt", + "serverEpoch", + "revision", + "type", + "snapshot" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "serverEpoch": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "failed" + ] + }, + "url": { + "type": "string" + }, + "title": { + "type": "string", + "maxLength": 512 + }, + "code": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "description": { + "type": "string" + }, + "download": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "fileName": { + "type": "string" + } + }, + "required": [ + "id", + "fileName" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "tabId", + "createdAt", + "serverEpoch", + "revision", + "type", + "url", + "title", + "code", + "description" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "tabId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "serverEpoch": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "closed" + ] + } + }, + "required": [ + "threadId", + "tabId", + "createdAt", + "serverEpoch", + "revision", + "type" + ], + "additionalProperties": true + } + ] + }, + "SubscribeProjectClonesSuccess": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + }, + "remoteUrl": { + "type": "string" + }, + "destinationPath": { + "type": "string" + }, + "repository": { + "anyOf": [ + { + "$ref": "#/$defs/Repository" + }, + { + "type": "null" + } + ] + }, + "phase": { + "type": "string", + "enum": [ + "running", + "done", + "failed", + "cancelled" + ] + }, + "stage": { + "type": "string", + "enum": [ + "connecting", + "counting", + "receiving", + "resolving", + "checkout" + ] + }, + "percent": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + { + "type": "null" + } + ] + }, + "detail": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ] + }, + "stalled": { + "type": "boolean" + }, + "error": { + "anyOf": [ + { + "type": "string", + "maxLength": 1000 + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "type": "string" + }, + "endedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "projectId", + "remoteUrl", + "destinationPath", + "repository", + "phase", + "stage", + "percent", + "detail", + "error", + "startedAt", + "endedAt", + "sequence" + ], + "additionalProperties": true + } + }, + "SubscribeServerConfigPayload": { + "type": "object", + "properties": { + "environmentThemes": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "usageLimitSources": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "usageLimitsCommand": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "workspaceSnapshotsOnDemand": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "SubscribeServerConfigSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "type": { + "type": "string", + "enum": [ + "snapshot" + ] + }, + "config": { + "$ref": "#/$defs/ServerGetConfigSuccess" + } + }, + "required": [ + "version", + "type", + "config" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "type": { + "type": "string", + "enum": [ + "keybindingsUpdated" + ] + }, + "payload": { + "$ref": "#/$defs/ServerRemoveKeybindingSuccess" + } + }, + "required": [ + "version", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "type": { + "type": "string", + "enum": [ + "providerStatuses" + ] + }, + "payload": { + "$ref": "#/$defs/ServerRefreshProvidersSuccess" + } + }, + "required": [ + "version", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "type": { + "type": "string", + "enum": [ + "settingsUpdated" + ] + }, + "payload": { + "type": "object", + "properties": { + "settings": { + "$ref": "#/$defs/Settings2" + } + }, + "required": [ + "settings" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "type": { + "type": "string", + "enum": [ + "environmentThemesUpdated" + ] + }, + "payload": { + "type": "object", + "properties": { + "themes": { + "$ref": "#/$defs/EnvironmentThemes" + } + }, + "required": [ + "themes" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "type": { + "type": "string", + "enum": [ + "usageLimitSourcesUpdated" + ] + }, + "payload": { + "type": "object", + "properties": { + "sources": { + "$ref": "#/$defs/UsageLimitSources3" + } + }, + "required": [ + "sources" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "type", + "payload" + ], + "additionalProperties": true + } + ] + }, + "SubscribeServerLifecycleSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "welcome" + ] + }, + "payload": { + "type": "object", + "properties": { + "environment": { + "$ref": "#/$defs/Environment" + }, + "cwd": { + "type": "string" + }, + "projectName": { + "type": "string" + }, + "bootstrapStatus": { + "anyOf": [ + { + "type": "string", + "enum": [ + "pending", + "complete" + ] + }, + { + "type": "null" + } + ] + }, + "bootstrapProjectId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "bootstrapThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "bootstrapProjectCreated": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "bootstrapThreadCreated": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "environment", + "cwd", + "projectName" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "sequence", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "ready" + ] + }, + "payload": { + "type": "object", + "properties": { + "at": { + "type": "string" + }, + "environment": { + "$ref": "#/$defs/Environment" + }, + "updateOutcome": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "fromVersion": { + "type": "string" + }, + "targetVersion": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "committed", + "rolled-back", + "failed" + ] + }, + "reason": { + "type": "string" + } + }, + "required": [ + "id", + "fromVersion", + "targetVersion", + "status" + ], + "additionalProperties": true + } + }, + "required": [ + "at", + "environment" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "sequence", + "type", + "payload" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "type": { + "type": "string", + "enum": [ + "legacyThreadMigration" + ] + }, + "payload": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "running", + "complete" + ] + }, + "totalThreadCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "status", + "totalThreadCount" + ], + "additionalProperties": true + } + }, + "required": [ + "version", + "sequence", + "type", + "payload" + ], + "additionalProperties": true + } + ] + }, + "SubscribeTerminalMetadataSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "snapshot" + ] + }, + "terminals": { + "type": "array", + "items": { + "$ref": "#/$defs/Terminal" + } + } + }, + "required": [ + "type", + "terminals" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "upsert" + ] + }, + "terminal": { + "$ref": "#/$defs/Terminal" + } + }, + "required": [ + "type", + "terminal" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "remove" + ] + }, + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type", + "threadId", + "terminalId" + ], + "additionalProperties": true + } + ] + }, + "SubscribeVcsStatusPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "includeRemote": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "SubscribeVcsStatusSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "snapshot" + ] + }, + "local": { + "$ref": "#/$defs/Local" + }, + "remote": { + "anyOf": [ + { + "$ref": "#/$defs/Remote" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "local", + "remote" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "localUpdated" + ] + }, + "local": { + "$ref": "#/$defs/Local" + } + }, + "required": [ + "_tag", + "local" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "remoteUpdated" + ] + }, + "remote": { + "anyOf": [ + { + "$ref": "#/$defs/Remote" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "remote" + ], + "additionalProperties": true + } + ] + }, + "SubscribeWorktreeSetupSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "phase": { + "type": "string", + "enum": [ + "running", + "done", + "failed", + "cancelled" + ] + }, + "startedAt": { + "type": "string" + }, + "endedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "baseRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "setupScript": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "command": { + "type": "string" + }, + "terminalId": { + "type": "string" + } + }, + "required": [ + "name", + "command", + "terminalId" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "stages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "enum": [ + "fetch", + "checkout", + "submodules", + "setup-script", + "agent" + ] + }, + "status": { + "type": "string", + "enum": [ + "pending", + "running", + "done", + "skipped", + "warning", + "failed" + ] + }, + "startedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "endedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "percent": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + { + "type": "null" + } + ] + }, + "detail": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ] + }, + "tail": { + "type": "array", + "items": { + "type": "string", + "maxLength": 400 + } + } + }, + "required": [ + "id", + "status", + "startedAt", + "endedAt", + "percent", + "detail", + "tail" + ], + "additionalProperties": true + } + }, + "error": { + "anyOf": [ + { + "type": "string", + "maxLength": 1000 + }, + { + "type": "null" + } + ] + }, + "sequence": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "threadId", + "phase", + "startedAt", + "endedAt", + "branch", + "baseRef", + "worktreePath", + "setupScript", + "stages", + "error", + "sequence" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "Task": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "schedule": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "interval" + ] + }, + "everyMs": { + "type": "integer", + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + } + }, + "required": [ + "type", + "everyMs" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/Schedule3" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "once" + ] + }, + "runAt": { + "type": "string" + } + }, + "required": [ + "type", + "runAt" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "signature": { + "anyOf": [ + { + "type": "object", + "properties": { + "header": { + "type": "string" + }, + "encoding": { + "type": "string", + "enum": [ + "hex", + "base64" + ] + }, + "prefix": { + "type": "string", + "description": "Text before the digest, such as 'sha256='. Empty for none." + } + }, + "required": [ + "header", + "encoding", + "prefix" + ], + "additionalProperties": true, + "description": "HMAC-SHA256 check on the raw body." + }, + { + "type": "null" + } + ] + }, + "maxDeliveryAgeMinutes": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 1440, + "description": "Skip requests held offline over this many minutes. Omit to run all." + }, + { + "type": "null" + } + ] + }, + "dedupe": { + "anyOf": [ + { + "$ref": "#/$defs/Dedupe" + }, + { + "type": "null" + } + ] + }, + "filters": { + "anyOf": [ + { + "$ref": "#/$defs/Filters" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "signature" + ], + "additionalProperties": true, + "description": "Run on each webhook request." + } + ] + }, + "projectId": { + "type": "string" + }, + "threadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspaceStrategy": { + "$ref": "#/$defs/WorkspaceStrategy3" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + }, + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "nextRunAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastRunAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastRunStatus": { + "type": "string", + "enum": [ + "never", + "running", + "succeeded", + "failed" + ] + }, + "lastRunError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "runCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "webhook": { + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hasSecret": { + "type": "boolean" + } + }, + "required": [ + "path", + "url", + "hasSecret" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "blockedByPolicy": { + "type": "string" + } + }, + "required": [ + "id", + "title", + "prompt", + "enabled", + "schedule", + "projectId", + "threadId", + "workspaceStrategy", + "modelSelection", + "runtimeMode", + "interactionMode", + "createdBy", + "creationSource", + "createdAt", + "updatedAt", + "nextRunAt", + "lastRunAt", + "lastRunStatus", + "lastRunError", + "runCount" + ], + "additionalProperties": true + }, + "Terminal": { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "cwd": { + "type": "string", + "minLength": 1 + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "starting", + "running", + "exited", + "error" + ] + }, + "pid": { + "anyOf": [ + { + "type": "integer", + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + { + "type": "null" + } + ] + }, + "exitCode": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "exitSignal": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "hasRunningSubprocess": { + "type": "boolean" + }, + "label": { + "type": "string", + "maxLength": 128 + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "threadId", + "terminalId", + "cwd", + "worktreePath", + "status", + "pid", + "exitCode", + "exitSignal", + "hasRunningSubprocess", + "label", + "updatedAt" + ], + "additionalProperties": true + }, + "TerminalAttachError": { + "anyOf": [ + { + "$ref": "#/$defs/TerminalCwdNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalCwdNotDirectoryErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalCwdStatErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalHistoryErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalSessionLookupErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalProviderInstanceNotFoundErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalProviderEnvironmentErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalNotRunningErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalWriteErrorEncoded" + }, + { + "$ref": "#/$defs/TerminalResizeErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "TerminalAttachPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cols": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 1000 + }, + { + "type": "null" + } + ] + }, + "rows": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + { + "type": "null" + } + ] + }, + "env": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true, + "maxProperties": 128 + }, + { + "type": "null" + } + ] + }, + "providerInstanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "restartIfNotRunning": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "resume": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "terminalId" + ], + "additionalProperties": true + }, + "TerminalAttachSuccess": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "snapshot" + ] + }, + "snapshot": { + "$ref": "#/$defs/Snapshot3" + } + }, + "required": [ + "type", + "snapshot" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "output" + ] + }, + "data": { + "type": "string" + } + }, + "required": [ + "threadId", + "terminalId", + "type", + "data" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "exited" + ] + }, + "exitCode": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "exitSignal": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "terminalId", + "type", + "exitCode", + "exitSignal" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "closed" + ] + } + }, + "required": [ + "threadId", + "terminalId", + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "error" + ] + }, + "message": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "threadId", + "terminalId", + "type", + "message" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "cleared" + ] + } + }, + "required": [ + "threadId", + "terminalId", + "type" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "restarted" + ] + }, + "snapshot": { + "$ref": "#/$defs/Snapshot3" + } + }, + "required": [ + "threadId", + "terminalId", + "type", + "snapshot" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "threadId": { + "type": "string", + "minLength": 1 + }, + "terminalId": { + "type": "string", + "minLength": 1 + }, + "sequence": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "activity" + ] + }, + "hasRunningSubprocess": { + "type": "boolean" + }, + "label": { + "type": "string", + "maxLength": 128 + }, + "cols": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 1000 + }, + { + "type": "null" + } + ] + }, + "rows": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "terminalId", + "type", + "hasRunningSubprocess", + "label" + ], + "additionalProperties": true + } + ] + }, + "TerminalClearPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + } + }, + "required": [ + "threadId", + "terminalId" + ], + "additionalProperties": true + }, + "TerminalClosePayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "terminalId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "deleteHistory": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId" + ], + "additionalProperties": true + }, + "TerminalCwdNotDirectoryErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalCwdNotDirectoryError" + ] + }, + "cwd": { + "type": "string" + } + }, + "required": [ + "_tag", + "cwd" + ], + "additionalProperties": true + }, + "TerminalCwdNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalCwdNotFoundError" + ] + }, + "cwd": { + "type": "string" + } + }, + "required": [ + "_tag", + "cwd" + ], + "additionalProperties": true + }, + "TerminalCwdStatErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalCwdStatError" + ] + }, + "cwd": { + "type": "string" + }, + "cause": {} + }, + "required": [ + "_tag", + "cwd", + "cause" + ], + "additionalProperties": true + }, + "TerminalHistoryErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalHistoryError" + ] + }, + "operation": { + "type": "string", + "enum": [ + "read", + "truncate", + "migrate" + ] + }, + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "threadId", + "terminalId" + ], + "additionalProperties": true + }, + "TerminalNotRunningErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalNotRunningError" + ] + }, + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + } + }, + "required": [ + "_tag", + "threadId", + "terminalId" + ], + "additionalProperties": true + }, + "TerminalOpenPayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cols": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 1000 + }, + { + "type": "null" + } + ] + }, + "rows": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + { + "type": "null" + } + ] + }, + "env": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true, + "maxProperties": 128 + }, + { + "type": "null" + } + ] + }, + "providerInstanceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "threadId", + "terminalId", + "cwd" + ], + "additionalProperties": true + }, + "TerminalProviderEnvironmentErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalProviderEnvironmentError" + ] + }, + "providerInstanceId": { + "type": "string" + }, + "cause": {} + }, + "required": [ + "_tag", + "providerInstanceId", + "cause" + ], + "additionalProperties": true + }, + "TerminalProviderInstanceNotFoundErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalProviderInstanceNotFoundError" + ] + }, + "providerInstanceId": { + "type": "string" + } + }, + "required": [ + "_tag", + "providerInstanceId" + ], + "additionalProperties": true + }, + "TerminalResizeErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalResizeError" + ] + }, + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + }, + "terminalPid": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cols": { + "type": "integer", + "minimum": 1, + "maximum": 1000 + }, + "rows": { + "type": "integer", + "minimum": 1, + "maximum": 500 + }, + "cause": {} + }, + "required": [ + "_tag", + "threadId", + "terminalId", + "terminalPid", + "cols", + "rows", + "cause" + ], + "additionalProperties": true + }, + "TerminalResizePayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + }, + "cols": { + "type": "integer", + "minimum": 1, + "maximum": 1000 + }, + "rows": { + "type": "integer", + "minimum": 1, + "maximum": 500 + } + }, + "required": [ + "threadId", + "terminalId", + "cols", + "rows" + ], + "additionalProperties": true + }, + "TerminalSessionLookupErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalSessionLookupError" + ] + }, + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + } + }, + "required": [ + "_tag", + "threadId", + "terminalId" + ], + "additionalProperties": true + }, + "TerminalWriteErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TerminalWriteError" + ] + }, + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + }, + "terminalPid": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "threadId", + "terminalId", + "terminalPid", + "cause" + ], + "additionalProperties": true + }, + "TerminalWritePayload": { + "type": "object", + "properties": { + "threadId": { + "type": "string" + }, + "terminalId": { + "type": "string" + }, + "data": { + "type": "string", + "minLength": 1, + "maxLength": 65536 + } + }, + "required": [ + "threadId", + "terminalId", + "data" + ], + "additionalProperties": true + }, + "TextGenerationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "TextGenerationError" + ] + }, + "operation": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "detail" + ], + "additionalProperties": true + }, + "Threads": { + "type": "array", + "items": { + "$ref": "#/$defs/ThreadsEntry" + } + }, + "ThreadsEntry": { + "type": "object", + "properties": { + "createdBy": { + "type": "string", + "enum": [ + "user", + "agent", + "system" + ] + }, + "creationSource": { + "type": "string", + "enum": [ + "web", + "mobile", + "mcp", + "provider", + "server" + ] + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "providerInstanceId": { + "type": "string" + }, + "modelSelection": { + "$ref": "#/$defs/ModelSelection" + }, + "runtimeMode": { + "type": "string", + "enum": [ + "approval-required", + "auto-accept-edits", + "auto", + "full-access" + ] + }, + "interactionMode": { + "type": "string", + "enum": [ + "default", + "plan" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "linkedPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + }, + "pullRequests": { + "anyOf": [ + { + "$ref": "#/$defs/PullRequests" + }, + { + "type": "null" + } + ] + }, + "branchPullRequest": { + "anyOf": [ + { + "$ref": "#/$defs/LinkedPullRequest" + }, + { + "type": "null" + } + ] + }, + "lineage": { + "$ref": "#/$defs/Lineage" + }, + "forkedFrom": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "run" + ] + }, + "threadId": { + "type": "string" + }, + "runId": { + "type": "string" + } + }, + "required": [ + "type", + "threadId", + "runId" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "node" + ] + }, + "nodeId": { + "type": "string" + } + }, + "required": [ + "type", + "nodeId" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/ForkedFrom" + }, + { + "type": "null" + } + ] + }, + "activeProviderThreadId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "historyOrigin": { + "anyOf": [ + { + "type": "string", + "enum": [ + "native", + "v1_import" + ] + }, + { + "type": "null" + } + ] + }, + "linkOrigin": { + "anyOf": [ + { + "$ref": "#/$defs/LinkOrigin" + }, + { + "type": "null" + } + ] + }, + "delegatedFrom": { + "anyOf": [ + { + "type": "object", + "properties": { + "environmentId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "environmentId", + "threadId", + "title" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "latestRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "latestRunRequestedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "latestRunStartedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "latestRunCompletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "latestRunStopRequested": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "activeRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activityRunStartedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activityRunStatus": { + "anyOf": [ + { + "type": "string", + "enum": [ + "preparing", + "starting", + "running", + "waiting" + ] + }, + { + "type": "null" + } + ] + }, + "blockedOnThreadWait": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "idle", + "preparing", + "queued", + "starting", + "running", + "waiting", + "completed", + "interrupted", + "failed", + "cancelled", + "rolled_back" + ] + }, + "lastError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastErrorClass": { + "anyOf": [ + { + "type": "string", + "enum": [ + "usage_limit", + "provider_error", + "transport_error", + "permission_error", + "validation_error", + "unknown" + ] + }, + { + "type": "null" + } + ] + }, + "lastErrorCode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "usageLimitResetAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pendingRuntimeRequest": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "command", + "file-read", + "file-change", + "mcp-elicitation", + "permission", + "dynamic_tool_call", + "user_input", + "auth_refresh" + ] + }, + "createdAt": { + "type": "string" + }, + "blocking": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "kind", + "createdAt" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "pendingGuestMessageCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "latestVisibleMessage": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "user", + "assistant", + "system" + ] + }, + "text": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "role", + "text", + "updatedAt" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "latestUserMessageAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "latestUserAuthoredMessageAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hasActionableProposedPlan": { + "type": "boolean" + }, + "pendingBackgroundTasks": { + "anyOf": [ + { + "$ref": "#/$defs/PendingBackgroundTasks" + }, + { + "type": "null" + } + ] + }, + "providerInstanceHistory": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "goal": { + "anyOf": [ + { + "$ref": "#/$defs/Goal" + }, + { + "type": "null" + } + ] + }, + "itemCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "visibleItemCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "archivedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "settledOverride": { + "anyOf": [ + { + "type": "string", + "enum": [ + "settled", + "active" + ] + }, + { + "type": "null" + } + ] + }, + "settledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "unsettledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "snoozedUntil": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "snoozedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "limitRecovery": { + "anyOf": [ + { + "$ref": "#/$defs/LimitRecovery" + }, + { + "type": "null" + } + ] + }, + "pinnedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "autoSettleDisabledAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "pinOrderKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeOrderKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tags": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "lastVisitedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "opens": { + "anyOf": [ + { + "$ref": "#/$defs/Opens" + }, + { + "type": "null" + } + ] + }, + "titleRegeneration": { + "anyOf": [ + { + "type": "object", + "properties": { + "requestId": { + "type": "string" + }, + "startedAt": { + "type": "string" + } + }, + "required": [ + "requestId", + "startedAt" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "createdBy", + "creationSource", + "id", + "projectId", + "title", + "providerInstanceId", + "modelSelection", + "runtimeMode", + "interactionMode", + "branch", + "worktreePath", + "lineage", + "forkedFrom", + "activeProviderThreadId", + "latestRunId", + "activeRunId", + "status", + "pendingRuntimeRequest", + "latestVisibleMessage", + "latestUserMessageAt", + "hasActionableProposedPlan", + "itemCount", + "visibleItemCount", + "createdAt", + "updatedAt", + "archivedAt", + "settledOverride", + "settledAt", + "deletedAt" + ], + "additionalProperties": true + }, + "ToolSource": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "browser", + "computer", + "integration" + ] + }, + "icon": { + "anyOf": [ + { + "$ref": "#/$defs/Icon" + }, + { + "$ref": "#/$defs/Icon2" + }, + { + "$ref": "#/$defs/Icon3" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "key", + "name", + "kind" + ], + "additionalProperties": true + }, + "Tools": { + "type": "object", + "properties": { + "hub": { + "$ref": "#/$defs/Agent" + }, + "agent": { + "$ref": "#/$defs/Agent" + } + }, + "required": [ + "hub", + "agent" + ], + "additionalProperties": true + }, + "Union_": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "identifier" + ] + }, + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type", + "name" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "not" + ] + }, + "node": { + "$ref": "#/$defs/Union_" + } + }, + "required": [ + "type", + "node" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "and" + ] + }, + "left": { + "$ref": "#/$defs/Union_" + }, + "right": { + "$ref": "#/$defs/Union_" + } + }, + "required": [ + "type", + "left", + "right" + ], + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "or" + ] + }, + "left": { + "$ref": "#/$defs/Union_" + }, + "right": { + "$ref": "#/$defs/Union_" + } + }, + "required": [ + "type", + "left", + "right" + ], + "additionalProperties": true + } + ] + }, + "UsageLimitSourceErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "UsageLimitSourceError" + ] + }, + "detail": { + "type": "string" + } + }, + "required": [ + "_tag", + "detail" + ], + "additionalProperties": true + }, + "UsageLimitSources2": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "cliproxy" + ] + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "type": "string" + }, + "managementKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "provider": { + "type": "string" + }, + "reference": { + "type": "string" + } + }, + "required": [ + "provider", + "reference" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "enabled": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "kind", + "url" + ], + "additionalProperties": true + }, + "UsageLimitSources3": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "cliproxy" + ] + }, + "label": { + "type": "string" + }, + "checkedAt": { + "type": "string" + }, + "accounts": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "plan": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspaceId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "usageLimits": { + "$ref": "#/$defs/UsageLimits" + } + }, + "required": [ + "id", + "driver", + "usageLimits" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "kind", + "label", + "checkedAt", + "accounts" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "UsageLimits": { + "type": "object", + "properties": { + "checkedAt": { + "type": "string" + }, + "windows": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "session", + "weekly", + "monthly", + "other" + ] + }, + "label": { + "type": "string" + }, + "usedPercent": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "resetsAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "windowDurationMins": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "kind", + "label", + "usedPercent" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "credentialFingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resetCredits": { + "anyOf": [ + { + "type": "object", + "properties": { + "availableCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "nextExpiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "nextCreditId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "availableCount" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "externalUsage": { + "anyOf": [ + { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "label", + "url" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "unavailable": { + "anyOf": [ + { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "unsupported", + "probeFailed" + ] + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "reason" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "checkedAt", + "windows" + ], + "additionalProperties": true + }, + "UsagePriceOverrides2": { + "type": "object", + "properties": { + "inputCostPerMillionTokens": { + "type": "number", + "minimum": 0 + }, + "outputCostPerMillionTokens": { + "type": "number", + "minimum": 0 + }, + "cacheReadCostPerMillionTokens": { + "type": "number", + "minimum": 0 + }, + "cacheWriteCostPerMillionTokens": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "inputCostPerMillionTokens", + "outputCostPerMillionTokens" + ], + "additionalProperties": true + }, + "UsageReadErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "UsageReadError" + ] + }, + "reason": { + "type": "string", + "enum": [ + "scanFailed", + "invalidWindow" + ] + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "reason", + "detail" + ], + "additionalProperties": true + }, + "VcsCreateRefError": { + "anyOf": [ + { + "$ref": "#/$defs/GitCommandErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "VcsCreateRefPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "refName": { + "type": "string" + }, + "switchRef": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "refName" + ], + "additionalProperties": true + }, + "VcsCreateRefSuccess": { + "type": "object", + "properties": { + "refName": { + "type": "string" + } + }, + "required": [ + "refName" + ], + "additionalProperties": true + }, + "VcsCreateWorktreePayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "refName": { + "type": "string" + }, + "newRefName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "baseRefName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "path": { + "type": "null" + } + }, + "required": [ + "cwd", + "refName", + "path" + ], + "additionalProperties": true + }, + "VcsCreateWorktreeSuccess": { + "type": "object", + "properties": { + "worktree": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "refName": { + "type": "string" + } + }, + "required": [ + "path", + "refName" + ], + "additionalProperties": true + } + }, + "required": [ + "worktree" + ], + "additionalProperties": true + }, + "VcsInitError": { + "anyOf": [ + { + "$ref": "#/$defs/VcsProcessSpawnErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessExitErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessTimeoutErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessStdinWriteErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessOutputReadErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessOutputLimitErrorEncoded" + }, + { + "$ref": "#/$defs/VcsProcessMissingExitCodeErrorEncoded" + }, + { + "$ref": "#/$defs/VcsRepositoryDetectionErrorEncoded" + }, + { + "$ref": "#/$defs/VcsUnsupportedOperationErrorEncoded" + }, + { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + ] + }, + "VcsInitPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "kind": { + "anyOf": [ + { + "type": "string", + "enum": [ + "git", + "jj", + "unknown" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "VcsListRefsPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "query": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cursor": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "includeMatchingRemoteRefs": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "refKind": { + "anyOf": [ + { + "type": "string", + "enum": [ + "all", + "local", + "remote" + ] + }, + { + "type": "null" + } + ] + }, + "refresh": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "limit": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 200 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "VcsListRefsSuccess": { + "type": "object", + "properties": { + "isAtRepositoryRoot": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "refs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "isRemote": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "remoteName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "upstreamRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "current": { + "type": "boolean" + }, + "isDefault": { + "type": "boolean" + }, + "worktreePath": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "name", + "current", + "isDefault", + "worktreePath" + ], + "additionalProperties": true + } + }, + "isRepo": { + "type": "boolean" + }, + "hasPrimaryRemote": { + "type": "boolean" + }, + "nextCursor": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "totalCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "refs", + "isRepo", + "hasPrimaryRemote", + "nextCursor", + "totalCount" + ], + "additionalProperties": true + }, + "VcsProcessExitErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsProcessExitError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "exitCode": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "detail": { + "type": "string" + }, + "failureKind": { + "anyOf": [ + { + "type": "string", + "enum": [ + "authentication", + "not-found", + "rate-limited", + "command-failed" + ] + }, + { + "type": "null" + } + ] + }, + "retryable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "stderrLength": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "stderrTruncated": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "command", + "cwd", + "exitCode", + "detail" + ], + "additionalProperties": true + }, + "VcsProcessMissingExitCodeErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsProcessMissingExitCodeError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "command", + "cwd" + ], + "additionalProperties": true + }, + "VcsProcessOutputLimitErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsProcessOutputLimitError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "stream": { + "type": "string", + "enum": [ + "stdout", + "stderr" + ] + }, + "maxBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "observedBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "_tag", + "operation", + "command", + "cwd", + "stream", + "maxBytes", + "observedBytes" + ], + "additionalProperties": true + }, + "VcsProcessOutputReadErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsProcessOutputReadError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "stream": { + "type": "string", + "enum": [ + "stdout", + "stderr", + "exitCode" + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "operation", + "command", + "cwd", + "stream", + "cause" + ], + "additionalProperties": true + }, + "VcsProcessSpawnErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsProcessSpawnError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "cause": {} + }, + "required": [ + "_tag", + "operation", + "command", + "cwd", + "cause" + ], + "additionalProperties": true + }, + "VcsProcessStdinWriteErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsProcessStdinWriteError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "stdinBytes": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "cause": {} + }, + "required": [ + "_tag", + "operation", + "command", + "cwd", + "stdinBytes", + "cause" + ], + "additionalProperties": true + }, + "VcsProcessTimeoutErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsProcessTimeoutError" + ] + }, + "operation": { + "type": "string" + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "argumentCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "timeoutMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "_tag", + "operation", + "command", + "cwd", + "timeoutMs" + ], + "additionalProperties": true + }, + "VcsPullSuccess": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pulled", + "skipped_up_to_date" + ] + }, + "refName": { + "type": "string" + }, + "upstreamRef": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status", + "refName", + "upstreamRef" + ], + "additionalProperties": true + }, + "VcsRefreshStatusPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "localOnly": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd" + ], + "additionalProperties": true + }, + "VcsRefreshStatusSuccess": { + "type": "object", + "properties": { + "isRepo": { + "type": "boolean" + }, + "sourceControlProvider": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "baseUrl": { + "type": "string" + } + }, + "required": [ + "kind", + "name", + "baseUrl" + ], + "additionalProperties": true + }, + { + "type": "null" + } + ] + }, + "hasPrimaryRemote": { + "type": "boolean" + }, + "isDefaultRef": { + "type": "boolean" + }, + "refName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hasWorkingTreeChanges": { + "type": "boolean" + }, + "workingTree": { + "$ref": "#/$defs/WorkingTree" + }, + "branchChanges": { + "anyOf": [ + { + "$ref": "#/$defs/BranchChanges" + }, + { + "type": "null" + } + ] + }, + "hasUpstream": { + "type": "boolean" + }, + "aheadCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "behindCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "aheadOfDefaultCount": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "pr": { + "anyOf": [ + { + "$ref": "#/$defs/Pr" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "isRepo", + "hasPrimaryRemote", + "isDefaultRef", + "refName", + "hasWorkingTreeChanges", + "workingTree", + "hasUpstream", + "aheadCount", + "behindCount", + "pr" + ], + "additionalProperties": true + }, + "VcsRemoveWorktreePayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "path": { + "type": "string" + }, + "force": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "cwd", + "path" + ], + "additionalProperties": true + }, + "VcsRepositoryDetectionErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsRepositoryDetectionError" + ] + }, + "operation": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "cause": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "operation", + "cwd", + "detail" + ], + "additionalProperties": true + }, + "VcsSwitchRefPayload": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "refName": { + "type": "string" + } + }, + "required": [ + "cwd", + "refName" + ], + "additionalProperties": true + }, + "VcsSwitchRefSuccess": { + "type": "object", + "properties": { + "refName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "refName" + ], + "additionalProperties": true + }, + "VcsUnsupportedOperationErrorEncoded": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "VcsUnsupportedOperationError" + ] + }, + "operation": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "git", + "jj", + "unknown" + ] + }, + "detail": { + "type": "string" + } + }, + "required": [ + "_tag", + "operation", + "kind", + "detail" + ], + "additionalProperties": true + }, + "Viewport": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "freeform" + ] + }, + "width": { + "type": "integer", + "minimum": 240, + "maximum": 3840 + }, + "height": { + "type": "integer", + "minimum": 240, + "maximum": 3840 + } + }, + "required": [ + "_tag", + "width", + "height" + ], + "additionalProperties": true + }, + "Viewport2": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "preset" + ] + }, + "width": { + "type": "integer", + "minimum": 240, + "maximum": 3840 + }, + "height": { + "type": "integer", + "minimum": 240, + "maximum": 3840 + }, + "presetId": { + "type": "string", + "enum": [ + "iphone-se", + "iphone-xr", + "iphone-12-pro", + "iphone-14-pro-max", + "pixel-7", + "samsung-galaxy-s8-plus", + "samsung-galaxy-s20-ultra", + "ipad-mini", + "ipad-air", + "ipad-pro", + "surface-pro-7", + "surface-duo", + "galaxy-z-fold-5", + "asus-zenbook-fold", + "samsung-galaxy-a51-71", + "nest-hub", + "nest-hub-max", + "desktop-1920x1080", + "desktop-1440x900", + "laptop-1366x768", + "laptop-1280x800", + "ipad-pro-11", + "iphone-15-pro", + "pixel-8", + "galaxy-s24" + ] + } + }, + "required": [ + "_tag", + "width", + "height", + "presetId" + ], + "additionalProperties": true + }, + "WorkingTree": { + "type": "object", + "properties": { + "rootPath": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ] + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "minLength": 1 + }, + "insertions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "path", + "insertions", + "deletions" + ], + "additionalProperties": true + } + }, + "insertions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "deletions": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "files", + "insertions", + "deletions" + ], + "additionalProperties": true + }, + "WorkspaceStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "existing_worktree" + ] + }, + "worktreePath": { + "type": "string" + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "worktreePath" + ], + "additionalProperties": true + }, + "WorkspaceStrategy2": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "worktree" + ] + }, + "baseRef": { + "type": "string" + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startFromOrigin": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "baseRef" + ], + "additionalProperties": true + }, + "WorkspaceStrategy3": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "root" + ] + }, + "branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + { + "$ref": "#/$defs/WorkspaceStrategy" + }, + { + "$ref": "#/$defs/WorkspaceStrategy2" + } + ] + }, + "WorktreeCleanup": { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "custom" + ] + }, + "rules": { + "type": "object", + "properties": { + "worktreeKeepWhen": { + "anyOf": [ + { + "type": "string", + "enum": [ + "any-local-files", + "uncommitted-changes", + "tracked-changes" + ] + }, + { + "type": "null" + } + ] + }, + "worktreeAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 1, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeSettledAfterDays": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 3650 + }, + { + "type": "null" + } + ] + }, + "worktreeOnMerge": { + "type": "boolean" + }, + "worktreeOnDelete": { + "type": "boolean" + }, + "worktreeUnchanged": { + "type": "boolean" + } + }, + "required": [ + "worktreeAfterDays", + "worktreeOnMerge", + "worktreeOnDelete", + "worktreeUnchanged" + ], + "additionalProperties": true + } + }, + "required": [ + "mode", + "rules" + ], + "additionalProperties": true + }, + "WorktreeSetupCancelSuccess": { + "type": "object", + "properties": { + "cancelled": { + "type": "boolean" + } + }, + "required": [ + "cancelled" + ], + "additionalProperties": true + } + }, + "methods": { + "agentSessions.import": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsImportPayload" + }, + "success": { + "$ref": "#/$defs/AgentSessionsImportSuccess" + }, + "error": { + "$ref": "#/$defs/AgentSessionsImportError" + } + }, + "agentSessions.scan": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/AgentSessionsScanSuccess" + }, + "error": { + "$ref": "#/$defs/AgentSessionsScanError" + } + }, + "assets.createUrl": { + "stream": false, + "payload": { + "$ref": "#/$defs/AssetsCreateUrlPayload" + }, + "success": { + "$ref": "#/$defs/AssetsCreateUrlSuccess" + }, + "error": { + "$ref": "#/$defs/AssetsCreateUrlError" + } + }, + "assets.persistChatAttachments": { + "stream": false, + "payload": { + "$ref": "#/$defs/AssetsPersistChatAttachmentsPayload" + }, + "success": { + "$ref": "#/$defs/AssetsPersistChatAttachmentsSuccess" + }, + "error": { + "$ref": "#/$defs/AssetsPersistChatAttachmentsError" + } + }, + "attachments.createUploadUrl": { + "stream": false, + "payload": { + "$ref": "#/$defs/AttachmentsCreateUploadUrlPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsCreateUploadUrlSuccess" + }, + "error": { + "$ref": "#/$defs/AttachmentsCreateUploadUrlError" + } + }, + "attachments.delete": { + "stream": false, + "payload": { + "$ref": "#/$defs/AttachmentsDeletePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "cloud.getRelayClientStatus": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/CloudGetRelayClientStatusSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "cloud.installRelayClient": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/CloudInstallRelayClientSuccess" + }, + "error": { + "$ref": "#/$defs/CloudInstallRelayClientError" + } + }, + "device.action": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceActionPayload" + }, + "success": { + "$ref": "#/$defs/DeviceActionSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "device.close": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceClosePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "device.configure": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceConfigurePayload" + }, + "success": { + "$ref": "#/$defs/DeviceConfigureSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "device.detail": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceDetailPayload" + }, + "success": { + "$ref": "#/$defs/DeviceActionSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "device.easAccount": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceEasAccountPayload" + }, + "success": { + "$ref": "#/$defs/DeviceEasAccountSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceEasAccountError" + } + }, + "device.list": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceListPayload" + }, + "success": { + "$ref": "#/$defs/DeviceConfigureSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "device.open": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceOpenPayload" + }, + "success": { + "$ref": "#/$defs/SessionsEntry" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "device.shutdown": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceShutdownPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "device.simlockIdentity": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceSimlockIdentityPayload" + }, + "success": { + "$ref": "#/$defs/DeviceSimlockIdentitySuccess" + }, + "error": { + "$ref": "#/$defs/DeviceSimlockIdentityError" + } + }, + "device.testHost": { + "stream": false, + "payload": { + "$ref": "#/$defs/DeviceTestHostPayload" + }, + "success": { + "$ref": "#/$defs/DeviceTestHostSuccess" + }, + "error": { + "$ref": "#/$defs/DeviceActionError" + } + }, + "filesystem.browse": { + "stream": false, + "payload": { + "$ref": "#/$defs/FilesystemBrowsePayload" + }, + "success": { + "$ref": "#/$defs/FilesystemBrowseSuccess" + }, + "error": { + "$ref": "#/$defs/FilesystemBrowseError" + } + }, + "filesystem.getMetadata": { + "stream": false, + "payload": { + "$ref": "#/$defs/FilesystemGetMetadataPayload" + }, + "success": { + "$ref": "#/$defs/FilesystemGetMetadataSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "git.preparePullRequestThread": { + "stream": false, + "payload": { + "$ref": "#/$defs/GitPreparePullRequestThreadPayload" + }, + "success": { + "$ref": "#/$defs/GitPreparePullRequestThreadSuccess" + }, + "error": { + "$ref": "#/$defs/GitPreparePullRequestThreadError" + } + }, + "git.resolvePullRequest": { + "stream": false, + "payload": { + "$ref": "#/$defs/GitResolvePullRequestPayload" + }, + "success": { + "$ref": "#/$defs/GitResolvePullRequestSuccess" + }, + "error": { + "$ref": "#/$defs/GitPreparePullRequestThreadError" + } + }, + "git.runStackedAction": { + "stream": true, + "payload": { + "$ref": "#/$defs/GitRunStackedActionPayload" + }, + "success": { + "$ref": "#/$defs/GitRunStackedActionSuccess" + }, + "error": { + "$ref": "#/$defs/GitPreparePullRequestThreadError" + } + }, + "guestShare.create": { + "stream": false, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/GuestShareCreateSuccess" + }, + "error": { + "$ref": "#/$defs/GuestShareCreateError" + } + }, + "mcpApps.callTool": { + "stream": false, + "payload": { + "$ref": "#/$defs/McpAppsCallToolPayload" + }, + "success": { + "$ref": "#/$defs/McpAppsCallToolSuccess" + }, + "error": { + "$ref": "#/$defs/McpAppsCallToolError" + } + }, + "mcpApps.readResource": { + "stream": false, + "payload": { + "$ref": "#/$defs/McpAppsReadResourcePayload" + }, + "success": { + "$ref": "#/$defs/McpAppsReadResourceSuccess" + }, + "error": { + "$ref": "#/$defs/McpAppsCallToolError" + } + }, + "mcpApps.toolInfo": { + "stream": false, + "payload": { + "$ref": "#/$defs/McpAppsToolInfoPayload" + }, + "success": { + "$ref": "#/$defs/McpAppsToolInfoSuccess" + }, + "error": { + "$ref": "#/$defs/McpAppsCallToolError" + } + }, + "mcpApps.updateModelContext": { + "stream": false, + "payload": { + "$ref": "#/$defs/McpAppsUpdateModelContextPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/McpAppsCallToolError" + } + }, + "orchestration.dispatchCommand": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationDispatchCommandPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationDispatchCommandSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationDispatchCommandError" + } + }, + "orchestration.forceStopThread": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationForceStopThreadPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationForceStopThreadSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationForceStopThreadError" + } + }, + "orchestration.getArchivedShellSnapshot": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetArchivedShellSnapshotSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetArchivedShellSnapshotError" + } + }, + "orchestration.getFullThreadDiff": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationGetFullThreadDiffPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationTurnDiffRange" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetFullThreadDiffError" + } + }, + "orchestration.getNativeChildWork": { + "stream": false, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetNativeChildWorkSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetNativeChildWorkError" + } + }, + "orchestration.getOlderSettledThreads": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationGetOlderSettledThreadsPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetOlderSettledThreadsSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetArchivedShellSnapshotError" + } + }, + "orchestration.getThreadFamily": { + "stream": false, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetThreadFamilySuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetArchivedShellSnapshotError" + } + }, + "orchestration.getThreadProjection": { + "stream": false, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetThreadProjectionSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetThreadProjectionError" + } + }, + "orchestration.getTurnDiff": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationTurnDiffRange2" + }, + "success": { + "$ref": "#/$defs/OrchestrationTurnDiffRange" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetTurnDiffError" + } + }, + "orchestration.getTurnItem": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationGetTurnItemPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetTurnItemSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetThreadProjectionError" + } + }, + "orchestration.getWorkflowScript": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationGetWorkflowScriptPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetWorkflowScriptSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetWorkflowScriptError" + } + }, + "orchestration.getWorktreeThreadIds": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationGetWorktreeThreadIdsPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationGetWorktreeThreadIdsSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetArchivedShellSnapshotError" + } + }, + "orchestration.launchThread": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationLaunchThreadPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationLaunchThreadSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationLaunchThreadError" + } + }, + "orchestration.searchThread": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationSearchThreadPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationSearchThreadSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationSearchThreadError" + } + }, + "orchestration.searchThreads": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationSearchThreadsPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationSearchThreadsSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationSearchThreadsError" + } + }, + "orchestration.searchThreadStream": { + "stream": true, + "payload": { + "$ref": "#/$defs/OrchestrationSearchThreadPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationSearchThreadSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationSearchThreadError" + } + }, + "orchestration.stopWorkflow": { + "stream": false, + "payload": { + "$ref": "#/$defs/OrchestrationStopWorkflowPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationStopWorkflowError" + } + }, + "orchestration.subscribeCommandOutput": { + "stream": true, + "payload": { + "$ref": "#/$defs/OrchestrationSubscribeCommandOutputPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationSubscribeCommandOutputSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationSubscribeCommandOutputError" + } + }, + "orchestration.subscribeShell": { + "stream": true, + "payload": { + "$ref": "#/$defs/OrchestrationSubscribeShellPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationSubscribeShellSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetArchivedShellSnapshotError" + } + }, + "orchestration.subscribeThread": { + "stream": true, + "payload": { + "$ref": "#/$defs/OrchestrationSubscribeThreadPayload" + }, + "success": { + "$ref": "#/$defs/OrchestrationSubscribeThreadSuccess" + }, + "error": { + "$ref": "#/$defs/OrchestrationGetThreadProjectionError" + } + }, + "peerLinks.answerRequest": { + "stream": false, + "payload": { + "$ref": "#/$defs/PeerLinksAnswerRequestPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PeerLinksAnswerRequestError" + } + }, + "peerLinks.link": { + "stream": false, + "payload": { + "$ref": "#/$defs/PeerLinksLinkPayload" + }, + "success": { + "$ref": "#/$defs/PeerLinksLinkSuccess" + }, + "error": { + "$ref": "#/$defs/PeerLinksLinkError" + } + }, + "peerLinks.list": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/PeerLinksListSuccess" + }, + "error": { + "$ref": "#/$defs/PeerLinksLinkError" + } + }, + "peerLinks.unlink": { + "stream": false, + "payload": { + "$ref": "#/$defs/PeerLinksUnlinkPayload" + }, + "success": { + "$ref": "#/$defs/PeerLinksUnlinkSuccess" + }, + "error": { + "$ref": "#/$defs/PeerLinksLinkError" + } + }, + "preview.adjust": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewAdjustPayload" + }, + "success": { + "$ref": "#/$defs/PreviewAdjustSuccess" + }, + "error": { + "$ref": "#/$defs/PreviewAdjustError" + } + }, + "preview.clearProfile": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewClearProfilePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PreviewClearProfileError" + } + }, + "preview.close": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewClosePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PreviewAdjustError" + } + }, + "preview.list": { + "stream": false, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/PreviewListSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "preview.open": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewOpenPayload" + }, + "success": { + "$ref": "#/$defs/PreviewAdjustSuccess" + }, + "error": { + "$ref": "#/$defs/PreviewAdjustError" + } + }, + "preview.reportProfiles": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewReportProfilesPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "preview.reportStatus": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewReportStatusPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PreviewAdjustError" + } + }, + "preview.resize": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewResizePayload" + }, + "success": { + "$ref": "#/$defs/PreviewAdjustSuccess" + }, + "error": { + "$ref": "#/$defs/PreviewAdjustError" + } + }, + "preview.stopLocalServer": { + "stream": false, + "payload": { + "$ref": "#/$defs/PreviewStopLocalServerPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PreviewStopLocalServerError" + } + }, + "projectClone.cancel": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectCloneCancelPayload" + }, + "success": { + "$ref": "#/$defs/ProjectCloneCancelSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "projectClone.retry": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectCloneCancelPayload" + }, + "success": { + "$ref": "#/$defs/ProjectCloneCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectCloneRetryError" + } + }, + "projectClone.start": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectCloneStartPayload" + }, + "success": { + "$ref": "#/$defs/ProjectCloneStartSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectCloneStartError" + } + }, + "projects.createFromThread": { + "stream": false, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/ProjectCloneCancelPayload" + }, + "error": { + "$ref": "#/$defs/ProjectsCreateFromThreadError" + } + }, + "projects.createNew": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsCreateNewPayload" + }, + "success": { + "$ref": "#/$defs/ProjectsCreateNewSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsCreateFromThreadError" + } + }, + "projects.ensureScratch": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ProjectCloneCancelPayload" + }, + "error": { + "$ref": "#/$defs/ProjectsCreateFromThreadError" + } + }, + "projects.listEntries": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsListEntriesPayload" + }, + "success": { + "$ref": "#/$defs/ProjectsListEntriesSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsListEntriesError" + } + }, + "projects.listIgnoredFiles": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsListIgnoredFilesPayload" + }, + "success": { + "$ref": "#/$defs/ProjectsListIgnoredFilesSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsListEntriesError" + } + }, + "projects.mutate": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsMutatePayload" + }, + "success": { + "$ref": "#/$defs/ProjectsMutateSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsMutateError" + } + }, + "projects.readFile": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsReadFilePayload" + }, + "success": { + "$ref": "#/$defs/ProjectsReadFileSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsReadFileError" + } + }, + "projects.searchContents": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsSearchContentsPayload" + }, + "success": { + "$ref": "#/$defs/ProjectsSearchContentsSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsSearchContentsError" + } + }, + "projects.searchEntries": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsSearchEntriesPayload" + }, + "success": { + "$ref": "#/$defs/ProjectsListEntriesSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsSearchEntriesError" + } + }, + "projects.writeFile": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsWriteFilePayload" + }, + "success": { + "$ref": "#/$defs/ProjectsWriteFileSuccess" + }, + "error": { + "$ref": "#/$defs/ProjectsWriteFileError" + } + }, + "provider.auth.cancel": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthCancelPayload" + }, + "success": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.auth.complete": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthCompletePayload" + }, + "success": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.auth.logout": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthLogoutPayload" + }, + "success": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.auth.respond": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthRespondPayload" + }, + "success": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.auth.start": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthStartPayload" + }, + "success": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.auth.subscribe": { + "stream": true, + "payload": { + "$ref": "#/$defs/ProviderAuthLogoutPayload" + }, + "success": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.chatgpt.handoff.subscribe": { + "stream": true, + "payload": { + "$ref": "#/$defs/ProviderChatgptHandoffSubscribePayload" + }, + "success": { + "$ref": "#/$defs/ProviderChatgptHandoffSubscribeSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.chatgpt.import-profile": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderChatgptImportProfilePayload" + }, + "success": { + "$ref": "#/$defs/ProviderAuthCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.chatgpt.reconnect-profile": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderChatgptReconnectProfilePayload" + }, + "success": { + "$ref": "#/$defs/ProviderChatgptReconnectProfileSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.consumeResetCredit": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderConsumeResetCreditPayload" + }, + "success": { + "$ref": "#/$defs/ProviderConsumeResetCreditSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderConsumeResetCreditError" + } + }, + "provider.install.cancel": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderInstallCancelPayload" + }, + "success": { + "$ref": "#/$defs/ProviderInstallCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.install.remove": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthLogoutPayload" + }, + "success": { + "$ref": "#/$defs/ProviderInstallCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.install.start": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthLogoutPayload" + }, + "success": { + "$ref": "#/$defs/ProviderInstallCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.install.subscribe": { + "stream": true, + "payload": { + "$ref": "#/$defs/ProviderAuthLogoutPayload" + }, + "success": { + "$ref": "#/$defs/ProviderInstallCancelSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderAuthCancelError" + } + }, + "provider.uploadFeedback": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderUploadFeedbackPayload" + }, + "success": { + "$ref": "#/$defs/ProviderUploadFeedbackSuccess" + }, + "error": { + "$ref": "#/$defs/ProviderUploadFeedbackError" + } + }, + "pullRequests.activity": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsActivitySuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.changeBreakdown": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsChangeBreakdownSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.checks": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsChecksSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.comment": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsCommentPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.detail": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsDetailSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.diffFileContents": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsDiffFileContentsPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsDiffFileContentsSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.filesViewed": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsFilesViewedSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.filterOptions": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsFilterOptionsPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsFilterOptionsSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.generateReviewGuide": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsGenerateReviewGuideSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.invalidate": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsInvalidatePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.labelCandidates": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsLabelCandidatesSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.linkedThreads": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsLinkedThreadsSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.list": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsListPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsListSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.listStats": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsListStatsPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsListStatsSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.preview": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsPreviewSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.replyToThread": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsReplyToThreadPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.reportState": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsReportStatePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.requestReviewers": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsRequestReviewersPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.reviewerCandidates": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsReviewerCandidatesSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.reviewGuide": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsGenerateReviewGuideSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.routing": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsRoutingSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.routingIdentity": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsRoutingIdentityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsRoutingIdentitySuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.runAction": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsRunActionPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.setFilesViewed": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsSetFilesViewedPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.setLabels": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsSetLabelsPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.setReaction": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsSetReactionPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.setThreadResolution": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsSetThreadResolutionPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.stack": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsStackSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.submitReview": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsSubmitReviewPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.subscribeRefreshes": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsSubscribeRefreshesSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "pullRequests.summary": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsActivityPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsSummarySuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.threadComments": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsThreadCommentsPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsThreadCommentsSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.update": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsUpdatePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "pullRequests.updateComment": { + "stream": false, + "payload": { + "$ref": "#/$defs/PullRequestsUpdateCommentPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/PullRequestsActivityError" + } + }, + "review.getBranchCommits": { + "stream": false, + "payload": { + "$ref": "#/$defs/ReviewGetBranchCommitsPayload" + }, + "success": { + "$ref": "#/$defs/ReviewGetBranchCommitsSuccess" + }, + "error": { + "$ref": "#/$defs/ReviewGetBranchCommitsError" + } + }, + "review.getDiffFileContents": { + "stream": false, + "payload": { + "$ref": "#/$defs/ReviewGetDiffFileContentsPayload" + }, + "success": { + "$ref": "#/$defs/PullRequestsDiffFileContentsSuccess" + }, + "error": { + "$ref": "#/$defs/ReviewGetBranchCommitsError" + } + }, + "review.getDiffPreview": { + "stream": false, + "payload": { + "$ref": "#/$defs/ReviewGetDiffPreviewPayload" + }, + "success": { + "$ref": "#/$defs/ReviewGetDiffPreviewSuccess" + }, + "error": { + "$ref": "#/$defs/ReviewGetBranchCommitsError" + } + }, + "scheduledTasks.delete": { + "stream": false, + "payload": { + "$ref": "#/$defs/ScheduledTasksDeletePayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksDeletePayload" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "scheduledTasks.getWebhookDelivery": { + "stream": false, + "payload": { + "$ref": "#/$defs/ScheduledTasksGetWebhookDeliveryPayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksGetWebhookDeliverySuccess" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "scheduledTasks.listWebhookDeliveries": { + "stream": false, + "payload": { + "$ref": "#/$defs/ScheduledTasksListWebhookDeliveriesPayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksListWebhookDeliveriesSuccess" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "scheduledTasks.rotateWebhookToken": { + "stream": false, + "payload": { + "$ref": "#/$defs/ScheduledTasksDeletePayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksRotateWebhookTokenSuccess" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "scheduledTasks.runNow": { + "stream": false, + "payload": { + "$ref": "#/$defs/ScheduledTasksDeletePayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksRotateWebhookTokenSuccess" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "scheduledTasks.setEnabled": { + "stream": false, + "payload": { + "$ref": "#/$defs/ScheduledTasksSetEnabledPayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksRotateWebhookTokenSuccess" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "scheduledTasks.subscribe": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksSubscribeSuccess" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "scheduledTasks.upsert": { + "stream": false, + "payload": { + "$ref": "#/$defs/ScheduledTasksUpsertPayload" + }, + "success": { + "$ref": "#/$defs/ScheduledTasksRotateWebhookTokenSuccess" + }, + "error": { + "$ref": "#/$defs/ScheduledTasksDeleteError" + } + }, + "secrets.answerRequest": { + "stream": false, + "payload": { + "$ref": "#/$defs/SecretsAnswerRequestPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/SecretsAnswerRequestError" + } + }, + "server.acceptAcpRegistryUrlAuth": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerAcceptAcpRegistryUrlAuthPayload" + }, + "success": { + "$ref": "#/$defs/ServerAcceptAcpRegistryUrlAuthSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.checkOtlpEndpoint": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerCheckOtlpEndpointPayload" + }, + "success": { + "$ref": "#/$defs/ServerCheckOtlpEndpointSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.checkSecretSource": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerCheckSecretSourcePayload" + }, + "success": { + "$ref": "#/$defs/ServerCheckSecretSourceSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.commitDesktopUpdate": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerCommitDesktopUpdatePayload" + }, + "success": { + "$ref": "#/$defs/ServerCommitDesktopUpdateSuccess" + }, + "error": { + "$ref": "#/$defs/ServerCommitDesktopUpdateError" + } + }, + "server.deleteAcpRegistrySession": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionPayload" + }, + "success": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionSuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.disableAcpRegistryProvider": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerDisableAcpRegistryProviderPayload" + }, + "success": { + "$ref": "#/$defs/ServerDisableAcpRegistryProviderSuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.discoverSourceControl": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerDiscoverSourceControlSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getConfig": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetConfigSuccess" + }, + "error": { + "$ref": "#/$defs/ServerGetConfigError" + } + }, + "server.getDatabaseStorage": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetDatabaseStorageSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getHostResources": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetHostResourcesSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getNpmRelease": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerGetNpmReleasePayload" + }, + "success": { + "$ref": "#/$defs/ServerGetNpmReleaseSuccess" + }, + "error": { + "$ref": "#/$defs/ServerGetNpmReleaseError" + } + }, + "server.getProcessDiagnostics": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetProcessDiagnosticsSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getProcessResourceHistory": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerGetProcessResourceHistoryPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetProcessResourceHistorySuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getResourceTelemetryHistory": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerGetProcessResourceHistoryPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetResourceTelemetryHistorySuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getStorageCleanupReport": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetStorageCleanupReportSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getTraceDiagnostics": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetTraceDiagnosticsSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.getUsageSummary": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerGetUsageSummaryPayload" + }, + "success": { + "$ref": "#/$defs/ServerGetUsageSummarySuccess" + }, + "error": { + "$ref": "#/$defs/ServerGetUsageSummaryError" + } + }, + "server.importAcpRegistrySession": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerImportAcpRegistrySessionPayload" + }, + "success": { + "$ref": "#/$defs/ServerImportAcpRegistrySessionSuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.listAcpRegistryProviders": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerListAcpRegistryProvidersPayload" + }, + "success": { + "$ref": "#/$defs/ServerListAcpRegistryProvidersSuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.listAcpRegistrySessions": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerListAcpRegistrySessionsPayload" + }, + "success": { + "$ref": "#/$defs/ServerListAcpRegistrySessionsSuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.listOnePasswordAccounts": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerListOnePasswordAccountsSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.logoutAcpRegistry": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProviderAuthLogoutPayload" + }, + "success": { + "$ref": "#/$defs/ServerLogoutAcpRegistrySuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.prepareAcpRegistryAgent": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerPrepareAcpRegistryAgentPayload" + }, + "success": { + "$ref": "#/$defs/ServerPrepareAcpRegistryAgentSuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.prepareTriage": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerPrepareTriagePayload" + }, + "success": { + "$ref": "#/$defs/ServerPrepareTriageSuccess" + }, + "error": { + "$ref": "#/$defs/ServerPrepareTriageError" + } + }, + "server.probe": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.refreshProviders": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerRefreshProvidersPayload" + }, + "success": { + "$ref": "#/$defs/ServerRefreshProvidersSuccess" + }, + "error": { + "$ref": "#/$defs/ServerRefreshProvidersError" + } + }, + "server.refreshUsageRates": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/Pricing" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.removeKeybinding": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerRemoveKeybindingPayload" + }, + "success": { + "$ref": "#/$defs/ServerRemoveKeybindingSuccess" + }, + "error": { + "$ref": "#/$defs/ServerRemoveKeybindingError" + } + }, + "server.reportClientActivity": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerReportClientActivityPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.retryResourceTelemetry": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/ServerRetryResourceTelemetrySuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.runStorageCleanup": { + "stream": false, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/Shape" + }, + "error": { + "$ref": "#/$defs/ServerRunStorageCleanupError" + } + }, + "server.searchAcpRegistry": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerSearchAcpRegistryPayload" + }, + "success": { + "$ref": "#/$defs/ServerSearchAcpRegistrySuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.setAcpRegistryProvider": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerSetAcpRegistryProviderPayload" + }, + "success": { + "$ref": "#/$defs/ServerSetAcpRegistryProviderSuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.signalProcess": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerSignalProcessPayload" + }, + "success": { + "$ref": "#/$defs/ServerSignalProcessSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "server.uninstallAcpRegistryManagedBinary": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerPrepareAcpRegistryAgentPayload" + }, + "success": { + "$ref": "#/$defs/ServerUninstallAcpRegistryManagedBinarySuccess" + }, + "error": { + "$ref": "#/$defs/ServerDeleteAcpRegistrySessionError" + } + }, + "server.updateProvider": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerUpdateProviderPayload" + }, + "success": { + "$ref": "#/$defs/ServerRefreshProvidersSuccess" + }, + "error": { + "$ref": "#/$defs/ServerUpdateProviderError" + } + }, + "server.updateServer": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerUpdateServerPayload" + }, + "success": { + "$ref": "#/$defs/ServerCommitDesktopUpdateSuccess" + }, + "error": { + "$ref": "#/$defs/ServerCommitDesktopUpdateError" + } + }, + "server.updateServerWithProgress": { + "stream": true, + "payload": { + "$ref": "#/$defs/ServerUpdateServerPayload" + }, + "success": { + "$ref": "#/$defs/ServerUpdateServerWithProgressSuccess" + }, + "error": { + "$ref": "#/$defs/ServerCommitDesktopUpdateError" + } + }, + "server.updateSettings": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerUpdateSettingsPayload" + }, + "success": { + "$ref": "#/$defs/Settings2" + }, + "error": { + "$ref": "#/$defs/ServerRunStorageCleanupError" + } + }, + "server.upsertKeybinding": { + "stream": false, + "payload": { + "$ref": "#/$defs/ServerUpsertKeybindingPayload" + }, + "success": { + "$ref": "#/$defs/ServerRemoveKeybindingSuccess" + }, + "error": { + "$ref": "#/$defs/ServerRemoveKeybindingError" + } + }, + "shell.openInEditor": { + "stream": false, + "payload": { + "$ref": "#/$defs/ShellOpenInEditorPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/ShellOpenInEditorError" + } + }, + "sourceControl.appAccess": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlAppAccessPayload" + }, + "success": { + "$ref": "#/$defs/SourceControlAppAccessSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "sourceControl.cloneRepository": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlCloneRepositoryPayload" + }, + "success": { + "$ref": "#/$defs/SourceControlCloneRepositorySuccess" + }, + "error": { + "$ref": "#/$defs/ProjectCloneRetryError" + } + }, + "sourceControl.lookupRepository": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlLookupRepositoryPayload" + }, + "success": { + "$ref": "#/$defs/Repository" + }, + "error": { + "$ref": "#/$defs/ProjectCloneRetryError" + } + }, + "sourceControl.publishRepository": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlPublishRepositoryPayload" + }, + "success": { + "$ref": "#/$defs/SourceControlPublishRepositorySuccess" + }, + "error": { + "$ref": "#/$defs/ProjectCloneRetryError" + } + }, + "sourceControl.signInCancel": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlSignInCancelPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/SourceControlSignInCancelError" + } + }, + "sourceControl.signInStart": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlSignInCancelPayload" + }, + "success": { + "$ref": "#/$defs/SourceControlSignInStartSuccess" + }, + "error": { + "$ref": "#/$defs/SourceControlSignInCancelError" + } + }, + "sourceControl.signInWait": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlSignInCancelPayload" + }, + "success": { + "$ref": "#/$defs/SourceControlSignInWaitSuccess" + }, + "error": { + "$ref": "#/$defs/SourceControlSignInCancelError" + } + }, + "sourceControl.star": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlAppAccessPayload" + }, + "success": { + "$ref": "#/$defs/SourceControlStarSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "sourceControl.starred": { + "stream": false, + "payload": { + "$ref": "#/$defs/SourceControlAppAccessPayload" + }, + "success": { + "$ref": "#/$defs/SourceControlStarSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribeAuthAccess": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/SubscribeAuthAccessSuccess" + }, + "error": { + "$ref": "#/$defs/SubscribeAuthAccessError" + } + }, + "subscribeDeviceState": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/DeviceConfigureSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribeDiscoveredLocalServers": { + "stream": true, + "payload": { + "$ref": "#/$defs/SubscribeDiscoveredLocalServersPayload" + }, + "success": { + "$ref": "#/$defs/SubscribeDiscoveredLocalServersSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribePreviewEvents": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/SubscribePreviewEventsSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribeProjectClones": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/SubscribeProjectClonesSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribeResourceTelemetry": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/Snapshot2" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribeServerConfig": { + "stream": true, + "payload": { + "$ref": "#/$defs/SubscribeServerConfigPayload" + }, + "success": { + "$ref": "#/$defs/SubscribeServerConfigSuccess" + }, + "error": { + "$ref": "#/$defs/ServerGetConfigError" + } + }, + "subscribeServerLifecycle": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/SubscribeServerLifecycleSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribeTerminalMetadata": { + "stream": true, + "payload": { + "$ref": "#/$defs/AgentSessionsScanPayload" + }, + "success": { + "$ref": "#/$defs/SubscribeTerminalMetadataSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "subscribeVcsStatus": { + "stream": true, + "payload": { + "$ref": "#/$defs/SubscribeVcsStatusPayload" + }, + "success": { + "$ref": "#/$defs/SubscribeVcsStatusSuccess" + }, + "error": { + "$ref": "#/$defs/GitPreparePullRequestThreadError" + } + }, + "subscribeWorktreeSetup": { + "stream": true, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/SubscribeWorktreeSetupSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + }, + "terminal.attach": { + "stream": true, + "payload": { + "$ref": "#/$defs/TerminalAttachPayload" + }, + "success": { + "$ref": "#/$defs/TerminalAttachSuccess" + }, + "error": { + "$ref": "#/$defs/TerminalAttachError" + } + }, + "terminal.clear": { + "stream": false, + "payload": { + "$ref": "#/$defs/TerminalClearPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/TerminalAttachError" + } + }, + "terminal.close": { + "stream": false, + "payload": { + "$ref": "#/$defs/TerminalClosePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/TerminalAttachError" + } + }, + "terminal.observe": { + "stream": true, + "payload": { + "$ref": "#/$defs/TerminalClearPayload" + }, + "success": { + "$ref": "#/$defs/TerminalAttachSuccess" + }, + "error": { + "$ref": "#/$defs/TerminalAttachError" + } + }, + "terminal.open": { + "stream": false, + "payload": { + "$ref": "#/$defs/TerminalOpenPayload" + }, + "success": { + "$ref": "#/$defs/Snapshot3" + }, + "error": { + "$ref": "#/$defs/TerminalAttachError" + } + }, + "terminal.resize": { + "stream": false, + "payload": { + "$ref": "#/$defs/TerminalResizePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/TerminalAttachError" + } + }, + "terminal.write": { + "stream": false, + "payload": { + "$ref": "#/$defs/TerminalWritePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/TerminalAttachError" + } + }, + "vcs.createRef": { + "stream": false, + "payload": { + "$ref": "#/$defs/VcsCreateRefPayload" + }, + "success": { + "$ref": "#/$defs/VcsCreateRefSuccess" + }, + "error": { + "$ref": "#/$defs/VcsCreateRefError" + } + }, + "vcs.createWorktree": { + "stream": false, + "payload": { + "$ref": "#/$defs/VcsCreateWorktreePayload" + }, + "success": { + "$ref": "#/$defs/VcsCreateWorktreeSuccess" + }, + "error": { + "$ref": "#/$defs/VcsCreateRefError" + } + }, + "vcs.init": { + "stream": false, + "payload": { + "$ref": "#/$defs/VcsInitPayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/VcsInitError" + } + }, + "vcs.listRefs": { + "stream": false, + "payload": { + "$ref": "#/$defs/VcsListRefsPayload" + }, + "success": { + "$ref": "#/$defs/VcsListRefsSuccess" + }, + "error": { + "$ref": "#/$defs/VcsCreateRefError" + } + }, + "vcs.pull": { + "stream": false, + "payload": { + "$ref": "#/$defs/ProjectsListIgnoredFilesPayload" + }, + "success": { + "$ref": "#/$defs/VcsPullSuccess" + }, + "error": { + "$ref": "#/$defs/VcsCreateRefError" + } + }, + "vcs.refreshStatus": { + "stream": false, + "payload": { + "$ref": "#/$defs/VcsRefreshStatusPayload" + }, + "success": { + "$ref": "#/$defs/VcsRefreshStatusSuccess" + }, + "error": { + "$ref": "#/$defs/GitPreparePullRequestThreadError" + } + }, + "vcs.removeWorktree": { + "stream": false, + "payload": { + "$ref": "#/$defs/VcsRemoveWorktreePayload" + }, + "success": { + "$ref": "#/$defs/AttachmentsDeleteSuccess" + }, + "error": { + "$ref": "#/$defs/VcsCreateRefError" + } + }, + "vcs.switchRef": { + "stream": false, + "payload": { + "$ref": "#/$defs/VcsSwitchRefPayload" + }, + "success": { + "$ref": "#/$defs/VcsSwitchRefSuccess" + }, + "error": { + "$ref": "#/$defs/VcsCreateRefError" + } + }, + "worktreeSetup.cancel": { + "stream": false, + "payload": { + "$ref": "#/$defs/GuestShareCreatePayload" + }, + "success": { + "$ref": "#/$defs/WorktreeSetupCancelSuccess" + }, + "error": { + "$ref": "#/$defs/EnvironmentAuthorizationErrorEncoded" + } + } + } +} diff --git a/packages/contracts/scripts/export-rpc-json-schema.ts b/packages/contracts/scripts/export-rpc-json-schema.ts new file mode 100644 index 0000000000000..d75df877b57f5 --- /dev/null +++ b/packages/contracts/scripts/export-rpc-json-schema.ts @@ -0,0 +1,316 @@ +// @effect-diagnostics nodeBuiltinImport:off - a codegen script that reads and writes one file. +// Writes schema/rpc.schema.json: every WsRpcGroup method's payload, success and error schema as +// one JSON Schema document with shared $defs, for clients that are not TypeScript (the GPUI +// desktop generates its Rust wire types from it). It describes the encoded (wire) side. +// +// vp run --filter @t3tools/contracts generate rewrite the file +// vp run --filter @t3tools/contracts generate --check fail when the file is stale +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; + +import * as Schema from "effect/Schema"; +import { RpcSchema } from "effect/rpc"; + +import { WsRpcGroup } from "../src/rpc.ts"; + +type Json = + | null + | boolean + | number + | string + | ReadonlyArray + | { readonly [key: string]: Json }; +type JsonObject = { [key: string]: Json }; + +const REF_PREFIX = "#/$defs/"; +// Repeated subtrees smaller than this stay inline: a $ref would cost about as much as the copy. +const MIN_HOIST_BYTES = 200; + +export const SCHEMA_PATH = NodePath.join( + NodeURL.fileURLToPath(new URL("..", import.meta.url)), + "schema", + "rpc.schema.json", +); + +const isObject = (value: Json): value is JsonObject => + typeof value === "object" && value !== null && !Array.isArray(value); + +const pascal = (text: string) => + text + .split(/[^A-Za-z0-9]+/) + .filter(Boolean) + .map((word) => word[0]!.toUpperCase() + word.slice(1)) + .join(""); + +const renameRefs = (node: Json, renames: ReadonlyMap): Json => { + if (Array.isArray(node)) return node.map((child) => renameRefs(child, renames)); + if (!isObject(node)) return node; + const out: JsonObject = {}; + for (const [key, child] of Object.entries(node)) { + out[key] = + key === "$ref" && typeof child === "string" && child.startsWith(REF_PREFIX) + ? REF_PREFIX + + (renames.get(child.slice(REF_PREFIX.length)) ?? child.slice(REF_PREFIX.length)) + : renameRefs(child, renames); + } + return out; +}; + +interface Root { + readonly tag: string; + readonly part: "payload" | "success" | "error"; + readonly schema: Schema.Top; +} + +const rootsOf = () => { + const methods: Array<{ tag: string; stream: boolean; roots: ReadonlyArray }> = []; + for (const rpc of [...WsRpcGroup.requests.values()].sort((a, b) => + a._tag.localeCompare(b._tag), + )) { + const stream = RpcSchema.isStreamSchema(rpc.successSchema); + // A stream method's success is the element schema and its error the stream error; the rpc's + // own error schema is Never. + const success = RpcSchema.isStreamSchema(rpc.successSchema) + ? rpc.successSchema.success + : rpc.successSchema; + const error = RpcSchema.isStreamSchema(rpc.successSchema) + ? rpc.successSchema.error + : rpc.errorSchema; + methods.push({ + tag: rpc._tag, + stream, + roots: [ + { tag: rpc._tag, part: "payload", schema: rpc.payloadSchema as Schema.Top }, + { tag: rpc._tag, part: "success", schema: success as Schema.Top }, + { tag: rpc._tag, part: "error", schema: error as Schema.Top }, + ], + }); + } + return methods; +}; + +const NULL_SCHEMA = '{"type":"null"}'; +const canon = (node: Json) => JSON.stringify(node); +const isBareRef = (node: Json) => + isObject(node) && typeof node.$ref === "string" && Object.keys(node).length === 1; +// Optional-and-nullable fields encode as an anyOf that repeats `null`. +const dedupeAlternatives = (node: Json): Json => { + if (Array.isArray(node)) return node.map(dedupeAlternatives); + if (!isObject(node)) return node; + const out = Object.fromEntries( + Object.entries(node).map(([key, child]) => [key, dedupeAlternatives(child)]), + ); + if (Array.isArray(out.anyOf)) { + const seen = new Set(); + out.anyOf = out.anyOf.filter( + (alternative) => !seen.has(canon(alternative)) && seen.add(canon(alternative)), + ); + } + return out; +}; +const refTo = (name: string): Json => ({ $ref: REF_PREFIX + name }); + +export const buildRpcSchemaDocument = () => { + const methods = rootsOf(); + const defs = new Map(); + + // Named definitions come from identifier annotations. Two contracts can reuse one identifier + // for different shapes, so a clash gets a numeric suffix and that schema's references follow. + const rawShapes = new Map(); + const trees = methods.map((method) => ({ + method, + parts: method.roots.map((root) => { + const document = Schema.toJsonSchemaDocument(root.schema) as unknown as { + schema: Json; + definitions: Record; + }; + const renames = new Map(); + const entries = Object.entries(document.definitions); + for (const [name, value] of entries) { + let candidate = name; + for ( + let n = 2; + rawShapes.has(candidate) && rawShapes.get(candidate) !== canon(value); + n++ + ) { + candidate = `${name}${n}`; + } + if (candidate !== name) renames.set(name, candidate); + rawShapes.set(candidate, canon(value)); + } + for (const [name, value] of entries) { + defs.set(renames.get(name) ?? name, dedupeAlternatives(renameRefs(value, renames))); + } + return { root, schema: dedupeAlternatives(renameRefs(document.schema, renames)) }; + }), + })); + + // Shapes that occur more than once across the whole forest become shared definitions, named + // after the property they most often sit under (`delegatedFrom` -> DelegatedFrom, an array's + // elements -> ThreadsEntry), so a name does not depend on which method came first. + const counts = new Map(); + const contexts = new Map>(); + const tally = (node: Json, context: string) => { + if (Array.isArray(node)) return node.forEach((child) => tally(child, context)); + if (!isObject(node)) return; + const key = canon(node); + if (key.length >= MIN_HOIST_BYTES) { + counts.set(key, (counts.get(key) ?? 0) + 1); + if (context) { + const seen = contexts.get(key) ?? new Map(); + seen.set(context, (seen.get(context) ?? 0) + 1); + contexts.set(key, seen); + } + } + for (const [child, value] of Object.entries(node)) { + if (child === "properties" && isObject(value)) { + Object.entries(value).forEach(([prop, schema]) => tally(schema, pascal(prop))); + } else { + tally(value, child === "items" ? `${context}Entry` : context); + } + } + }; + trees.forEach(({ parts }) => parts.forEach(({ schema }) => tally(schema, ""))); + defs.forEach((body, name) => tally(body, name)); + const nameFor = (key: string) => + [...(contexts.get(key) ?? [["Shape", 0]])].sort( + ([a, x], [b, y]) => y - x || a.localeCompare(b), + )[0]![0]; + + const taken = new Set(defs.keys()); + const claim = (base: string) => { + let name = base; + for (let n = 2; taken.has(name); n++) name = `${base}${n}`; + taken.add(name); + return name; + }; + const byShape = new Map(); + const shared = new Map(); + const inner = new Set(); + + const children = (node: JsonObject): JsonObject => + Object.fromEntries( + Object.entries(node).map(([key, child]) => [ + key, + key === "properties" && isObject(child) + ? Object.fromEntries(Object.entries(child).map(([prop, value]) => [prop, rewrite(value)])) + : rewrite(child), + ]), + ); + // The outermost repeated shape wins. Its body is rewritten once, so shapes repeated only + // inside copies of it are not hoisted separately (unused leftovers are inlined below). + const rewrite = (node: Json): Json => { + if (Array.isArray(node)) return node.map(rewrite); + if (!isObject(node) || isBareRef(node)) return node; + const key = canon(node); + // typify names the payload of a nullable definition after the definition, so hoist only the + // non-null shape and keep the null alternative inline. + const nullable = + Array.isArray(node.anyOf) && + node.anyOf.some((alternative) => canon(alternative) === NULL_SCHEMA); + if (nullable || (counts.get(key) ?? 0) < 2) return children(node); + let name = byShape.get(key); + if (!name) { + name = claim(nameFor(key)); + byShape.set(key, name); + inner.add(name); + shared.set(name, children(node)); + } + return refTo(name); + }; + + const index: Record = {}; + for (const { method, parts } of trees) { + const entry: JsonObject = { stream: method.stream }; + for (const { root, schema } of parts) { + let reference = schema; + if (!isBareRef(schema)) { + // The same shape under two methods (a void payload, a shared error union) shares one def. + const key = canon(schema); + let name = byShape.get(key); + if (!name) { + name = claim(`${pascal(root.tag)}${pascal(root.part)}`); + byShape.set(key, name); + shared.set(name, children(schema as JsonObject)); + } + inner.delete(name); + reference = refTo(name); + } + entry[root.part] = reference; + } + index[method.tag] = entry; + } + for (const [name, body] of defs) { + defs.set(name, isObject(body) ? children(body) : body); + } + for (const [name, body] of shared) defs.set(name, body); + + // A hoisted inner shape used once, from a single definition, reads better inline. + const inline = (node: Json, uses: ReadonlyMap): Json => { + if (Array.isArray(node)) return node.map((child) => inline(child, uses)); + if (!isObject(node)) return node; + const ref = node.$ref; + if (typeof ref === "string" && isBareRef(node)) { + const name = ref.slice(REF_PREFIX.length); + if (inner.has(name) && uses.get(name) === 1) return inline(defs.get(name)!, uses); + return node; + } + return Object.fromEntries( + Object.entries(node).map(([key, child]) => [key, inline(child, uses)]), + ); + }; + for (;;) { + const uses = new Map(); + const scan = (node: Json) => { + if (Array.isArray(node)) return node.forEach(scan); + if (!isObject(node)) return; + if (isBareRef(node)) { + const name = (node.$ref as string).slice(REF_PREFIX.length); + uses.set(name, (uses.get(name) ?? 0) + 1); + return; + } + Object.values(node).forEach(scan); + }; + defs.forEach(scan); + Object.values(index).forEach(scan); + const drop = [...inner].filter((name) => (uses.get(name) ?? 0) <= 1); + if (drop.length === 0) break; + const rewritten = new Map([...defs].map(([name, body]) => [name, inline(body, uses)] as const)); + for (const name of drop) { + rewritten.delete(name); + inner.delete(name); + } + defs.clear(); + rewritten.forEach((body, name) => defs.set(name, body)); + } + + const sorted: JsonObject = {}; + for (const name of [...defs.keys()].sort()) sorted[name] = defs.get(name)!; + return { + $schema: "https://json-schema.org/draft/2020-12/schema", + title: "T3 Code WebSocket RPC (encoded)", + $defs: sorted, + methods: index, + }; +}; + +export const renderRpcSchemaDocument = () => + JSON.stringify(buildRpcSchemaDocument(), null, 1) + "\n"; + +if (import.meta.main) { + const next = renderRpcSchemaDocument(); + if (process.argv.includes("--check")) { + const current = NodeFS.existsSync(SCHEMA_PATH) ? NodeFS.readFileSync(SCHEMA_PATH, "utf8") : ""; + if (current !== next) { + console.error( + "packages/contracts/schema/rpc.schema.json is stale. Run `vp run --filter @t3tools/contracts generate` and commit it.", + ); + process.exitCode = 1; + } + } else { + NodeFS.mkdirSync(NodePath.dirname(SCHEMA_PATH), { recursive: true }); + NodeFS.writeFileSync(SCHEMA_PATH, next); + console.log(`wrote ${NodePath.relative(process.cwd(), SCHEMA_PATH)} (${next.length} bytes)`); + } +} diff --git a/scripts/check-changed.ts b/scripts/check-changed.ts index c5d7979df5800..8e10ad5762ec6 100644 --- a/scripts/check-changed.ts +++ b/scripts/check-changed.ts @@ -460,6 +460,8 @@ const checkChanged = Effect.fn("checkChanged")(function* (input: { const graphql = ["apps/server", "packages/source-control-github"].filter((dir) => files.some((file) => file.startsWith(`${dir}/`)), ); + // CI's Lint job also checks the RPC JSON Schema that the GPUI desktop generates its wire types from. + const rpcSchema = files.some((file) => file.startsWith("packages/contracts/")); const filters = (names: ReadonlyArray) => names.flatMap((name) => ["--filter", name]); const typecheckWorkers = Math.min(concurrency, affected.length); const command = (name: string, args: ReadonlyArray) => ({ name, run: vp(args) }); @@ -479,6 +481,9 @@ const checkChanged = Effect.fn("checkChanged")(function* (input: { ...graphql.map((dir) => command(`graphql (${dir})`, ["run", "--filter", `./${dir}`, "generate", "--check"]), ), + ...(rpcSchema + ? [command("rpc schema", ["run", "--filter", "@t3tools/contracts", "generate", "--check"])] + : []), ...(affected.length > 0 ? [ { diff --git a/vite.config.ts b/vite.config.ts index c92ad24843f91..88f8cffdb891e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -169,6 +169,7 @@ export default defineConfig({ "*.tsbuildinfo", "**/routeTree.gen.ts", "**/*.graphql.ts", + "packages/contracts/schema/**", "packages/source-control-github/schema/**", "apps/server/schema/**", "apps/mobile/android/**", @@ -201,6 +202,7 @@ export default defineConfig({ "*.tsbuildinfo", "**/routeTree.gen.ts", "**/*.graphql.ts", + "packages/contracts/schema/**", "packages/source-control-github/schema/**", "apps/server/schema/**", "apps/mobile/android/**", From fc3bad40940aebfc2c9eaea0fd93b84b27826310 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Wed, 4 Nov 2026 23:58:51 -0500 Subject: [PATCH 03/14] feat(desktop-gpui): attach to an already running server with a pairing token Spawning a second server on a home that another server already owns would race it on the same database. Attaching with a pairing token lets the native client show a running server's data, and it is the same path remote and tunnel servers will need. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/README.md | 7 +++++ apps/desktop-gpui/src/auth.rs | 22 ++++++++++++--- apps/desktop-gpui/src/config.rs | 42 +++++++++++++++++++++++++++++ apps/desktop-gpui/src/connection.rs | 33 ++++++++++++++--------- apps/desktop-gpui/src/main.rs | 30 ++++++++++++++++++--- apps/desktop-gpui/src/server.rs | 5 ++-- 6 files changed, 118 insertions(+), 21 deletions(-) diff --git a/apps/desktop-gpui/README.md b/apps/desktop-gpui/README.md index 4262d1e05ad10..72d22c08abd38 100644 --- a/apps/desktop-gpui/README.md +++ b/apps/desktop-gpui/README.md @@ -23,6 +23,13 @@ The app reads these variables, which the dev runner sets. Without them it shows | `T3CODE_GPUI_SERVER_COMMAND` | Program that starts the server (`node` in dev, `t3` once packaged). | | `T3CODE_GPUI_SERVER_ENTRY` | Optional first argument, the server entry script in dev. | +To see real data, attach to a server that is already running (for example your `vp run dev:desktop`) instead of starting one. That server keeps sole ownership of its database. Mint a one-time pairing token with `node apps/server/src/bin.ts pair --base-dir `, then run the app with: + +| Variable | Meaning | +| --------------------------- | --------------------------------------------------------- | +| `T3CODE_GPUI_SERVER_URL` | HTTP origin of the running server. Takes precedence. | +| `T3CODE_GPUI_PAIRING_TOKEN` | The `Token:` value printed by `pair`. It works only once. | + Other scripts: ```sh diff --git a/apps/desktop-gpui/src/auth.rs b/apps/desktop-gpui/src/auth.rs index 618ab7ffca6f8..20ad54b2b85a9 100644 --- a/apps/desktop-gpui/src/auth.rs +++ b/apps/desktop-gpui/src/auth.rs @@ -79,13 +79,29 @@ struct TicketResult { ticket: String, } -/// RFC 8693 exchange of the desktop bootstrap token for a bearer session. +/// What the app proves itself with: the secret it handed a server it spawned, +/// or a pairing token minted by `t3 pair` for a server it attaches to. +pub enum Credential { + DesktopSecret(String), + Pairing(String), +} + +impl Credential { + fn subject_token(&self) -> String { + match self { + Self::DesktopSecret(secret) => bootstrap_token(secret, now_ms()), + Self::Pairing(token) => token.clone(), + } + } +} + +/// RFC 8693 exchange of a bootstrap credential for a bearer session. pub async fn exchange_bearer( client: &reqwest::Client, http_base: &str, - secret: &str, + credential: &Credential, ) -> Result { - let credential = bootstrap_token(secret, now_ms()); + let credential = credential.subject_token(); let response = client .post(format!("{http_base}/oauth/token")) .form(&[ diff --git a/apps/desktop-gpui/src/config.rs b/apps/desktop-gpui/src/config.rs index 1ff8afe8ce68a..d9bf28e9233b9 100644 --- a/apps/desktop-gpui/src/config.rs +++ b/apps/desktop-gpui/src/config.rs @@ -4,6 +4,34 @@ pub const ENV_PORT: &str = "T3CODE_GPUI_SERVER_PORT"; pub const ENV_HOME: &str = "T3CODE_GPUI_HOME"; pub const ENV_COMMAND: &str = "T3CODE_GPUI_SERVER_COMMAND"; pub const ENV_ENTRY: &str = "T3CODE_GPUI_SERVER_ENTRY"; +pub const ENV_ATTACH_URL: &str = "T3CODE_GPUI_SERVER_URL"; +pub const ENV_PAIRING_TOKEN: &str = "T3CODE_GPUI_PAIRING_TOKEN"; + +/// A server someone else runs (a dev server, `npx t3`, a remote host), reached +/// with a pairing token instead of being spawned. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Attach { + pub http_base: String, + pub pairing_token: String, +} + +impl Attach { + pub fn from_env() -> Option { + Self::from_lookup(|name| std::env::var(name).ok()) + } + + fn from_lookup(get: impl Fn(&str) -> Option) -> Option { + let non_empty = |name| { + get(name) + .map(|v| v.trim().to_owned()) + .filter(|v| !v.is_empty()) + }; + Some(Self { + http_base: non_empty(ENV_ATTACH_URL)?.trim_end_matches('/').to_owned(), + pairing_token: non_empty(ENV_PAIRING_TOKEN)?, + }) + } +} /// How to start the server. The command is data: dev runs `node `, a /// packaged build runs `t3` from PATH with no entry. @@ -83,4 +111,18 @@ mod tests { assert_eq!(Config::from_lookup(lookup(&[(ENV_PORT, "1")])), None); assert_eq!(Config::from_lookup(lookup(&[])), None); } + + #[test] + fn attach_needs_url_and_token() { + let attach = Attach::from_lookup(lookup(&[ + (ENV_ATTACH_URL, "http://127.0.0.1:13773/"), + (ENV_PAIRING_TOKEN, "tok"), + ])) + .expect("attach"); + assert_eq!(attach.http_base, "http://127.0.0.1:13773"); + assert_eq!( + Attach::from_lookup(lookup(&[(ENV_ATTACH_URL, "http://h")])), + None + ); + } } diff --git a/apps/desktop-gpui/src/connection.rs b/apps/desktop-gpui/src/connection.rs index 60f2dbd9eb471..c6c5e658f6e25 100644 --- a/apps/desktop-gpui/src/connection.rs +++ b/apps/desktop-gpui/src/connection.rs @@ -1,7 +1,6 @@ //! The authenticated RPC WebSocket: connect, keep alive, reconnect. -use crate::auth::{self, AuthError}; -use crate::config::Config; +use crate::auth::{self, AuthError, Credential}; use crate::rpc::{Event, PING_FRAME, Session, SubscribeShell}; use crate::shell::ShellState; use crate::status::{Counts, Status, StatusSender, publish}; @@ -43,9 +42,18 @@ impl Backoff { } } -fn socket_url(port: u16, ticket: &str) -> Result { +fn socket_url(http_base: &str, ticket: &str) -> Result { + let ws_base = http_base + .strip_prefix("https://") + .map(|rest| format!("wss://{rest}")) + .or_else(|| { + http_base + .strip_prefix("http://") + .map(|rest| format!("ws://{rest}")) + }) + .ok_or_else(|| format!("unsupported server URL: {http_base}"))?; reqwest::Url::parse_with_params( - &format!("ws://127.0.0.1:{port}/ws"), + &format!("{}/ws", ws_base.trim_end_matches('/')), [ ("orchestrationProtocol", ORCHESTRATION_PROTOCOL_VERSION), ("wsTicket", ticket), @@ -59,14 +67,13 @@ fn socket_url(port: u16, ticket: &str) -> Result { } /// Connects and stays connected until cancelled, reporting through `status`. -pub async fn run(config: &Config, secret: &str, label: &str, status: &StatusSender) { +pub async fn run(http_base: &str, credential: &Credential, label: &str, status: &StatusSender) { let client = auth::http_client(); - let http_base = config.http_base(); let mut bearer: Option = None; let mut backoff = Backoff::new(); loop { publish(status, Status::Connecting); - let failure = match connect(&client, config, &http_base, secret, &mut bearer).await { + let failure = match connect(&client, http_base, credential, &mut bearer).await { Ok(socket) => { publish( status, @@ -90,15 +97,14 @@ type Socket = async fn connect( client: &reqwest::Client, - config: &Config, http_base: &str, - secret: &str, + credential: &Credential, bearer: &mut Option, ) -> Result { let token = match bearer { Some(token) => token.clone(), None => { - let token = auth::exchange_bearer(client, http_base, secret) + let token = auth::exchange_bearer(client, http_base, credential) .await .map_err(|error| error.to_string())?; *bearer = Some(token.clone()); @@ -114,7 +120,7 @@ async fn connect( return Err(error.to_string()); } }; - let url = socket_url(config.port, &ticket)?; + let url = socket_url(http_base, &ticket)?; let (socket, _) = tokio_tungstenite::connect_async(url.as_str()) .await .map_err(|error| error.to_string())?; @@ -223,9 +229,12 @@ mod tests { #[test] fn socket_url_carries_protocol_and_escaped_ticket() { - let url = socket_url(13773, "a b&c").expect("url"); + let url = socket_url("http://127.0.0.1:13773", "a b&c").expect("url"); assert_eq!(url.scheme(), "ws"); assert_eq!(url.path(), "/ws"); + let remote = socket_url("https://host.example/", "t").expect("url"); + assert_eq!(remote.scheme(), "wss"); + assert_eq!(remote.path(), "/ws"); let pairs: Vec<_> = url.query_pairs().into_owned().collect(); assert!(pairs.contains(&("orchestrationProtocol".into(), "2".into()))); assert!(pairs.contains(&("wsTicket".into(), "a b&c".into()))); diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs index c3cbc50c93bbb..3ee9c7d1f861e 100644 --- a/apps/desktop-gpui/src/main.rs +++ b/apps/desktop-gpui/src/main.rs @@ -7,7 +7,7 @@ mod shell; mod status; mod wire; -use config::Config; +use config::{Attach, Config}; use gpui::{ App, Bounds, Context, SharedString, TitlebarOptions, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size, @@ -42,9 +42,31 @@ fn main() { .build() .expect("failed to start the async runtime"); let (status_tx, status_rx) = async_channel::unbounded(); - let supervisor = Config::from_env() - .map(|config| Rc::new(Supervisor::start(runtime.handle(), config, status_tx))); - let initial = if supervisor.is_some() { + let attach = Attach::from_env(); + let supervisor = if attach.is_some() { + None + } else { + Config::from_env().map(|config| { + Rc::new(Supervisor::start( + runtime.handle(), + config, + status_tx.clone(), + )) + }) + }; + let initial = if let Some(attach) = attach { + runtime.spawn(async move { + let client = auth::http_client(); + match server::wait_until_ready(&client, &attach.http_base).await { + Ok(label) => { + let credential = auth::Credential::Pairing(attach.pairing_token); + connection::run(&attach.http_base, &credential, &label, &status_tx).await; + } + Err(message) => status::publish(&status_tx, Status::Failed(message)), + } + }); + Status::Connecting + } else if supervisor.is_some() { Status::Starting } else { Status::NotConnected diff --git a/apps/desktop-gpui/src/server.rs b/apps/desktop-gpui/src/server.rs index 3d4873b2c3ba8..e96963438d941 100644 --- a/apps/desktop-gpui/src/server.rs +++ b/apps/desktop-gpui/src/server.rs @@ -165,7 +165,7 @@ struct Descriptor { } /// Polls until the descriptor answers, returning the environment label. -async fn wait_until_ready(client: &reqwest::Client, http_base: &str) -> Result { +pub async fn wait_until_ready(client: &reqwest::Client, http_base: &str) -> Result { let url = format!("{http_base}{READINESS_PATH}"); let deadline = Instant::now() + READINESS_TIMEOUT; loop { @@ -218,7 +218,8 @@ async fn session(config: &Config, secret: &str, status: &StatusSender) -> String let client = auth::http_client(); match wait_until_ready(&client, &config.http_base()).await { Ok(label) => { - connection::run(config, secret, &label, status).await; + let credential = auth::Credential::DesktopSecret(secret.to_owned()); + connection::run(&config.http_base(), &credential, &label, status).await; unreachable_session() } Err(message) => message, From 7c525bcfbd2399edacfdd1b7fc368d58e7a7d8c6 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 00:20:18 -0500 Subject: [PATCH 04/14] feat(desktop-gpui): render the main screen skeleton with real threads A status line can't show whether the generated types hold up against real data. gpui-component supplies native inputs, buttons and theming, so the sidebar, empty state and composer render without a webview. That requires the crates.io gpui-pre build it targets. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/Cargo.lock | 2201 ++++++++++++++++++++++----- apps/desktop-gpui/Cargo.toml | 8 +- apps/desktop-gpui/README.md | 4 +- apps/desktop-gpui/src/connection.rs | 9 +- apps/desktop-gpui/src/main.rs | 54 +- apps/desktop-gpui/src/shell.rs | 56 + apps/desktop-gpui/src/status.rs | 39 +- apps/desktop-gpui/src/ui.rs | 235 +++ 8 files changed, 2187 insertions(+), 419 deletions(-) create mode 100644 apps/desktop-gpui/src/ui.rs diff --git a/apps/desktop-gpui/Cargo.lock b/apps/desktop-gpui/Cargo.lock index d2c4c67b10c26..1e3e164a6a4d0 100644 --- a/apps/desktop-gpui/Cargo.lock +++ b/apps/desktop-gpui/Cargo.lock @@ -119,6 +119,23 @@ dependencies = [ "libc", ] +[[package]] +name = "annotate-snippets" +version = "0.12.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a5b0a5ab8561c603f16c3b78c24a386b1f70e7fc1b1b1e8c15738828e4a176" +dependencies = [ + "anstyle", + "memchr", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + [[package]] name = "anyhow" version = "1.0.104" @@ -131,6 +148,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed" +[[package]] +name = "arc-swap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e" +dependencies = [ + "rustversion", +] + [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -142,6 +168,12 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "arrayref" version = "0.3.9" @@ -363,9 +395,9 @@ dependencies = [ "log", "num-rational", "num-traits", - "pastey 0.1.1", + "pastey", "rayon", - "thiserror", + "thiserror 2.0.21", "v_frame", "y4m", ] @@ -431,6 +463,12 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "base62" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a647dce9631f152a99ec16cfc09267ae55fe280d5dc288964027d458076323" + [[package]] name = "base64" version = "0.22.1" @@ -445,18 +483,18 @@ checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bit-set" -version = "0.11.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d87354e4229f54a44f7bf2435906a4656dba36026ab6eaca629a2c436a691c" +checksum = "09ec2f926cc3060f09db9ebc5b52823d85268d24bb917e472c0c4bea35780a7d" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.10.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5727b15fa97d4f4fee0a3b7c3d550ed0269f54329207b86388de918604e31269" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" [[package]] name = "bit_field" @@ -485,6 +523,12 @@ dependencies = [ "no_std_io2", ] +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -543,6 +587,17 @@ dependencies = [ "piper", ] +[[package]] +name = "bstr" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" +dependencies = [ + "memchr", + "regex-automata", + "serde_core", +] + [[package]] name = "built" version = "0.8.1" @@ -707,6 +762,36 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "libc", + "objc", +] + [[package]] name = "codespace" version = "0.0.1-alpha.3" @@ -731,16 +816,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "collections" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ - "gpui_util", - "indexmap", - "rustc-hash 2.1.3", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -791,6 +866,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -807,6 +892,19 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + [[package]] name = "core-graphics" version = "0.24.0" @@ -814,12 +912,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ "bitflags 2.13.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.10.1", + "core-graphics-types 0.2.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-helmer-fork" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types", "libc", ] +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.2.0" @@ -827,7 +949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.13.2", - "core-foundation", + "core-foundation 0.10.1", "libc", ] @@ -837,12 +959,18 @@ version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" dependencies = [ - "core-foundation", - "core-graphics", + "core-foundation 0.10.1", + "core-graphics 0.24.0", "foreign-types", "libc", ] +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + [[package]] name = "core_maths" version = "0.1.1" @@ -1012,16 +1140,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "derive_refineable" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "digest" version = "0.10.7" @@ -1045,13 +1163,34 @@ dependencies = [ "ctutils", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys", + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.6", + "windows-sys 0.48.0", ] [[package]] @@ -1062,10 +1201,16 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.3", "windows-sys 0.61.2", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + [[package]] name = "dispatch2" version = "0.3.1" @@ -1150,12 +1295,55 @@ dependencies = [ "winreg", ] +[[package]] +name = "encoding_rs" +version = "0.8.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89253175e704ab869b04f9db224d38630e0211a53841a79181deae0889e169f5" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22" +dependencies = [ + "encoding_rs", +] + [[package]] name = "endi" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enum-iterator" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4549325971814bda7a44061bf3fe7e487d447cba01e4220a4b454d630d7a016" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -1336,6 +1524,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "flume" version = "0.12.0" @@ -1347,6 +1544,12 @@ dependencies = [ "spin 0.9.9", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.2.0" @@ -1447,6 +1650,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.34" @@ -1599,6 +1812,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gif" version = "0.14.2" @@ -1626,11 +1849,41 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "globset" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98b0d85522dc0a824cfebbc9594ff4a9b289c568bea0370319dfbdf20ccdd2d" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "glow" -version = "0.18.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "263218b0ba2b0715c3370fb04674f5f8aa331594b521c94ff0a8e4a8472d1386" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" dependencies = [ "js-sys", "slotmap", @@ -1657,94 +1910,211 @@ dependencies = [ "hashbrown 0.16.1", "log", "presser", - "thiserror", + "thiserror 2.0.21", "windows 0.62.2", ] [[package]] -name = "gpui" -version = "0.2.2" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-base" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff6377e30f0c857681662e7edc7286bf30f5606033390401b0706d7bd930833" dependencies = [ - "accesskit", + "aho-corasick", "anyhow", + "ashpd", "async-channel", - "async-task", - "backtrace", - "bitflags 2.13.2", "chrono", - "collections", - "derive_more", - "embed-resource", - "etagere", + "data-url", "futures", - "getrandom 0.3.4", - "gpui_macros", - "gpui_shared_string", - "gpui_util", - "heapless", - "http_client", - "icu_segmenter", - "image", - "inventory", - "itertools", - "libc", - "log", - "lyon", - "num_cpus", - "objc2-core-foundation", - "objc2-core-video", - "parking", - "parking_lot", - "pin-project", - "pollster", - "profiling", - "rand 0.9.5", + "gpui-pre", + "gpui-pre-macros", + "gpui-pre-sum-tree", + "html5ever", + "instant", + "itertools 0.13.0", + "lsp-types", + "markdown", + "markup5ever_rcdom", + "num-traits", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", "raw-window-handle", - "refineable", "regex", - "resvg", - "scheduler", + "ropey", "schemars 1.2.3", - "seahash", "serde", "serde_json", - "slotmap", "smallvec", - "spin 0.10.1", - "strum", - "sum_tree", - "taffy", - "thiserror", - "ttf-parser", + "smol", + "tracing", + "unicode-linebreak", "unicode-segmentation", - "url", - "usvg", - "uuid", - "waker-fn", - "windows 0.62.2", - "zed-scap", + "web-time", + "windows 0.58.0", ] [[package]] -name = "gpui_apple" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ +name = "gpui-component" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bbfdba0efa392190f2fd446ec311325302eb047d5ce45dc1bcf189cb18f5eb" +dependencies = [ + "anyhow", + "chrono", + "core-text", + "enum-iterator", + "gpui-base", + "gpui-component-macros", + "gpui-kit-assets", + "gpui-pre", + "gpui-pre-macros", + "gpui-pre-sum-tree", + "instant", + "itertools 0.13.0", + "log", + "lsp-types", + "markdown", + "notify", + "num-traits", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", + "once_cell", + "paste", + "raw-window-handle", + "regex", + "resvg 0.45.1", + "ropey", + "rust-i18n", + "schemars 1.2.3", + "serde", + "serde_json", + "serde_repr", + "similar", + "smallvec", + "smol", + "tracing", + "unicode-segmentation", + "unicode-width", + "uuid", + "windows 0.58.0", +] + +[[package]] +name = "gpui-component-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ac1d2ca5352973e291b3316c027e603b1e7334755341f44c8231f85fde627a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpui-kit-assets" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956a9864880e1f230e274a74cea8006e5018b8af383ad7bbd30b6b18254a97a9" +dependencies = [ + "anyhow", + "gpui-pre", + "gpui-pre-reqwest", + "log", + "rust-embed", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "gpui-pre" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9303c771abf2acff2a96d706f69d9a82088c4770173a07cce88f0002431d887d" +dependencies = [ + "accesskit", + "anyhow", + "async-channel", + "async-task", + "backtrace", + "bitflags 2.13.2", + "chrono", + "derive_more", + "embed-resource", + "etagere", + "futures", + "getrandom 0.3.4", + "gpui-pre-collections", + "gpui-pre-http-client", + "gpui-pre-macros", + "gpui-pre-refineable", + "gpui-pre-scheduler", + "gpui-pre-shared-string", + "gpui-pre-sum-tree", + "gpui-pre-util", + "heapless", + "icu_segmenter", + "image", + "inventory", + "itertools 0.14.0", + "libc", + "log", + "lyon", + "num_cpus", + "objc2-core-foundation", + "objc2-core-video", + "parking", + "parking_lot", + "pin-project", + "pollster", + "profiling", + "rand 0.9.5", + "raw-window-handle", + "regex", + "resvg 0.46.0", + "schemars 1.2.3", + "seahash", + "serde", + "serde_json", + "slotmap", + "smallvec", + "spin 0.10.1", + "strum", + "taffy", + "thiserror 2.0.21", + "ttf-parser", + "unicode-segmentation", + "url", + "usvg 0.46.0", + "uuid", + "waker-fn", + "windows 0.62.2", + "zed-scap", +] + +[[package]] +name = "gpui-pre-apple" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c0bea8ffa563d3a1ada533e148bb55be7fb873c178979e889a3d1204f8e1b91" +dependencies = [ "anyhow", "async-task", "block2 0.6.2", "cbindgen", - "collections", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", - "core-graphics", + "core-graphics 0.24.0", "core-text", "dispatch2", "etagere", "futures", - "gpui", - "gpui_util", + "gpui-pre", + "gpui-pre-collections", + "gpui-pre-util", "image", "libc", "log", @@ -1761,20 +2131,61 @@ dependencies = [ "parking_lot", "pathfinder_geometry", "smallvec", - "thiserror", + "thiserror 2.0.21", "zed-font-kit", ] [[package]] -name = "gpui_ios" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-collections" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06882d54d40681ae3b145af9ef8a4021c2bf9967cc0a9fcf0cc50f003bf3cdf3" +dependencies = [ + "gpui-pre-util", + "indexmap", + "rustc-hash 2.1.3", +] + +[[package]] +name = "gpui-pre-derive-refineable" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3999591b50cfd74f1734baf97fe802cce5f282d60d85b908e3393d534fbd50fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpui-pre-http-client" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00ff0e68341f52c5903182d7e2e2d7dc247d87b0d9947ce3564e453720347a0" +dependencies = [ + "anyhow", + "bytes", + "futures", + "http", + "http-body", + "parking_lot", + "serde", + "serde_json", + "serde_urlencoded", + "url", +] + +[[package]] +name = "gpui-pre-ios" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "326382c3b3f004617ac0f61338594c00f053b08b7875f3486ea0f221e9b20a75" dependencies = [ "anyhow", "block2 0.6.2", "futures", - "gpui", - "gpui_apple", + "gpui-pre", + "gpui-pre-apple", "log", "objc2 0.6.5", "objc2-core-foundation", @@ -1787,19 +2198,20 @@ dependencies = [ ] [[package]] -name = "gpui_linux" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-linux" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cac5cbd3485c1e3db8eafd5dc69019188f5cb0a39846946af772efa386f624" dependencies = [ "accesskit", "anyhow", "bytemuck", "calloop", - "collections", "futures", - "gpui", - "gpui_util", - "http_client", + "gpui-pre", + "gpui-pre-collections", + "gpui-pre-http-client", + "gpui-pre-util", "libc", "log", "notify-rust", @@ -1814,27 +2226,29 @@ dependencies = [ ] [[package]] -name = "gpui_macos" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-macos" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7410cfdaa1fa55a414346cc492d18ce340e3a8b6d0ac3213b984ed690c82b92" dependencies = [ "accesskit", "accesskit_macos", "anyhow", "block2 0.6.2", - "collections", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", - "core-graphics", + "core-graphics 0.24.0", "ctor", "dispatch2", "foreign-types", "futures", - "gpui", - "gpui_apple", - "gpui_util", + "gpui-pre", + "gpui-pre-apple", + "gpui-pre-collections", + "gpui-pre-scheduler", + "gpui-pre-util", "image", - "itertools", + "itertools 0.14.0", "log", "objc2 0.6.5", "objc2-app-kit 0.3.2", @@ -1846,7 +2260,6 @@ dependencies = [ "objc2-user-notifications", "parking_lot", "raw-window-handle", - "scheduler", "semver", "smallvec", "strum", @@ -1854,35 +2267,115 @@ dependencies = [ ] [[package]] -name = "gpui_macros" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-macros" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1c52959baa6834db3183e86affe70242ca90b4fc64ed2ae6dd689a799ce78e" dependencies = [ "heck 0.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.119", ] [[package]] -name = "gpui_platform" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-platform" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175f55bd6a80ea3bb0cf44d9ba454a3312381d66e702b3b5e99f73a7ca1738ee" dependencies = [ "anyhow", "console_error_panic_hook", - "gpui", - "gpui_ios", - "gpui_linux", - "gpui_macos", - "gpui_web", - "gpui_windows", + "gpui-pre", + "gpui-pre-ios", + "gpui-pre-linux", + "gpui-pre-macos", + "gpui-pre-web", + "gpui-pre-windows", ] [[package]] -name = "gpui_shared_string" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-refineable" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "097375d98fecafd6e00ec13c7711709cc6d827ccad385694ce9e2c2f570c02a5" +dependencies = [ + "gpui-pre-derive-refineable", +] + +[[package]] +name = "gpui-pre-reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05be23908e707966824f8c51a609904b7f30739e8d915e120a53c1b995ba964c" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry 0.4.0", +] + +[[package]] +name = "gpui-pre-scheduler" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c3c9f76341cdf03801699d68876915b42d5858e80c52a141ebfc350358147b" +dependencies = [ + "async-task", + "backtrace", + "chrono", + "flume", + "futures", + "parking_lot", + "rand 0.9.5", + "wasm_thread", + "web-time", +] + +[[package]] +name = "gpui-pre-shared-string" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781aaf9a9c19e3367b94100c7b1a17283721766bc890909ce8aa34e8c5b1a99a" dependencies = [ "schemars 1.2.3", "serde", @@ -1890,9 +2383,22 @@ dependencies = [ ] [[package]] -name = "gpui_util" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-sum-tree" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cb18765472fb2c64b7e6374f9a36f512ce25b1ccb3662fff51788564107eb3" +dependencies = [ + "gpui-pre-ztracing", + "heapless", + "rayon", + "tracing", +] + +[[package]] +name = "gpui-pre-util" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49036ad9887034f8d6652b65761ee1c1fdbd7d66be33a6088245fae54dc643e3" dependencies = [ "anyhow", "log", @@ -1900,22 +2406,23 @@ dependencies = [ ] [[package]] -name = "gpui_web" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-web" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad51fcf6e251d45b092fd4a98a26325eb9cf6ad4e6228b0c6bc50672db9e0563" dependencies = [ "anyhow", "async-channel", "console_error_panic_hook", "futures", - "gpui", - "gpui_wgpu", - "http_client", + "gpui-pre", + "gpui-pre-http-client", + "gpui-pre-scheduler", + "gpui-pre-wgpu", "js-sys", "log", "parking_lot", "raw-window-handle", - "scheduler", "smallvec", "unicode-properties", "unicode-script", @@ -1929,18 +2436,19 @@ dependencies = [ ] [[package]] -name = "gpui_wgpu" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-wgpu" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "211cf352de1a0b0c9bea3f105df5572ce1d2c899389b924a888e5af36b50f93d" dependencies = [ "anyhow", "bytemuck", - "collections", "cosmic-text", "etagere", - "gpui", - "gpui_util", - "itertools", + "gpui-pre", + "gpui-pre-collections", + "gpui-pre-util", + "itertools 0.14.0", "log", "parking_lot", "profiling", @@ -1954,32 +2462,78 @@ dependencies = [ ] [[package]] -name = "gpui_windows" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "gpui-pre-windows" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5141a3db59bab761a16ab19529fe4117a86e91922e8fc00af6dfc4829cb1744d" dependencies = [ "accesskit", "accesskit_windows", "anyhow", - "collections", "dunce", "etagere", "futures", - "gpui", - "gpui_util", + "gpui-pre", + "gpui-pre-collections", + "gpui-pre-scheduler", + "gpui-pre-util", "image", - "itertools", + "itertools 0.14.0", "log", "parking_lot", "rand 0.9.5", "raw-window-handle", - "scheduler", "smallvec", "uuid", "windows 0.62.2", "windows-core 0.62.2", "windows-numerics 0.3.1", - "windows-registry", + "windows-registry 0.6.1", +] + +[[package]] +name = "gpui-pre-ztracing" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ac9274f4fb36ce6bf128f9b48032196a3120cb858503af33ced6c21366cf2f7" +dependencies = [ + "gpui-pre-ztracing-macro", + "tracing", +] + +[[package]] +name = "gpui-pre-ztracing-macro" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b18b06f5026b3d312488eacfdddbe78f82b4bf32bb2d2bc3186e31e552b797" + +[[package]] +name = "granit-parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e20f99e46474f56bd905c56e817ebddcf377a611f94c53ac4649e4d3fa3c0cd0" +dependencies = [ + "arraydeque", + "smallvec", +] + +[[package]] +name = "h2" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] @@ -2097,6 +2651,20 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "html5ever" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "http" version = "1.5.0" @@ -2130,23 +2698,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http_client" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ - "anyhow", - "bytes", - "futures", - "http", - "http-body", - "parking_lot", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - [[package]] name = "httparse" version = "1.10.1" @@ -2170,7 +2721,9 @@ checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" dependencies = [ "atomic-waker", "bytes", + "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -2191,6 +2744,7 @@ dependencies = [ "hyper", "hyper-util", "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", "tower-service", @@ -2394,6 +2948,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "484be5657551d7868d67587df967fb4fcbe9d266a761cc855aaa148fda0b3cc7" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.25.10" @@ -2404,7 +2974,7 @@ dependencies = [ "byteorder-lite", "color_quant", "exr", - "gif", + "gif 0.14.2", "image-webp", "moxcms", "num-traits", @@ -2413,8 +2983,8 @@ dependencies = [ "ravif", "rayon", "tiff", - "zune-core", - "zune-jpeg", + "zune-core 0.5.3", + "zune-jpeg 0.5.15", ] [[package]] @@ -2427,6 +2997,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + [[package]] name = "imagesize" version = "0.14.0" @@ -2451,6 +3027,26 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inotify" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" +dependencies = [ + "bitflags 2.13.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" +dependencies = [ + "libc", +] + [[package]] name = "inout" version = "0.1.4" @@ -2461,6 +3057,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "interpolate_name" version = "0.2.4" @@ -2487,6 +3095,24 @@ version = "2.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -2514,7 +3140,7 @@ dependencies = [ "jni-sys 0.4.1", "log", "simd_cesu8", - "thiserror", + "thiserror 2.0.21", "walkdir", "windows-link 0.2.1", ] @@ -2598,6 +3224,37 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kqueue" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +dependencies = [ + "bitflags 2.13.2", + "libc", +] + +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + [[package]] name = "kurbo" version = "0.13.1" @@ -2732,6 +3389,19 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" +[[package]] +name = "lsp-types" +version = "0.97.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53353550a17c04ac46c585feb189c2db82154fc84b79c7a66c96c2c644f66071" +dependencies = [ + "bitflags 1.3.2", + "fluent-uri", + "serde", + "serde_json", + "serde_repr", +] + [[package]] name = "lyon" version = "1.0.19" @@ -2784,6 +3454,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + [[package]] name = "mac-notification-sys" version = "0.6.15" @@ -2805,20 +3481,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" [[package]] -name = "macro_rules_attribute" -version = "0.2.3" +name = "malloc_buf" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ae8f6d608c795738406608304d30a2dfbdc8e58e44f7ba43236da5208ded3c" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" dependencies = [ - "macro_rules_attribute-proc_macro", - "pastey 0.2.3", + "libc", ] [[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.2.3" +name = "markdown" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5cab8f2cadc416a82d2e783a1946388b31654d391d1c7d92cc1f03e295b1deb" +dependencies = [ + "serde", + "unicode-id", +] + +[[package]] +name = "markup5ever" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +dependencies = [ + "log", + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "markup5ever_rcdom" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc04a4c58212d57930a24bf47d3fa87485264a3a054e9c10e042eb373573ad3c" +checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" +dependencies = [ + "html5ever", + "markup5ever", + "tendril", + "xml5ever", +] [[package]] name = "maybe-rayon" @@ -2864,6 +3569,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2891,6 +3612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] @@ -2905,10 +3627,17 @@ dependencies = [ "pxfm", ] +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + [[package]] name = "naga" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a616d2fb8c89516ac2723a581f69d6c18576046bed761bd6b305e5618e6ae130" dependencies = [ "arrayvec", "bit-set", @@ -2926,19 +3655,20 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror", + "thiserror 2.0.21", "unicode-ident", ] [[package]] name = "naga-types" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590afbf58a6f4f62873cd5cff4468061844bafa1cdf399cc954537c22d768d49" dependencies = [ "hashbrown 0.17.1", "indexmap", "rustc-hash 1.1.0", - "thiserror", + "thiserror 2.0.21", ] [[package]] @@ -2965,6 +3695,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "8.0.0" @@ -2980,6 +3716,34 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "normpath" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11ce00d2594068e8a27c9146fdc5cf9f3ac38eb42c7cd34d05fea618873ac9f" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "notify" +version = "8.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f812702fbc75d35416d3f884e04925be7e9498f4eceea2e24ba34559284674" +dependencies = [ + "bitflags 2.13.2", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "objc2-core-foundation", + "objc2-core-services", + "walkdir", + "windows-sys 0.61.2", +] + [[package]] name = "notify-rust" version = "4.18.2" @@ -2994,6 +3758,15 @@ dependencies = [ "zbus", ] +[[package]] +name = "notify-types" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a437a9308232ce794b1e7fdf540d38ea37efc3c73492640c13b4c8568b8f108" +dependencies = [ + "bitflags 2.13.2", +] + [[package]] name = "ntapi" version = "0.4.3" @@ -3121,6 +3894,27 @@ dependencies = [ "libc", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -3156,8 +3950,8 @@ dependencies = [ "block2 0.5.1", "libc", "objc2 0.5.3", - "objc2-core-data", - "objc2-core-image", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-foundation 0.2.2", "objc2-quartz-core 0.2.2", ] @@ -3170,12 +3964,31 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.13.2", "block2 0.6.2", + "libc", "objc2 0.6.5", + "objc2-cloud-kit", + "objc2-core-data 0.3.2", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.2", + "objc2-core-text", + "objc2-core-video", "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", "objc2-uniform-type-identifiers", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -3188,6 +4001,17 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" @@ -3226,6 +4050,26 @@ dependencies = [ "objc2-metal 0.2.2", ] +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-services" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583300ad934cba24ff5292aee751ecc070f7ca6b39a574cc21b7b5e588e06a0b" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-core-text" version = "0.3.2" @@ -3233,7 +4077,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ "bitflags 2.13.2", + "objc2 0.6.5", "objc2-core-foundation", + "objc2-core-graphics", ] [[package]] @@ -3389,6 +4235,24 @@ dependencies = [ "objc2-foundation 0.3.2", ] +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "object" version = "0.40.0" @@ -3407,7 +4271,8 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "oo7" version = "0.6.0" -source = "git+https://github.com/zed-industries/oo7?rev=731c1215edb6905876c6137ab300428c7b8d7c13#731c1215edb6905876c6137ab300428c7b8d7c13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f2bfed90f1618b4b48dcad9307f25e14ae894e2949642c87c351601d62cebd" dependencies = [ "aes", "ashpd", @@ -3510,12 +4375,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" -[[package]] -name = "pastey" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" - [[package]] name = "pathfinder_geometry" version = "0.5.1" @@ -3527,29 +4386,67 @@ dependencies = [ ] [[package]] -name = "pathfinder_simd" -version = "0.5.6" +name = "pathfinder_simd" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "rustc_version", + "phf_generator", + "phf_shared", ] [[package]] -name = "pbkdf2" -version = "0.12.2" +name = "phf_generator" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "digest 0.10.7", - "hmac 0.12.1", + "phf_shared", + "rand 0.8.8", ] [[package]] -name = "percent-encoding" -version = "2.3.2" +name = "phf_shared" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] [[package]] name = "pico-args" @@ -3696,6 +4593,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "presser" version = "0.3.1" @@ -3816,7 +4719,7 @@ dependencies = [ "rustc-hash 2.1.3", "rustls", "socket2", - "thiserror", + "thiserror 2.0.21", "tokio", "tracing", "web-time", @@ -3839,7 +4742,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.21", "tinyvec", "tracing", "web-time", @@ -3993,7 +4896,7 @@ dependencies = [ "built", "cfg-if", "interpolate_name", - "itertools", + "itertools 0.14.0", "libc", "libfuzzer-sys", "log", @@ -4007,7 +4910,7 @@ dependencies = [ "rand 0.9.5", "rand_chacha 0.9.0", "simd_helpers", - "thiserror", + "thiserror 2.0.21", "v_frame", "wasm-bindgen", ] @@ -4111,6 +5014,17 @@ dependencies = [ "bitflags 2.13.2", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.3" @@ -4118,7 +5032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" dependencies = [ "libredox", - "thiserror", + "thiserror 2.0.21", ] [[package]] @@ -4141,14 +5055,6 @@ dependencies = [ "syn 3.0.9", ] -[[package]] -name = "refineable" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ - "derive_refineable", -] - [[package]] name = "regex" version = "1.13.1" @@ -4231,21 +5137,38 @@ dependencies = [ "web-sys", ] +[[package]] +name = "resvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "gif 0.13.3", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes 0.15.3", + "tiny-skia", + "usvg 0.45.1", + "zune-jpeg 0.4.21", +] + [[package]] name = "resvg" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" dependencies = [ - "gif", + "gif 0.14.2", "image-webp", "log", "pico-args", "rgb", - "svgtypes", + "svgtypes 0.16.2", "tiny-skia", - "usvg", - "zune-jpeg", + "usvg 0.46.0", + "zune-jpeg 0.5.15", ] [[package]] @@ -4271,6 +5194,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ropey" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4045a00dc327d084a2bbf126976e14125b54f23bd30511d45b842eba76c52d74" +dependencies = [ + "str_indices", +] + [[package]] name = "roxmltree" version = "0.20.0" @@ -4286,6 +5218,91 @@ dependencies = [ "memchr", ] +[[package]] +name = "rust-embed" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19afa5b4b6a611de00bd1bdae6ae6f39084c9399f0679c3f52d8469cf335cc23" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d8afda6374eac59e066abee06d265247ebbaf3006cf878e2879e8356e34053" +dependencies = [ + "mime_guess", + "proc-macro2", + "quote", + "rust-embed-utils", + "shellexpand", + "syn 2.0.119", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d84e8ba78bd384263e5922f084cbe1b081c3b7e69add59c8fb097b879ba968a" +dependencies = [ + "globset", + "sha2 0.11.1", + "walkdir", +] + +[[package]] +name = "rust-i18n" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8502e73e6461c738659455c5d2cf95bc132760a6f203c717e66ee72b78906d7f" +dependencies = [ + "globwalk", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec", +] + +[[package]] +name = "rust-i18n-macro" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54a28dd9d46108d56b9f0c85b220908844233e561c03087890325104f90f2c6" +dependencies = [ + "glob", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "syn 2.0.119", +] + +[[package]] +name = "rust-i18n-support" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e806c2a9701cbd6043992cd935c30cb1fdb128fa15e7b506f3b46f97045737c0" +dependencies = [ + "arc-swap", + "base62", + "foldhash", + "globwalk", + "itertools 0.11.0", + "normpath", + "serde", + "serde-saphyr", + "serde_json", + "siphasher", + "toml 0.8.23", + "triomphe", +] + [[package]] name = "rustc-demangle" version = "0.1.28" @@ -4334,6 +5351,7 @@ checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "aws-lc-rs", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -4352,6 +5370,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.15.1" @@ -4368,7 +5395,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" dependencies = [ - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "jni", "log", @@ -4449,22 +5476,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "scheduler" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ - "async-task", - "backtrace", - "chrono", - "flume", - "futures", - "parking_lot", - "rand 0.9.5", - "wasm_thread", - "web-time", -] - [[package]] name = "schemars" version = "0.8.22" @@ -4521,6 +5532,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "screencapturekit" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e" +dependencies = [ + "screencapturekit-sys", +] + +[[package]] +name = "screencapturekit-sys" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60" +dependencies = [ + "block", + "dispatch", + "objc", + "objc-foundation", + "objc_id", + "once_cell", +] + [[package]] name = "seahash" version = "4.1.0" @@ -4534,7 +5568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags 2.13.2", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -4576,6 +5610,23 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-saphyr" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8050abb251097357e24aff63ba2c52a6309ecb7d23a5474023df960a02694d8" +dependencies = [ + "annotate-snippets", + "base64 0.23.1", + "encoding_rs_io", + "granit-parser", + "nohash-hasher", + "num-traits", + "serde_core", + "smallvec", + "zmij", +] + [[package]] name = "serde_bytes" version = "0.11.19" @@ -4735,6 +5786,15 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "shellexpand" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" +dependencies = [ + "dirs 6.0.0", +] + [[package]] name = "shlex" version = "2.0.1" @@ -4782,6 +5842,16 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +dependencies = [ + "bstr", + "unicode-segmentation", +] + [[package]] name = "simplecss" version = "0.2.2" @@ -4913,6 +5983,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + [[package]] name = "strict-num" version = "0.1.1" @@ -4922,6 +5998,31 @@ dependencies = [ "float-cmp", ] +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + [[package]] name = "strum" version = "0.28.0" @@ -4949,30 +6050,29 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "sum_tree" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ - "heapless", - "rayon", - "tracing", - "ztracing", -] - [[package]] name = "svg_fmt" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo 0.11.3", + "siphasher", +] + [[package]] name = "svgtypes" version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f12b12995e3638c70618c1facb1ce65302f6a6f706b675f07c58a5d7391120e" dependencies = [ - "kurbo", + "kurbo 0.13.1", "siphasher", ] @@ -5052,6 +6152,27 @@ dependencies = [ "windows 0.57.0", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "t3-desktop-gpui" version = "0.1.0" @@ -5059,8 +6180,10 @@ dependencies = [ "async-channel", "futures-util", "getrandom 0.4.3", - "gpui", - "gpui_platform", + "gpui-component", + "gpui-kit-assets", + "gpui-pre", + "gpui-pre-platform", "hex", "hmac 0.13.0", "libc", @@ -5087,13 +6210,25 @@ dependencies = [ "smallvec", ] +[[package]] +name = "tao-core-video-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "objc", +] + [[package]] name = "tauri-winrt-notification" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37a6c354fd28fc9e322ed9bd47e3959576dad28c9d58ea1cf888cce1c7ccb36" dependencies = [ - "thiserror", + "thiserror 2.0.21", "windows 0.62.2", "windows-version", ] @@ -5111,6 +6246,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -5120,13 +6266,33 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -5151,7 +6317,7 @@ dependencies = [ "half", "quick-error", "weezl", - "zune-jpeg", + "zune-jpeg 0.5.15", ] [[package]] @@ -5253,6 +6419,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-socks" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e2948f60dbe26b35f2c7fb74ac2854c1fddded0fe9d7548fcc674a246f7615" +dependencies = [ + "either", + "futures-util", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.30.0" @@ -5262,7 +6440,21 @@ dependencies = [ "futures-util", "log", "tokio", - "tungstenite", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", ] [[package]] @@ -5434,6 +6626,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "triomphe" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d15f4f5898bcfc5d8a8e45ed30b2f108d710c1a2e5489dbf80e078f707181bc" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -5462,7 +6665,7 @@ dependencies = [ "log", "rand 0.10.3", "sha1", - "thiserror", + "thiserror 2.0.21", ] [[package]] @@ -5487,7 +6690,7 @@ dependencies = [ "serde_json", "strum", "syn 3.0.9", - "thiserror", + "thiserror 2.0.21", ] [[package]] @@ -5516,7 +6719,7 @@ dependencies = [ "serde", "serde_json", "syn 3.0.9", - "thiserror", + "thiserror 2.0.21", "typespace", "unicode-ident", ] @@ -5549,6 +6752,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "unicase" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357cc3acc6a036009fd6c973ed009037c732d60d0b4f6c673e9041497482a28f" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -5567,6 +6776,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" +[[package]] +name = "unicode-id" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65dd75ab2d9d379dbe86d4059e91d8b5a6e7054865eaba1ddbfc7a817ff96392" + [[package]] name = "unicode-ident" version = "1.0.27" @@ -5627,6 +6842,33 @@ dependencies = [ "serde", ] +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize 0.13.0", + "kurbo 0.11.3", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes 0.15.3", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "usvg" version = "0.46.0" @@ -5637,8 +6879,8 @@ dependencies = [ "data-url", "flate2", "fontdb", - "imagesize", - "kurbo", + "imagesize 0.14.0", + "kurbo 0.13.1", "log", "pico-args", "roxmltree 0.21.2", @@ -5646,7 +6888,7 @@ dependencies = [ "simplecss", "siphasher", "strict-num", - "svgtypes", + "svgtypes 0.16.2", "tiny-skia-path", "unicode-bidi", "unicode-script", @@ -5654,6 +6896,12 @@ dependencies = [ "xmlwriter", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.1.0" @@ -5824,10 +7072,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasm_thread" version = "0.3.3" -source = "git+https://github.com/zed-industries/wasm_thread?rev=0cf96c7708dfb97ccf3da50347e25edcf75d6937#0cf96c7708dfb97ccf3da50347e25edcf75d6937" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7516db7f32decdadb1c3b8deb1b7d78b9df7606c5cc2f6241737c2ab3a0258e" dependencies = [ "futures", "js-sys", @@ -5884,8 +7146,9 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527ccdf43dd5b2e8676eed9984ce00e2bbb0a1b85b70c1969dcb6cd2eb55ab9e" dependencies = [ "arrayvec", "bitflags 2.13.2", @@ -5897,6 +7160,8 @@ dependencies = [ "js-sys", "log", "naga", + "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "smallvec", @@ -5906,14 +7171,14 @@ dependencies = [ "web-sys", "wgpu-core", "wgpu-hal", - "wgpu-sync", "wgpu-types", ] [[package]] name = "wgpu-core" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c018fce9b6270aa203c2fdd56f3cce996713534bd757e4ea58c8560b121f14" dependencies = [ "arrayvec", "bit-set", @@ -5927,68 +7192,64 @@ dependencies = [ "log", "naga", "naga-types", - "num-traits", + "once_cell", + "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "rustc-hash 1.1.0", - "scopeguard", "smallvec", - "thiserror", + "thiserror 2.0.21", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", - "wgpu-core-deps-linux-android-bsd", "wgpu-core-deps-wasm", - "wgpu-core-deps-windows", + "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-naga-bridge", - "wgpu-sync", "wgpu-types", ] [[package]] name = "wgpu-core-deps-apple" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061f3d319a40d39d00b1ecc2c33b89fe21d4e6fe01859df3500a3a8ecccd6b68" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" -dependencies = [ - "wgpu-hal", -] - -[[package]] -name = "wgpu-core-deps-linux-android-bsd" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98b86cf4abf524a902dd35f18ca6a3f08fc2ae9847c8f10b48e30491b1f0b86" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-wasm" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d260ca2ca8adc654f513a869bf3cac0b47f5b8d3f87803d6942015f8de597aae" dependencies = [ "wgpu-hal", ] [[package]] -name = "wgpu-core-deps-windows" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +name = "wgpu-core-deps-windows-linux-android" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7586165fd5f6d881cb9ce4bb71f40d6caab2c0f1837e3fc1d9788a197fb6004f" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b7fb58561a792bc237628ba0792e332de418fefe145f13b5ed8201e6d52f58" dependencies = [ "android_system_properties", "arrayvec", @@ -6017,7 +7278,11 @@ dependencies = [ "objc2-foundation 0.3.2", "objc2-metal 0.3.2", "objc2-quartz-core 0.3.2", + "once_cell", "ordered-float", + "parking_lot", + "portable-atomic", + "portable-atomic-util", "profiling", "range-alloc", "raw-window-handle", @@ -6025,12 +7290,11 @@ dependencies = [ "renderdoc-sys", "smallvec", "static_assertions", - "thiserror", + "thiserror 2.0.21", "wasm-bindgen", "wayland-sys", "web-sys", "wgpu-naga-bridge", - "wgpu-sync", "wgpu-types", "windows 0.62.2", "windows-core 0.62.2", @@ -6039,36 +7303,24 @@ dependencies = [ [[package]] name = "wgpu-naga-bridge" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f62e73117bb7a62bfd9c5a5841438a823f6566c6442a808ee269d2d055c081" dependencies = [ "naga", "wgpu-types", ] -[[package]] -name = "wgpu-sync" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" -dependencies = [ - "cfg-if", - "lock_api", - "once_cell", - "parking_lot", - "portable-atomic", - "portable-atomic-util", -] - [[package]] name = "wgpu-types" -version = "30.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=cddb2f5d80dc0009b2ef6767635140cacf90a007#cddb2f5d80dc0009b2ef6767635140cacf90a007" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99dad6f1fbdbbdb4c278a6508b059d44688f5cebddf78d005a46a31340269286" dependencies = [ "bitflags 2.13.2", "bytemuck", "js-sys", "log", - "macro_rules_attribute", "naga-types", "raw-window-handle", "static_assertions", @@ -6122,7 +7374,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ "windows-core 0.57.0", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", ] [[package]] @@ -6158,7 +7420,7 @@ checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" dependencies = [ "parking_lot", "rayon", - "thiserror", + "thiserror 2.0.21", "windows 0.61.3", "windows-future 0.2.1", ] @@ -6190,7 +7452,20 @@ dependencies = [ "windows-implement 0.57.0", "windows-interface 0.57.0", "windows-result 0.1.2", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] @@ -6252,6 +7527,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "windows-implement" version = "0.60.2" @@ -6274,6 +7560,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "windows-interface" version = "0.59.3" @@ -6317,6 +7614,17 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.5", +] + [[package]] name = "windows-registry" version = "0.6.1" @@ -6334,7 +7642,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -6355,6 +7672,25 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -6373,13 +7709,22 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -6391,20 +7736,52 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -6434,54 +7811,144 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.7.15" @@ -6559,6 +8026,17 @@ version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" +[[package]] +name = "xml5ever" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" +dependencies = [ + "log", + "mac", + "markup5ever", +] + [[package]] name = "xmlwriter" version = "0.1.0" @@ -6684,14 +8162,15 @@ dependencies = [ [[package]] name = "zed-font-kit" version = "0.14.1-zed" -source = "git+https://github.com/zed-industries/font-kit?rev=94b0f28166665e8fd2f53ff6d268a14955c82269#94b0f28166665e8fd2f53ff6d268a14955c82269" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3898e450f36f852edda72e3f985c34426042c4951790b23b107f93394f9bff5" dependencies = [ "bitflags 2.13.2", "byteorder", - "core-foundation", - "core-graphics", + "core-foundation 0.10.1", + "core-graphics 0.24.0", "core-text", - "dirs", + "dirs 5.0.1", "dwrote", "float-ord", "freetype-sys", @@ -6708,12 +8187,19 @@ dependencies = [ [[package]] name = "zed-scap" version = "0.0.8-zed" -source = "git+https://github.com/zed-industries/scap?rev=b5c86ec58dd4b6b7732bf6ac8be03d6b1f190a82#b5c86ec58dd4b6b7732bf6ac8be03d6b1f190a82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b338d705ae33a43ca00287c11129303a7a0aa57b101b72a1c08c863f698ac8" dependencies = [ "anyhow", + "cocoa", + "core-graphics-helmer-fork", "log", + "objc", "rand 0.8.8", + "screencapturekit", + "screencapturekit-sys", "sysinfo", + "tao-core-video-sys", "windows 0.61.3", "windows-capture", "x11", @@ -6835,18 +8321,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] -name = "ztracing" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" -dependencies = [ - "tracing", - "ztracing_macro", -] - -[[package]] -name = "ztracing_macro" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed?rev=c7bea343838f42b4c14151393e41f0bbe033c95e#c7bea343838f42b4c14151393e41f0bbe033c95e" +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-core" @@ -6863,13 +8341,22 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core 0.4.12", +] + [[package]] name = "zune-jpeg" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ - "zune-core", + "zune-core 0.5.3", ] [[package]] diff --git a/apps/desktop-gpui/Cargo.toml b/apps/desktop-gpui/Cargo.toml index a3d580d795e1e..2d27e6b534c73 100644 --- a/apps/desktop-gpui/Cargo.toml +++ b/apps/desktop-gpui/Cargo.toml @@ -9,10 +9,10 @@ publish = false async-channel = "2.5.0" futures-util = "0.3.34" getrandom = "0.4.3" -gpui = { git = "https://github.com/zed-industries/zed", rev = "c7bea343838f42b4c14151393e41f0bbe033c95e" } -gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "c7bea343838f42b4c14151393e41f0bbe033c95e", features = [ - "font-kit", -] } +gpui = { package = "gpui-pre", version = "=0.3.11" } +gpui-component = "=0.9.0" +gpui-kit-assets = "=0.9.0" +gpui_platform = { package = "gpui-pre-platform", version = "=0.3.11", features = ["font-kit"] } hex = "0.4.3" hmac = "0.13.0" libc = "0.2.191" diff --git a/apps/desktop-gpui/README.md b/apps/desktop-gpui/README.md index 72d22c08abd38..7f7fa8b2ea0a6 100644 --- a/apps/desktop-gpui/README.md +++ b/apps/desktop-gpui/README.md @@ -2,7 +2,7 @@ Native desktop client written in Rust with [GPUI](https://github.com/zed-industries/zed/tree/main/crates/gpui). It is meant to replace `apps/desktop` (Electron) over time and never embeds Node or Chromium. It starts the T3 server as a child process, authenticates without pairing, and opens the RPC WebSocket. -Status: connection shell. The window shows the connection state ("Starting server...", "Connecting...", "Connected to · N projects · M threads", or the error). The counts come from a live `orchestration.subscribeShell` subscription. There are no screens yet. +Status: connection shell. The window shows the connection state ("Starting server...", "Connecting...", "Connected to · N projects · M threads", or the error). The counts come from a live `orchestration.subscribeShell` subscription. Once connected, it draws a static skeleton of the main screen with [gpui-component](https://github.com/longbridge/gpui-component): a thread list (newest first) and an empty composer. Nothing is interactive yet. ## Run @@ -63,7 +63,7 @@ Known gaps: typify cannot flatten the `projectIcon` union (an `anyOf` with a `{k ## Notes -- `gpui` and `gpui_platform` are pinned to one `zed-industries/zed` commit in `Cargo.toml`. Bump both together. +- `gpui` and `gpui_platform` are the crates.io packages `gpui-pre` and `gpui-pre-platform`, pinned to the exact version `gpui-component` requires. Bump them together with `gpui-component` and `gpui-kit-assets`. - macOS needs the Xcode Metal toolchain (`xcodebuild -downloadComponent MetalToolchain`) and the `font-kit` feature, which is enabled. - Server spawning uses a unix pipe for fd 3, so it is macOS and Linux only for now. - CI does not build this crate yet. GPUI needs a macOS runner or Linux system libraries (wayland, x11, xkbcommon, vulkan). diff --git a/apps/desktop-gpui/src/connection.rs b/apps/desktop-gpui/src/connection.rs index c6c5e658f6e25..a7a2f59b0f717 100644 --- a/apps/desktop-gpui/src/connection.rs +++ b/apps/desktop-gpui/src/connection.rs @@ -3,7 +3,7 @@ use crate::auth::{self, AuthError, Credential}; use crate::rpc::{Event, PING_FRAME, Session, SubscribeShell}; use crate::shell::ShellState; -use crate::status::{Counts, Status, StatusSender, publish}; +use crate::status::{Status, StatusSender, publish}; use crate::wire::{ShellItem, SubscribeShellPayload}; use futures_util::{SinkExt, StreamExt}; use std::time::Duration; @@ -79,7 +79,7 @@ pub async fn run(http_base: &str, credential: &Credential, label: &str, status: status, Status::Connected { label: label.to_owned(), - counts: None, + shell: None, }, ); backoff.reset(); @@ -196,10 +196,7 @@ fn handle( status, Status::Connected { label: label.to_owned(), - counts: Some(Counts { - projects: shell.project_count(), - threads: shell.thread_count(), - }), + shell: Some(shell.view()), }, ); } diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs index 3ee9c7d1f861e..76eac738b6f4d 100644 --- a/apps/desktop-gpui/src/main.rs +++ b/apps/desktop-gpui/src/main.rs @@ -5,35 +5,18 @@ mod rpc; mod server; mod shell; mod status; +mod ui; mod wire; use config::{Attach, Config}; -use gpui::{ - App, Bounds, Context, SharedString, TitlebarOptions, Window, WindowBounds, WindowOptions, div, - prelude::*, px, rgb, size, -}; +use gpui::{App, AppContext as _, Bounds, WindowBounds, WindowOptions, px, size}; +use gpui_component::{Root, Theme, ThemeMode, TitleBar}; +use gpui_kit_assets::Assets; use gpui_platform::application; use server::Supervisor; use status::Status; use std::rc::Rc; - -struct StatusView { - status: SharedString, -} - -impl Render for StatusView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .flex() - .size_full() - .justify_center() - .items_center() - .bg(rgb(0x1a1a1a)) - .text_color(rgb(0xe6e6e6)) - .text_xl() - .child(self.status.clone()) - } -} +use ui::MainView; fn main() { let runtime = tokio::runtime::Builder::new_multi_thread() @@ -72,7 +55,9 @@ fn main() { Status::NotConnected }; - application().run(move |cx: &mut App| { + application().with_assets(Assets).run(move |cx: &mut App| { + gpui_component::init(cx); + Theme::change(ThemeMode::Dark, None, cx); if let Some(supervisor) = supervisor { // The grace period outlasts gpui's quit timeout, so block here instead of awaiting. cx.on_app_quit(move |_| { @@ -85,30 +70,23 @@ fn main() { cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), - titlebar: Some(TitlebarOptions { - title: Some("T3 Code".into()), - ..Default::default() - }), - ..Default::default() + ..TitleBar::window_options() }, - |_, cx| { - cx.new(|cx| { + |window, cx| { + let view = cx.new(|cx| { cx.spawn(async move |this, cx| { while let Ok(status) = status_rx.recv().await { - let updated = this.update(cx, |view: &mut StatusView, cx| { - view.status = status.to_string().into(); - cx.notify(); - }); + let updated = this + .update(cx, |view: &mut MainView, cx| view.set_status(status, cx)); if updated.is_err() { break; } } }) .detach(); - StatusView { - status: initial.to_string().into(), - } - }) + MainView::new(initial, window, cx) + }); + cx.new(|cx| Root::new(view, window, cx)) }, ) .expect("failed to open the main window"); diff --git a/apps/desktop-gpui/src/shell.rs b/apps/desktop-gpui/src/shell.rs index f68bd3c1ec604..324c642974045 100644 --- a/apps/desktop-gpui/src/shell.rs +++ b/apps/desktop-gpui/src/shell.rs @@ -1,6 +1,7 @@ //! The shell read model built from `orchestration.subscribeShell`, mirroring //! `packages/client-runtime/src/state/shellReducer.ts` for what this client shows. +use crate::status::{ShellView, ThreadRow}; use crate::wire::generated::{ OrchestrationSubscribeShellSuccess as Item, OrchestrationSubscribeShellSuccessVariant4Location as Location, @@ -32,6 +33,46 @@ impl ShellState { self.threads.len() } + /// Threads newest first (ties by id); the first project is the oldest by creation. + pub fn view(&self) -> ShellView { + let mut rows: Vec = self + .threads + .values() + .map(|thread| ThreadRow { + id: thread.id.clone(), + project: self + .projects + .get(&thread.project_id) + .map(|project| project.title.clone()) + .unwrap_or_default(), + title: thread.title.clone(), + branch: thread.branch.clone(), + status: thread.status, + updated_at: thread.updated_at.clone(), + }) + .collect(); + rows.sort_by(|a, b| { + b.updated_at + .cmp(&a.updated_at) + .then_with(|| a.id.cmp(&b.id)) + }); + let first_project = self + .projects + .values() + .min_by(|a, b| { + a.created_at + .cmp(&b.created_at) + .then_with(|| a.id.cmp(&b.id)) + }) + .map(|project| project.title.clone()); + ShellView { + projects: self.project_count(), + threads: self.thread_count(), + first_project, + rows, + } + } + /// Applies one stream item. Returns whether the counts or sync marker may have changed. pub fn apply(&mut self, item: Item) -> bool { match item { @@ -176,6 +217,21 @@ mod tests { assert_eq!(state.project_count(), 0); } + #[test] + fn view_lists_newest_threads_first_with_project_names() { + let mut state = seeded(); + state.apply(with(THREAD_UPDATED, |value| { + value["thread"]["id"] = "thread-new".into(); + value["thread"]["updatedAt"] = "2026-07-01T00:00:00.000Z".into(); + })); + let view = state.view(); + assert_eq!((view.projects, view.threads), (1, 2)); + assert_eq!(view.first_project.as_deref(), Some("Project")); + let ids: Vec<_> = view.rows.iter().map(|row| row.id.as_str()).collect(); + assert_eq!(ids, ["thread-new", "thread-v2"]); + assert!(view.rows.iter().all(|row| row.project == "Project")); + } + #[test] fn new_project_id_adds_a_row() { let mut state = seeded(); diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index fb5526b2b966c..d1e35de40e9fc 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -1,3 +1,4 @@ +use crate::wire::generated::ThreadsEntryStatus; use std::fmt; /// What the window shows about the managed server and its socket. @@ -6,18 +7,31 @@ pub enum Status { NotConnected, Starting, Connecting, - /// Socket is up. Counts stay `None` until the first shell snapshot lands. + /// Socket is up. `shell` stays `None` until the first shell snapshot lands. Connected { label: String, - counts: Option, + shell: Option, }, Failed(String), } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Counts { +/// What the window needs from the shell state, newest thread first. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ShellView { pub projects: usize, pub threads: usize, + pub first_project: Option, + pub rows: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ThreadRow { + pub id: String, + pub project: String, + pub title: String, + pub branch: Option, + pub status: ThreadsEntryStatus, + pub updated_at: String, } fn plural(count: usize, noun: &str) -> String { @@ -34,13 +48,12 @@ impl fmt::Display for Status { Self::NotConnected => f.write_str("Not connected"), Self::Starting => f.write_str("Starting server..."), Self::Connecting => f.write_str("Connecting..."), + Self::Connected { label, shell: None } => write!(f, "Connected to {label}"), Self::Connected { label, - counts: None, - } => write!(f, "Connected to {label}"), - Self::Connected { - label, - counts: Some(Counts { projects, threads }), + shell: Some(ShellView { + projects, threads, .. + }), } => write!( f, "Connected to {label} \u{b7} {projects} {} \u{b7} {threads} {}", @@ -68,14 +81,15 @@ mod tests { let label = "Local".to_owned(); let waiting = Status::Connected { label: label.clone(), - counts: None, + shell: None, }; assert_eq!(waiting.to_string(), "Connected to Local"); let known = Status::Connected { label: label.clone(), - counts: Some(Counts { + shell: Some(ShellView { projects: 2, threads: 14, + ..Default::default() }), }; assert_eq!( @@ -84,9 +98,10 @@ mod tests { ); let single = Status::Connected { label, - counts: Some(Counts { + shell: Some(ShellView { projects: 1, threads: 1, + ..Default::default() }), }; assert_eq!( diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs new file mode 100644 index 0000000000000..d41c2be95800a --- /dev/null +++ b/apps/desktop-gpui/src/ui.rs @@ -0,0 +1,235 @@ +use crate::status::{ShellView, Status, ThreadRow}; +use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; +use gpui::{ + AnyElement, Context, Entity, Hsla, IntoElement, ParentElement, Render, Styled, + UniformListScrollHandle, Window, div, prelude::*, px, uniform_list, +}; +use gpui_component::{ + ActiveTheme, Icon, IconName, Sizable, TitleBar, + breadcrumb::{Breadcrumb, BreadcrumbItem}, + button::{Button, ButtonVariants}, + h_flex, + input::{Input, InputState, Textarea, TextareaState}, + tag::Tag, + v_flex, +}; + +const SIDEBAR_WIDTH: f32 = 300.; +const ROW_HEIGHT: f32 = 60.; + +pub struct MainView { + status: Status, + search: Entity, + composer: Entity, + list: UniformListScrollHandle, +} + +impl MainView { + pub fn new(status: Status, window: &mut Window, cx: &mut Context) -> Self { + Self { + status, + search: cx.new(|cx| InputState::new(window, cx).placeholder("Search threads")), + composer: cx.new(|cx| { + TextareaState::new(window, cx) + .placeholder("Ask anything. @ files, $ skills, / commands") + .auto_grow(3, 10) + }), + list: UniformListScrollHandle::new(), + } + } + + pub fn set_status(&mut self, status: Status, cx: &mut Context) { + self.status = status; + cx.notify(); + } + + fn shell(&self) -> Option<&ShellView> { + match &self.status { + Status::Connected { shell, .. } => shell.as_ref(), + _ => None, + } + } + + fn environment(&self) -> String { + match &self.status { + Status::Connected { label, .. } => label.clone(), + other => other.to_string(), + } + } + + fn sidebar(&self, cx: &mut Context) -> impl IntoElement { + let count = self.shell().map_or(0, |shell| shell.rows.len()); + v_flex() + .w(px(SIDEBAR_WIDTH)) + .flex_shrink_0() + .h_full() + .gap_2() + .p_2() + .bg(cx.theme().sidebar) + .border_r_1() + .border_color(cx.theme().sidebar_border) + .child(Input::new(&self.search).prefix(Icon::new(IconName::Search))) + .child( + uniform_list( + "threads", + count, + cx.processor(|this, range: std::ops::Range, _, cx| { + let Some(shell) = this.shell() else { + return Vec::new(); + }; + let (muted, hover) = (cx.theme().muted_foreground, cx.theme().list_hover); + shell.rows[range] + .iter() + .map(|row| thread_row(row, muted, hover)) + .collect() + }), + ) + .track_scroll(&self.list) + .flex_1() + .min_h_0(), + ) + } + + fn main_area(&self, cx: &mut Context) -> AnyElement { + let Some(shell) = self.shell() else { + return centered(cx) + .text_xl() + .child(self.status.to_string()) + .into_any_element(); + }; + let project = shell + .first_project + .clone() + .unwrap_or_else(|| "your project".to_owned()); + v_flex() + .flex_1() + .min_w_0() + .child( + h_flex() + .h(px(40.)) + .px_4() + .items_center() + .border_b_1() + .border_color(cx.theme().border) + .child( + Breadcrumb::new() + .child(BreadcrumbItem::new(project.clone())) + .child(BreadcrumbItem::new("New thread")), + ), + ) + .child( + centered(cx) + .gap_6() + .child( + div() + .text_2xl() + .child(format!("What should we build in {project}?")), + ) + .child(self.composer_box(cx)), + ) + .into_any_element() + } + + fn composer_box(&self, cx: &mut Context) -> impl IntoElement { + v_flex() + .w(px(720.)) + .max_w_full() + .p_3() + .gap_2() + .rounded_xl() + .border_1() + .border_color(cx.theme().border) + .bg(cx.theme().secondary) + .child( + Textarea::new(&self.composer) + .appearance(false) + .bordered(false), + ) + .child( + h_flex().justify_end().child( + Button::new("send") + .primary() + .rounded_full() + .icon(IconName::ArrowUp) + .on_click(|_, _, _| {}), + ), + ) + } +} + +fn centered(cx: &Context) -> gpui::Div { + v_flex() + .flex_1() + .size_full() + .items_center() + .justify_center() + .text_color(cx.theme().foreground) +} + +fn status_tag(status: ThreadStatus) -> Option { + let (tag, label) = match status { + ThreadStatus::Running | ThreadStatus::Starting => (Tag::info(), "Running"), + ThreadStatus::Waiting => (Tag::warning(), "Waiting"), + ThreadStatus::Queued | ThreadStatus::Preparing => (Tag::secondary(), "Queued"), + ThreadStatus::Failed => (Tag::danger(), "Failed"), + _ => return None, + }; + Some(tag.small().child(label)) +} + +fn thread_row(row: &ThreadRow, muted: Hsla, hover: Hsla) -> impl IntoElement + use<> { + v_flex() + .id(row.id.clone()) + .h(px(ROW_HEIGHT)) + .px_2() + .gap_0p5() + .justify_center() + .rounded_md() + .hover(|style| style.bg(hover)) + .child( + div() + .text_xs() + .text_color(muted) + .truncate() + .child(row.project.clone()), + ) + .child( + h_flex() + .gap_2() + .justify_between() + .child(div().text_sm().truncate().child(row.title.clone())) + .children(status_tag(row.status)), + ) + .children(row.branch.as_ref().map(|branch| { + div() + .text_xs() + .text_color(muted) + .truncate() + .child(branch.clone()) + })) +} + +impl Render for MainView { + fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + v_flex() + .size_full() + .bg(cx.theme().background) + .text_color(cx.theme().foreground) + .child( + TitleBar::new().child("T3 Code").child( + div() + .ml_2() + .text_xs() + .text_color(cx.theme().muted_foreground) + .child(self.environment()), + ), + ) + .child( + h_flex() + .flex_1() + .min_h_0() + .child(self.sidebar(cx)) + .child(self.main_area(cx)), + ) + } +} From 768f8dd9ed1f860c3e7b03375452162f36d1504b Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 00:34:57 -0500 Subject: [PATCH 05/14] fix(desktop-gpui): thread rows no longer clip or overflow the sidebar Rows were a fixed 60px holding three lines, so the project name was cut off, and status tags pushed past the sidebar edge on long titles. The header also floated to the middle of the main area. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/src/ui.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index d41c2be95800a..f1af4fad848ce 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -15,7 +15,7 @@ use gpui_component::{ }; const SIDEBAR_WIDTH: f32 = 300.; -const ROW_HEIGHT: f32 = 60.; +const ROW_HEIGHT: f32 = 68.; pub struct MainView { status: Status, @@ -104,8 +104,10 @@ impl MainView { v_flex() .flex_1() .min_w_0() + .h_full() .child( h_flex() + .flex_shrink_0() .h(px(40.)) .px_4() .items_center() @@ -160,7 +162,9 @@ impl MainView { fn centered(cx: &Context) -> gpui::Div { v_flex() .flex_1() - .size_full() + .min_h_0() + .w_full() + .px_6() .items_center() .justify_center() .text_color(cx.theme().foreground) @@ -181,8 +185,10 @@ fn thread_row(row: &ThreadRow, muted: Hsla, hover: Hsla) -> impl IntoElement + u v_flex() .id(row.id.clone()) .h(px(ROW_HEIGHT)) + .w_full() + .overflow_hidden() .px_2() - .gap_0p5() + .py_1() .justify_center() .rounded_md() .hover(|style| style.bg(hover)) @@ -196,9 +202,15 @@ fn thread_row(row: &ThreadRow, muted: Hsla, hover: Hsla) -> impl IntoElement + u .child( h_flex() .gap_2() - .justify_between() - .child(div().text_sm().truncate().child(row.title.clone())) - .children(status_tag(row.status)), + .child( + div() + .flex_1() + .min_w_0() + .text_sm() + .truncate() + .child(row.title.clone()), + ) + .children(status_tag(row.status).map(|tag| div().flex_shrink_0().child(tag))), ) .children(row.branch.as_ref().map(|branch| { div() From 80ea723065f6ffe757f0755ddc837265009a4e1a Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 00:57:50 -0500 Subject: [PATCH 06/14] feat(desktop-gpui): pick the project from the empty-state heading The heading named a fixed project with no way to change it. Match the web empty state: the project name opens a picker, and the start-without-a-project link shows its shortcut. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/src/shell.rs | 15 ++-- apps/desktop-gpui/src/status.rs | 3 +- apps/desktop-gpui/src/ui.rs | 151 +++++++++++++++++++++++++++++--- 3 files changed, 149 insertions(+), 20 deletions(-) diff --git a/apps/desktop-gpui/src/shell.rs b/apps/desktop-gpui/src/shell.rs index 324c642974045..650ef0b79dbfe 100644 --- a/apps/desktop-gpui/src/shell.rs +++ b/apps/desktop-gpui/src/shell.rs @@ -56,19 +56,16 @@ impl ShellState { .cmp(&a.updated_at) .then_with(|| a.id.cmp(&b.id)) }); - let first_project = self + let mut project_titles: Vec = self .projects .values() - .min_by(|a, b| { - a.created_at - .cmp(&b.created_at) - .then_with(|| a.id.cmp(&b.id)) - }) - .map(|project| project.title.clone()); + .map(|project| project.title.clone()) + .collect(); + project_titles.sort_by_key(|title| title.to_lowercase()); ShellView { projects: self.project_count(), threads: self.thread_count(), - first_project, + project_titles, rows, } } @@ -226,7 +223,7 @@ mod tests { })); let view = state.view(); assert_eq!((view.projects, view.threads), (1, 2)); - assert_eq!(view.first_project.as_deref(), Some("Project")); + assert_eq!(view.project_titles, ["Project"]); let ids: Vec<_> = view.rows.iter().map(|row| row.id.as_str()).collect(); assert_eq!(ids, ["thread-new", "thread-v2"]); assert!(view.rows.iter().all(|row| row.project == "Project")); diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index d1e35de40e9fc..4e5eb66052e5e 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -20,7 +20,8 @@ pub enum Status { pub struct ShellView { pub projects: usize, pub threads: usize, - pub first_project: Option, + /// Project titles in picker order. + pub project_titles: Vec, pub rows: Vec, } diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index f1af4fad848ce..a6fb60a6a6557 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -1,8 +1,8 @@ use crate::status::{ShellView, Status, ThreadRow}; use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; use gpui::{ - AnyElement, Context, Entity, Hsla, IntoElement, ParentElement, Render, Styled, - UniformListScrollHandle, Window, div, prelude::*, px, uniform_list, + Anchor, AnyElement, Context, Entity, Hsla, IntoElement, Keystroke, ParentElement, Render, + Styled, UniformListScrollHandle, Window, div, prelude::*, px, uniform_list, }; use gpui_component::{ ActiveTheme, Icon, IconName, Sizable, TitleBar, @@ -10,7 +10,10 @@ use gpui_component::{ button::{Button, ButtonVariants}, h_flex, input::{Input, InputState, Textarea, TextareaState}, + kbd::Kbd, + popover::Popover, tag::Tag, + tooltip::Tooltip, v_flex, }; @@ -22,6 +25,8 @@ pub struct MainView { search: Entity, composer: Entity, list: UniformListScrollHandle, + selected_project: Option, + picker_open: bool, } impl MainView { @@ -35,6 +40,8 @@ impl MainView { .auto_grow(3, 10) }), list: UniformListScrollHandle::new(), + selected_project: None, + picker_open: false, } } @@ -97,9 +104,7 @@ impl MainView { .child(self.status.to_string()) .into_any_element(); }; - let project = shell - .first_project - .clone() + let project = active_project(self.selected_project.as_deref(), shell) .unwrap_or_else(|| "your project".to_owned()); v_flex() .flex_1() @@ -122,16 +127,88 @@ impl MainView { .child( centered(cx) .gap_6() - .child( - div() - .text_2xl() - .child(format!("What should we build in {project}?")), - ) + .child(self.hero(&project, shell.project_titles.clone(), cx)) .child(self.composer_box(cx)), ) .into_any_element() } + fn hero(&self, project: &str, titles: Vec, cx: &mut Context) -> impl IntoElement { + let view = cx.entity(); + let picker = Popover::new("project-picker") + .anchor(Anchor::TopCenter) + .open(self.picker_open) + .on_open_change({ + let view = view.clone(); + move |open, _, cx| { + view.update(cx, |this, cx| { + this.picker_open = *open; + cx.notify(); + }); + } + }) + .trigger( + Button::new("project") + .ghost() + .tooltip(project.to_owned()) + .child(div().text_2xl().child(project.to_owned())), + ) + .content(move |_, _, cx| { + let hover = cx.theme().list_hover; + v_flex() + .id("project-list") + .w(px(240.)) + .max_h(px(320.)) + .overflow_y_scroll() + .children(titles.iter().enumerate().map(|(index, title)| { + let (view, title) = (view.clone(), title.clone()); + div() + .id(("project", index)) + .px_2() + .py_1() + .rounded_md() + .text_sm() + .truncate() + .cursor_pointer() + .hover(move |style| style.bg(hover)) + .child(title.clone()) + .on_click(move |_, _, cx| { + view.update(cx, |this, cx| { + this.selected_project = Some(title.clone()); + this.picker_open = false; + cx.notify(); + }); + }) + })) + }); + v_flex() + .items_center() + .gap_1() + .child( + h_flex() + .items_center() + .text_2xl() + .child("What should we build in") + .child(picker) + .child("?"), + ) + .child( + div() + .id("start-without-project") + .text_sm() + .text_color(cx.theme().muted_foreground) + .cursor_pointer() + .hover(|style| style.underline()) + .tooltip(|window, cx| { + Tooltip::element(|_, _| { + Kbd::new(Keystroke::parse("alt-cmd-n").expect("valid keystroke")) + }) + .build(window, cx) + }) + .child("or start without a project"), + ) + } + fn composer_box(&self, cx: &mut Context) -> impl IntoElement { v_flex() .w(px(720.)) @@ -159,6 +236,22 @@ impl MainView { } } +/// The picked project while it still exists, else the newest thread's project. +fn active_project(selected: Option<&str>, shell: &ShellView) -> Option { + selected + .filter(|title| shell.project_titles.iter().any(|known| known == title)) + .map(str::to_owned) + .or_else(|| { + shell + .rows + .iter() + .map(|row| &row.project) + .find(|project| !project.is_empty()) + .cloned() + }) + .or_else(|| shell.project_titles.first().cloned()) +} + fn centered(cx: &Context) -> gpui::Div { v_flex() .flex_1() @@ -245,3 +338,41 @@ impl Render for MainView { ) } } + +#[cfg(test)] +mod tests { + use super::*; + + fn row(project: &str) -> ThreadRow { + ThreadRow { + id: project.to_owned(), + project: project.to_owned(), + title: String::new(), + branch: None, + status: ThreadStatus::Idle, + updated_at: String::new(), + } + } + + #[test] + fn active_project_prefers_a_live_pick_then_the_newest_thread() { + let shell = ShellView { + project_titles: vec!["alpha".into(), "beta".into()], + rows: vec![row(""), row("beta")], + ..ShellView::default() + }; + assert_eq!( + active_project(Some("alpha"), &shell).as_deref(), + Some("alpha") + ); + assert_eq!( + active_project(Some("gone"), &shell).as_deref(), + Some("beta") + ); + let empty = ShellView { + project_titles: vec!["alpha".into()], + ..ShellView::default() + }; + assert_eq!(active_project(None, &empty).as_deref(), Some("alpha")); + } +} From 15d1a53ca4313f97decff596d52e040b48a99845 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 01:10:44 -0500 Subject: [PATCH 07/14] feat(desktop-gpui): project picker matches the web The hero picker lists projects the way the web does: pinned first, then by recent activity, with the active project leading, colored initials, and a search filter. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/src/main.rs | 1 + apps/desktop-gpui/src/projects.rs | 359 ++++++++++++++++++++++++++++++ apps/desktop-gpui/src/shell.rs | 17 +- apps/desktop-gpui/src/status.rs | 6 +- apps/desktop-gpui/src/ui.rs | 216 +++++++++++++----- 5 files changed, 531 insertions(+), 68 deletions(-) create mode 100644 apps/desktop-gpui/src/projects.rs diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs index 76eac738b6f4d..2c146d5e077ac 100644 --- a/apps/desktop-gpui/src/main.rs +++ b/apps/desktop-gpui/src/main.rs @@ -1,6 +1,7 @@ mod auth; mod config; mod connection; +mod projects; mod rpc; mod server; mod shell; diff --git a/apps/desktop-gpui/src/projects.rs b/apps/desktop-gpui/src/projects.rs new file mode 100644 index 0000000000000..7f0186db0913a --- /dev/null +++ b/apps/desktop-gpui/src/projects.rs @@ -0,0 +1,359 @@ +//! The project picker's rows, mirroring the web picker: `sortLogicalProjectsForSidebar` +//! (Sidebar.logic.ts), `pickableProjectGroups` (projectGrouping.ts), `deriveProjectIdentity` +//! (projectIdentity.ts) and `buildSidebarProjectPickerEntries` (sidebarProjectGrouping.ts). + +use crate::wire::{Project, Thread}; +use std::collections::HashMap; + +/// `[name, light, dark]` as sRGB hex: Tailwind's 600 and 400 shades, in `PROJECT_ICON_COLORS` order. +const COLORS: [(&str, u32, u32); 18] = [ + ("gray", 0x4b5563, 0x9ca3af), + ("red", 0xdc2626, 0xf87171), + ("orange", 0xea580c, 0xfb923c), + ("amber", 0xd97706, 0xfbbf24), + ("yellow", 0xca8a04, 0xfacc15), + ("lime", 0x65a30d, 0xa3e635), + ("green", 0x16a34a, 0x4ade80), + ("emerald", 0x059669, 0x34d399), + ("teal", 0x0d9488, 0x2dd4bf), + ("cyan", 0x0891b2, 0x22d3ee), + ("sky", 0x0284c7, 0x38bdf8), + ("blue", 0x2563eb, 0x60a5fa), + ("indigo", 0x4f46e5, 0x818cf8), + ("violet", 0x7c3aed, 0xa78bfa), + ("purple", 0x9333ea, 0xc084fc), + ("fuchsia", 0xc026d3, 0xe879f9), + ("pink", 0xdb2777, 0xf472b6), + ("rose", 0xe11d48, 0xfb7185), +]; + +/// The server's title for the managed no-project workspace (ManagedProjectFolders.ts). +const SCRATCH_TITLE: &str = "No project"; +const SCRATCH_FOLDER: &str = "scratch"; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ProjectEntry { + pub id: String, + pub title: String, + pub initials: String, + /// Index into the shared palette. + pub color: usize, + pub pinned: bool, + pub scratch: bool, +} + +impl ProjectEntry { + /// The badge text color for the theme; the badge background is this at 14% alpha. + pub fn color_hex(&self, dark: bool) -> u32 { + let (_, light, night) = COLORS[self.color]; + if dark { night } else { light } + } +} + +fn words(text: &str) -> Vec<&str> { + text.split(|c: char| !c.is_alphanumeric()) + .filter(|word| !word.is_empty()) + .collect() +} + +fn initials(name: &str) -> String { + let words = words(name); + let Some(first_word) = words.first() else { + return "PR".to_owned(); + }; + let glyphs: Vec = first_word.chars().collect(); + let first = glyphs[0]; + let second = glyphs[1..] + .iter() + .copied() + .find(|glyph| glyph.is_numeric()) + .or_else(|| { + if words.len() > 1 { + words.last().and_then(|word| word.chars().next()) + } else { + glyphs.last().copied() + } + }) + .unwrap_or(first); + format!("{first}{second}") + .to_uppercase() + .chars() + .take(2) + .collect() +} + +fn color_index(name: &str) -> usize { + let lowered = name.trim().to_lowercase(); + let seed = if lowered.is_empty() { + "project" + } else { + &lowered + }; + seed.chars().fold(0, |index, glyph| { + (index * 31 + glyph as usize) % COLORS.len() + }) +} + +/// A monogram icon override wins, as in `ProjectFavicon`; other icon kinds fall back to the derived one. +fn monogram_override(project: &Project) -> Option<(String, usize)> { + let icon = project.project_icon.as_ref()?; + if icon.get("kind")?.as_str()? != "monogram" { + return None; + } + let text = icon.get("text")?.as_str()?.to_owned(); + let color = icon.get("color")?.as_str()?; + let index = COLORS.iter().position(|(name, ..)| *name == color)?; + Some((text, index)) +} + +/// The server creates the scratch project at `/scratch` titled "No project", and the +/// shell does not carry the server config's `scratchWorkspaceRoot`, so match on both. +fn is_scratch(project: &Project) -> bool { + let root = project.workspace_root.trim_end_matches(['/', '\\']); + project.title == SCRATCH_TITLE + && root + .rsplit(['/', '\\']) + .next() + .is_some_and(|folder| folder == SCRATCH_FOLDER) +} + +/// ISO timestamps from the server are fixed-width UTC, so text order is time order. +fn thread_activity(thread: &Thread) -> &str { + thread + .latest_user_message_at + .as_deref() + .unwrap_or(&thread.updated_at) +} + +/// Unarchived projects, pinned first, then most recent thread activity (the project's own +/// `updatedAt` when it has no threads), then title. +pub fn build_entries<'a>( + projects: impl IntoIterator, + threads: impl IntoIterator, +) -> Vec { + let mut latest: HashMap<&str, &str> = HashMap::new(); + for thread in threads.into_iter().filter(|t| t.archived_at.is_none()) { + let activity = thread_activity(thread); + let slot = latest.entry(thread.project_id.as_str()).or_insert(activity); + *slot = (*slot).max(activity); + } + let mut ranked: Vec<(&Project, &str)> = projects + .into_iter() + .filter(|project| project.archived_at.is_none()) + .map(|project| { + let activity = latest + .get(project.id.as_str()) + .copied() + .unwrap_or(&project.updated_at); + (project, activity) + }) + .collect(); + ranked.sort_by(|(a, a_at), (b, b_at)| { + b.pinned_at + .is_some() + .cmp(&a.pinned_at.is_some()) + .then_with(|| b_at.cmp(a_at)) + .then_with(|| a.title.to_lowercase().cmp(&b.title.to_lowercase())) + .then_with(|| a.id.cmp(&b.id)) + }); + ranked + .into_iter() + .map(|(project, _)| { + let (initials, color) = monogram_override(project) + .unwrap_or_else(|| (initials(&project.title), color_index(&project.title))); + ProjectEntry { + id: project.id.clone(), + title: project.title.clone(), + initials, + color, + pinned: project.pinned_at.is_some(), + scratch: is_scratch(project), + } + }) + .collect() +} + +/// What the picker lists: no scratch project, the active project leading the unpinned ones +/// (pinned stay first), then the case-insensitive title filter. +pub fn picker_entries<'a>( + entries: &'a [ProjectEntry], + active_id: Option<&str>, + query: &str, +) -> Vec<&'a ProjectEntry> { + let mut listed: Vec<&ProjectEntry> = entries.iter().filter(|entry| !entry.scratch).collect(); + if let Some(at) = listed + .iter() + .position(|entry| Some(entry.id.as_str()) == active_id && !entry.pinned) + { + let active = listed.remove(at); + let pinned = listed.iter().filter(|entry| entry.pinned).count(); + listed.insert(pinned, active); + } + let query = query.trim().to_lowercase(); + listed.retain(|entry| entry.title.to_lowercase().contains(&query)); + listed +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::{Value, json}; + + const SNAPSHOT: &str = include_str!("../tests/fixtures/shell-snapshot.json"); + + fn project(id: &str, title: &str, edit: impl FnOnce(&mut Value)) -> Project { + let mut value: Value = serde_json::from_str(SNAPSHOT).expect("json"); + let mut project = value["snapshot"]["projects"][0].take(); + project["id"] = id.into(); + project["title"] = title.into(); + project["archivedAt"] = Value::Null; + project["pinnedAt"] = Value::Null; + project["updatedAt"] = "2026-01-01T00:00:00.000Z".into(); + edit(&mut project); + serde_json::from_value(project).expect("project decodes") + } + + fn thread(project_id: &str, user_message_at: Option<&str>, updated_at: &str) -> Thread { + let mut value: Value = serde_json::from_str(SNAPSHOT).expect("json"); + let mut thread = value["snapshot"]["threads"][0].take(); + thread["id"] = format!("{project_id}-{updated_at}").into(); + thread["projectId"] = project_id.into(); + thread["latestUserMessageAt"] = user_message_at.into(); + thread["updatedAt"] = updated_at.into(); + serde_json::from_value(thread).expect("thread decodes") + } + + fn titles(entries: &[&ProjectEntry]) -> Vec { + entries.iter().map(|entry| entry.title.clone()).collect() + } + + #[test] + fn initials_follow_the_web_monogram() { + for (name, expected) in [ + ("atlas", "AS"), + ("fx-workspace", "FW"), + ("agent0", "A0"), + ("t3code", "T3"), + ("x", "XX"), + (" ", "PR"), + ("--", "PR"), + ] { + assert_eq!(initials(name), expected, "{name}"); + } + } + + #[test] + fn color_is_deterministic_and_case_insensitive() { + assert_eq!(color_index("Atlas"), color_index(" atlas ")); + assert_eq!(color_index(""), color_index("project")); + assert_eq!(COLORS[color_index("a")].0, COLORS[97 % 18].0); + assert!(color_index("anything at all") < COLORS.len()); + } + + #[test] + fn monogram_icon_overrides_the_derived_badge() { + let overridden = project("p", "atlas", |p| { + p["projectIcon"] = json!({"kind": "monogram", "text": "ZZ", "color": "rose"}); + }); + let lucide = project("q", "atlas", |p| { + p["projectIcon"] = json!({"kind": "lucide", "name": "star", "color": "rose"}); + }); + let entries = build_entries([&overridden, &lucide], []); + let by_id = |id: &str| entries.iter().find(|e| e.id == id).unwrap(); + assert_eq!(by_id("p").initials, "ZZ"); + assert_eq!(COLORS[by_id("p").color].0, "rose"); + assert_eq!(by_id("q").initials, "AS"); + } + + #[test] + fn entries_order_pinned_first_then_recent_activity_then_title() { + let projects = [ + project("quiet", "quiet", |p| { + p["updatedAt"] = "2026-03-01T00:00:00.000Z".into() + }), + project("busy", "busy", |_| {}), + project("pinned", "pinned", |p| { + p["pinnedAt"] = "2026-01-01T00:00:00.000Z".into() + }), + project("tie-b", "Bravo", |_| {}), + project("tie-a", "alpha", |_| {}), + project("gone", "gone", |p| { + p["archivedAt"] = "2026-01-01T00:00:00.000Z".into() + }), + ]; + let mut archived = thread("busy", None, "2026-09-01T00:00:00.000Z"); + archived.archived_at = Some("2026-09-02T00:00:00.000Z".into()); + let threads = [ + // The user message time wins over a newer updatedAt. + thread( + "busy", + Some("2026-05-01T00:00:00.000Z"), + "2026-06-01T00:00:00.000Z", + ), + thread("busy", None, "2026-04-01T00:00:00.000Z"), + archived, + ]; + let entries = build_entries(&projects, &threads); + let order: Vec<_> = entries.iter().map(|e| e.title.as_str()).collect(); + assert_eq!(order, ["pinned", "busy", "quiet", "alpha", "Bravo"]); + assert!(entries[0].pinned && !entries[1].pinned); + } + + #[test] + fn scratch_is_the_managed_no_project_folder() { + let scratch = project("s", "No project", |p| { + p["workspaceRoot"] = "/home/me/.t3/userdata/scratch/".into() + }); + let titled_only = project("t", "No project", |p| { + p["workspaceRoot"] = "/work/app".into() + }); + let folder_only = project("f", "scratch", |p| { + p["workspaceRoot"] = "/work/scratch".into() + }); + let entries = build_entries([&scratch, &titled_only, &folder_only], []); + let flags: Vec<_> = ["s", "t", "f"] + .iter() + .map(|id| entries.iter().find(|e| e.id == *id).unwrap().scratch) + .collect(); + assert_eq!(flags, [true, false, false]); + assert_eq!( + titles(&picker_entries(&entries, None, "")).len(), + 2, + "scratch is left out" + ); + } + + #[test] + fn picker_leads_with_the_active_unpinned_project_and_filters() { + let projects = [ + project("pin", "pinned", |p| { + p["pinnedAt"] = "2026-01-01T00:00:00.000Z".into() + }), + project("a", "Alpha", |p| { + p["updatedAt"] = "2026-03-01T00:00:00.000Z".into() + }), + project("b", "Beta", |p| { + p["updatedAt"] = "2026-02-01T00:00:00.000Z".into() + }), + project("c", "Gamma", |_| {}), + ]; + let entries = build_entries(&projects, []); + assert_eq!( + titles(&picker_entries(&entries, None, "")), + ["pinned", "Alpha", "Beta", "Gamma"] + ); + assert_eq!( + titles(&picker_entries(&entries, Some("c"), "")), + ["pinned", "Gamma", "Alpha", "Beta"] + ); + assert_eq!( + titles(&picker_entries(&entries, Some("pin"), "")), + ["pinned", "Alpha", "Beta", "Gamma"] + ); + assert_eq!( + titles(&picker_entries(&entries, Some("c"), " ETA ")), + ["Beta"] + ); + assert!(picker_entries(&entries, None, "zzz").is_empty()); + } +} diff --git a/apps/desktop-gpui/src/shell.rs b/apps/desktop-gpui/src/shell.rs index 650ef0b79dbfe..c5471e51e907d 100644 --- a/apps/desktop-gpui/src/shell.rs +++ b/apps/desktop-gpui/src/shell.rs @@ -1,6 +1,7 @@ //! The shell read model built from `orchestration.subscribeShell`, mirroring //! `packages/client-runtime/src/state/shellReducer.ts` for what this client shows. +use crate::projects; use crate::status::{ShellView, ThreadRow}; use crate::wire::generated::{ OrchestrationSubscribeShellSuccess as Item, @@ -40,6 +41,7 @@ impl ShellState { .values() .map(|thread| ThreadRow { id: thread.id.clone(), + project_id: thread.project_id.clone(), project: self .projects .get(&thread.project_id) @@ -56,16 +58,10 @@ impl ShellState { .cmp(&a.updated_at) .then_with(|| a.id.cmp(&b.id)) }); - let mut project_titles: Vec = self - .projects - .values() - .map(|project| project.title.clone()) - .collect(); - project_titles.sort_by_key(|title| title.to_lowercase()); ShellView { projects: self.project_count(), threads: self.thread_count(), - project_titles, + project_entries: projects::build_entries(self.projects.values(), self.threads.values()), rows, } } @@ -223,7 +219,12 @@ mod tests { })); let view = state.view(); assert_eq!((view.projects, view.threads), (1, 2)); - assert_eq!(view.project_titles, ["Project"]); + let titles: Vec<_> = view + .project_entries + .iter() + .map(|e| e.title.as_str()) + .collect(); + assert_eq!(titles, ["Project"]); let ids: Vec<_> = view.rows.iter().map(|row| row.id.as_str()).collect(); assert_eq!(ids, ["thread-new", "thread-v2"]); assert!(view.rows.iter().all(|row| row.project == "Project")); diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index 4e5eb66052e5e..f2880041decd2 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -1,3 +1,4 @@ +use crate::projects::ProjectEntry; use crate::wire::generated::ThreadsEntryStatus; use std::fmt; @@ -20,14 +21,15 @@ pub enum Status { pub struct ShellView { pub projects: usize, pub threads: usize, - /// Project titles in picker order. - pub project_titles: Vec, + /// Unarchived projects in picker order, scratch included. + pub project_entries: Vec, pub rows: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct ThreadRow { pub id: String, + pub project_id: String, pub project: String, pub title: String, pub branch: Option, diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index a6fb60a6a6557..d25b1a639e183 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -1,15 +1,17 @@ +use crate::projects::{self, ProjectEntry}; use crate::status::{ShellView, Status, ThreadRow}; use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; use gpui::{ - Anchor, AnyElement, Context, Entity, Hsla, IntoElement, Keystroke, ParentElement, Render, - Styled, UniformListScrollHandle, Window, div, prelude::*, px, uniform_list, + Anchor, AnyElement, Context, Entity, Focusable as _, FontWeight, Hsla, IntoElement, Keystroke, + ParentElement, Render, Styled, UniformListScrollHandle, Window, div, prelude::*, px, rgb, + uniform_list, }; use gpui_component::{ ActiveTheme, Icon, IconName, Sizable, TitleBar, breadcrumb::{Breadcrumb, BreadcrumbItem}, button::{Button, ButtonVariants}, h_flex, - input::{Input, InputState, Textarea, TextareaState}, + input::{Input, InputEvent, InputState, Textarea, TextareaState}, kbd::Kbd, popover::Popover, tag::Tag, @@ -23,6 +25,7 @@ const ROW_HEIGHT: f32 = 68.; pub struct MainView { status: Status, search: Entity, + project_search: Entity, composer: Entity, list: UniformListScrollHandle, selected_project: Option, @@ -31,9 +34,18 @@ pub struct MainView { impl MainView { pub fn new(status: Status, window: &mut Window, cx: &mut Context) -> Self { + let project_search = + cx.new(|cx| InputState::new(window, cx).placeholder("Search projects...")); + cx.subscribe(&project_search, |_, _, event: &InputEvent, cx| { + if matches!(event, InputEvent::Change) { + cx.notify(); + } + }) + .detach(); Self { status, search: cx.new(|cx| InputState::new(window, cx).placeholder("Search threads")), + project_search, composer: cx.new(|cx| { TextareaState::new(window, cx) .placeholder("Ask anything. @ files, $ skills, / commands") @@ -104,8 +116,8 @@ impl MainView { .child(self.status.to_string()) .into_any_element(); }; - let project = active_project(self.selected_project.as_deref(), shell) - .unwrap_or_else(|| "your project".to_owned()); + let active = active_project(self.selected_project.as_deref(), shell); + let project = active.map_or_else(|| "your project".to_owned(), |entry| entry.title.clone()); v_flex() .flex_1() .min_w_0() @@ -127,20 +139,35 @@ impl MainView { .child( centered(cx) .gap_6() - .child(self.hero(&project, shell.project_titles.clone(), cx)) + .child(self.hero( + &project, + active.map(|entry| entry.id.clone()), + shell.project_entries.clone(), + cx, + )) .child(self.composer_box(cx)), ) .into_any_element() } - fn hero(&self, project: &str, titles: Vec, cx: &mut Context) -> impl IntoElement { + fn hero( + &self, + project: &str, + active_id: Option, + entries: Vec, + cx: &mut Context, + ) -> impl IntoElement { let view = cx.entity(); + let search = self.project_search.clone(); let picker = Popover::new("project-picker") .anchor(Anchor::TopCenter) .open(self.picker_open) + .track_focus(&search.focus_handle(cx)) + .p_0() .on_open_change({ - let view = view.clone(); - move |open, _, cx| { + let (view, search) = (view.clone(), search.clone()); + move |open, window, cx| { + search.update(cx, |state, cx| state.set_value("", window, cx)); view.update(cx, |this, cx| { this.picker_open = *open; cx.notify(); @@ -154,32 +181,91 @@ impl MainView { .child(div().text_2xl().child(project.to_owned())), ) .content(move |_, _, cx| { - let hover = cx.theme().list_hover; + let query = search.read(cx).value(); + let listed = projects::picker_entries(&entries, active_id.as_deref(), &query); + let theme = cx.theme(); + let (dark, muted) = (theme.is_dark(), theme.muted_foreground); + let (hover, selected) = (theme.list_hover, theme.list_active); + let mono = theme.mono_font_family.clone(); + let border = theme.border; + let rows = listed.iter().enumerate().map(|(index, entry)| { + let (view, id) = (view.clone(), entry.id.clone()); + let color: Hsla = rgb(entry.color_hex(dark)).into(); + let active = active_id.as_deref() == Some(entry.id.as_str()); + h_flex() + .id(("project", index)) + .gap_2() + .px_2() + .py_1p5() + .items_center() + .rounded_md() + .cursor_pointer() + .when(active, |row| row.bg(selected)) + .hover(move |style| style.bg(hover)) + .child( + div() + .flex() + .flex_shrink_0() + .items_center() + .justify_center() + .size(px(20.)) + .rounded(px(5.)) + .bg(color.opacity(0.14)) + .text_color(color) + .text_size(px(10.)) + .font_weight(FontWeight::BOLD) + .font_family(mono.clone()) + .child(entry.initials.clone()), + ) + .child( + div() + .flex_1() + .min_w_0() + .text_sm() + .truncate() + .child(entry.title.clone()), + ) + .when(entry.pinned, |row| { + row.child( + Icon::new(gpui_kit_assets::IconName::Pin) + .small() + .text_color(muted), + ) + }) + .on_click(move |_, _, cx| { + view.update(cx, |this, cx| { + this.selected_project = Some(id.clone()); + this.picker_open = false; + cx.notify(); + }); + }) + }); v_flex() - .id("project-list") - .w(px(240.)) - .max_h(px(320.)) - .overflow_y_scroll() - .children(titles.iter().enumerate().map(|(index, title)| { - let (view, title) = (view.clone(), title.clone()); - div() - .id(("project", index)) - .px_2() - .py_1() - .rounded_md() - .text_sm() - .truncate() - .cursor_pointer() - .hover(move |style| style.bg(hover)) - .child(title.clone()) - .on_click(move |_, _, cx| { - view.update(cx, |this, cx| { - this.selected_project = Some(title.clone()); - this.picker_open = false; - cx.notify(); - }); - }) - })) + .w(px(400.)) + .child( + h_flex().px_3().border_b_1().border_color(border).child( + Input::new(&search) + .appearance(false) + .prefix(Icon::new(IconName::Search)), + ), + ) + .child( + v_flex() + .id("project-list") + .max_h(px(320.)) + .overflow_y_scroll() + .p_1() + .children(rows) + .when(listed.is_empty(), |list| { + list.child( + div() + .p_2() + .text_sm() + .text_color(muted) + .child("No matching projects."), + ) + }), + ) }); v_flex() .items_center() @@ -236,20 +322,19 @@ impl MainView { } } -/// The picked project while it still exists, else the newest thread's project. -fn active_project(selected: Option<&str>, shell: &ShellView) -> Option { +/// The picked project while it still exists, else the newest thread's project, else the first listed. +fn active_project<'a>(selected: Option<&str>, shell: &'a ShellView) -> Option<&'a ProjectEntry> { + let by_id = |id: &str| shell.project_entries.iter().find(|entry| entry.id == id); selected - .filter(|title| shell.project_titles.iter().any(|known| known == title)) - .map(str::to_owned) + .and_then(by_id) .or_else(|| { shell .rows .iter() - .map(|row| &row.project) - .find(|project| !project.is_empty()) - .cloned() + .filter_map(|row| by_id(&row.project_id)) + .find(|entry| !entry.scratch) }) - .or_else(|| shell.project_titles.first().cloned()) + .or_else(|| shell.project_entries.iter().find(|entry| !entry.scratch)) } fn centered(cx: &Context) -> gpui::Div { @@ -343,10 +428,22 @@ impl Render for MainView { mod tests { use super::*; - fn row(project: &str) -> ThreadRow { + fn entry(id: &str, scratch: bool) -> ProjectEntry { + ProjectEntry { + id: id.to_owned(), + title: id.to_owned(), + initials: "XX".to_owned(), + color: 0, + pinned: false, + scratch, + } + } + + fn row(project_id: &str) -> ThreadRow { ThreadRow { - id: project.to_owned(), - project: project.to_owned(), + id: project_id.to_owned(), + project_id: project_id.to_owned(), + project: project_id.to_owned(), title: String::new(), branch: None, status: ThreadStatus::Idle, @@ -355,24 +452,27 @@ mod tests { } #[test] - fn active_project_prefers_a_live_pick_then_the_newest_thread() { + fn active_project_prefers_a_live_pick_then_the_newest_real_project() { let shell = ShellView { - project_titles: vec!["alpha".into(), "beta".into()], - rows: vec![row(""), row("beta")], + project_entries: vec![ + entry("alpha", false), + entry("none", true), + entry("beta", false), + ], + rows: vec![row("none"), row("gone"), row("beta")], ..ShellView::default() }; - assert_eq!( - active_project(Some("alpha"), &shell).as_deref(), - Some("alpha") - ); - assert_eq!( - active_project(Some("gone"), &shell).as_deref(), - Some("beta") - ); + let id = |selected| active_project(selected, &shell).map(|e| e.id.as_str()); + assert_eq!(id(Some("alpha")), Some("alpha")); + assert_eq!(id(Some("gone")), Some("beta")); + assert_eq!(id(None), Some("beta")); let empty = ShellView { - project_titles: vec!["alpha".into()], + project_entries: vec![entry("none", true), entry("alpha", false)], ..ShellView::default() }; - assert_eq!(active_project(None, &empty).as_deref(), Some("alpha")); + assert_eq!( + active_project(None, &empty).map(|e| e.id.as_str()), + Some("alpha") + ); } } From 1f75651e49ddb2f495e525c4a5b24787bc39cf9f Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 01:45:21 -0500 Subject: [PATCH 08/14] feat(desktop-gpui): pin projects from the picker and keep the list steady Hover a picker row to pin or unpin the project through projects.mutate, gated on the server's projectOrganization capability. The picker keeps its order while open and the default project no longer follows thread activity, so rows and the heading stop jumping. The project name is plain heading text that underlines on hover, and the composer gains the attach button. Catalog icons beyond gpui-component's defaults rendered blank, so the app now embeds the extra icons it uses. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/src/assets.rs | 41 ++ apps/desktop-gpui/src/connection.rs | 156 ++++- apps/desktop-gpui/src/main.rs | 91 +-- apps/desktop-gpui/src/projects.rs | 60 ++ apps/desktop-gpui/src/rpc.rs | 9 + apps/desktop-gpui/src/server.rs | 58 +- apps/desktop-gpui/src/status.rs | 10 +- apps/desktop-gpui/src/ui.rs | 189 +++++- apps/desktop-gpui/src/wire/generated.rs | 810 ++++++++++++++++++++++++ apps/desktop-gpui/src/wire/mod.rs | 2 + apps/desktop-gpui/tests/wire_types.rs | 2 +- 11 files changed, 1347 insertions(+), 81 deletions(-) create mode 100644 apps/desktop-gpui/src/assets.rs diff --git a/apps/desktop-gpui/src/assets.rs b/apps/desktop-gpui/src/assets.rs new file mode 100644 index 0000000000000..9cca92b6de3f0 --- /dev/null +++ b/apps/desktop-gpui/src/assets.rs @@ -0,0 +1,41 @@ +use gpui::{AssetSource, Result, SharedString}; +use std::borrow::Cow; + +// `Assets` carries only gpui-component's default icons; anything else from the catalog renders blank. +gpui_kit_assets::icon_assets!(ExtraIcons, [Paperclip, Pin, PinOff]); + +pub struct AppAssets; + +impl AssetSource for AppAssets { + fn load(&self, path: &str) -> Result>> { + match ExtraIcons.load(path)? { + Some(bytes) => Ok(Some(bytes)), + None => gpui_kit_assets::Assets.load(path), + } + } + + fn list(&self, path: &str) -> Result> { + let mut paths = gpui_kit_assets::Assets.list(path)?; + paths.extend(ExtraIcons.list(path)?); + Ok(paths) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use gpui_kit_assets::IconName; + + #[test] + fn every_catalog_icon_the_app_uses_loads() { + for icon in [ + IconName::Paperclip, + IconName::Pin, + IconName::PinOff, + IconName::Search, + ] { + let loaded = AppAssets.load(&icon.path()).ok().flatten(); + assert!(loaded.is_some(), "{} is not embedded", icon.path()); + } + } +} diff --git a/apps/desktop-gpui/src/connection.rs b/apps/desktop-gpui/src/connection.rs index a7a2f59b0f717..e51b83dfa4b81 100644 --- a/apps/desktop-gpui/src/connection.rs +++ b/apps/desktop-gpui/src/connection.rs @@ -1,11 +1,12 @@ //! The authenticated RPC WebSocket: connect, keep alive, reconnect. use crate::auth::{self, AuthError, Credential}; -use crate::rpc::{Event, PING_FRAME, Session, SubscribeShell}; +use crate::rpc::{Event, PING_FRAME, ProjectsMutate, Session, SubscribeShell}; use crate::shell::ShellState; use crate::status::{Status, StatusSender, publish}; -use crate::wire::{ShellItem, SubscribeShellPayload}; +use crate::wire::{ProjectMutation, ProjectMutationType, ShellItem, SubscribeShellPayload}; use futures_util::{SinkExt, StreamExt}; +use std::collections::HashMap; use std::time::Duration; use tokio::time::{Instant, MissedTickBehavior, interval, sleep, sleep_until}; use tokio_tungstenite::tungstenite::Message; @@ -16,6 +17,51 @@ const PING_INTERVAL: Duration = Duration::from_secs(5); const PING_TIMEOUT: Duration = Duration::from_secs(15); const ORCHESTRATION_PROTOCOL_VERSION: &str = "2"; +/// What the connection needs to know about the server it dialed. +pub struct Environment { + pub label: String, + /// The server keeps `pinned` from `project.update` (`capabilities.projectOrganization`). + pub project_organization: bool, +} + +/// A request from the window that the connection sends as an RPC. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Action { + SetProjectPinned { project_id: String, pinned: bool }, +} + +pub type ActionSender = async_channel::Sender; +pub type ActionReceiver = async_channel::Receiver; + +impl Action { + fn describe(&self) -> &'static str { + match self { + Self::SetProjectPinned { pinned: true, .. } => "pin project", + Self::SetProjectPinned { pinned: false, .. } => "unpin project", + } + } + + fn payload(&self, command_id: String) -> ProjectMutation { + match self { + Self::SetProjectPinned { project_id, pinned } => ProjectMutation::Variant1 { + archived: None, + auto_pull: None, + command_id, + default_model_selection: None, + default_thread_env_mode: None, + favicon_path: None, + pinned: Some(*pinned), + project_icon: None, + project_id: project_id.clone(), + scripts: None, + title: None, + type_: ProjectMutationType::ProjectUpdate, + workspace_root: None, + }, + } + } +} + /// Doubling delay between attempts, capped (Electron restarts the backend the same way). pub struct Backoff { next: Duration, @@ -67,7 +113,13 @@ fn socket_url(http_base: &str, ticket: &str) -> Result { } /// Connects and stays connected until cancelled, reporting through `status`. -pub async fn run(http_base: &str, credential: &Credential, label: &str, status: &StatusSender) { +pub async fn run( + http_base: &str, + credential: &Credential, + environment: &Environment, + actions: &ActionReceiver, + status: &StatusSender, +) { let client = auth::http_client(); let mut bearer: Option = None; let mut backoff = Backoff::new(); @@ -78,12 +130,13 @@ pub async fn run(http_base: &str, credential: &Credential, label: &str, status: publish( status, Status::Connected { - label: label.to_owned(), + label: environment.label.clone(), + can_pin: environment.project_organization, shell: None, }, ); backoff.reset(); - serve(socket, label, status).await + serve(socket, environment, actions, status).await } Err(message) => message, }; @@ -128,7 +181,12 @@ async fn connect( } /// Subscribes to the shell stream and keeps the socket alive; returns why it ended. -async fn serve(socket: Socket, label: &str, status: &StatusSender) -> String { +async fn serve( + socket: Socket, + environment: &Environment, + actions: &ActionReceiver, + status: &StatusSender, +) -> String { let (mut sink, mut stream) = socket.split(); let mut session = Session::new(); let mut shell = ShellState::default(); @@ -139,6 +197,9 @@ async fn serve(socket: Socket, label: &str, status: &StatusSender) -> String { if let Err(error) = sink.send(Message::text(request)).await { return error.to_string(); } + // Whatever was clicked while disconnected is stale by now. + while actions.try_recv().is_ok() {} + let mut pending: HashMap = HashMap::new(); let mut ping = interval(PING_INTERVAL); ping.set_missed_tick_behavior(MissedTickBehavior::Delay); let mut deadline = Instant::now() + PING_TIMEOUT; @@ -149,6 +210,20 @@ async fn serve(socket: Socket, label: &str, status: &StatusSender) -> String { return error.to_string(); } } + action = actions.recv() => { + let Ok(action) = action else { + return "window closed".to_owned(); + }; + match session.request::(&action.payload(command_id())) { + Ok((id, frame)) => { + pending.insert(id, action.describe()); + if let Err(error) = sink.send(Message::text(frame)).await { + return error.to_string(); + } + } + Err(error) => eprintln!("[rpc] could not {}: {error}", action.describe()), + } + } _ = sleep_until(deadline) => { return format!("server stopped responding for {}s", PING_TIMEOUT.as_secs()); } @@ -170,7 +245,10 @@ async fn serve(socket: Socket, label: &str, status: &StatusSender) -> String { } } for event in reaction.events { - if let Some(reason) = handle(event, &mut shell, label, status) { + if settle(&mut pending, &event) { + continue; + } + if let Some(reason) = handle(event, &mut shell, environment, status) { return reason; } } @@ -181,11 +259,33 @@ async fn serve(socket: Socket, label: &str, status: &StatusSender) -> String { } } +fn command_id() -> String { + let mut bytes = [0u8; 8]; + // A failed read leaves zeros, and the server rejects a repeated command id rather than applying it twice. + let _ = getrandom::fill(&mut bytes); + format!("gpui-{}", hex::encode(bytes)) +} + +/// Consumes the outcome of an action's request, logging a failure; true when `event` was one. +fn settle(pending: &mut HashMap, event: &Event) -> bool { + match event { + Event::Done { request_id, .. } => pending.remove(request_id).is_some(), + Event::Failed { + request_id, + message, + } => pending.remove(request_id).is_some_and(|what| { + eprintln!("[rpc] could not {what}: {message}"); + true + }), + _ => false, + } +} + /// Applies one event; `Some(reason)` means the subscription is over and the socket should reopen. fn handle( event: Event, shell: &mut ShellState, - label: &str, + environment: &Environment, status: &StatusSender, ) -> Option { match event { @@ -195,7 +295,8 @@ fn handle( publish( status, Status::Connected { - label: label.to_owned(), + label: environment.label.clone(), + can_pin: environment.project_organization, shell: Some(shell.view()), }, ); @@ -224,6 +325,43 @@ mod tests { assert_eq!(backoff.next_delay().as_millis(), 500); } + #[test] + fn pin_action_builds_a_project_update_mutation() { + let pin = Action::SetProjectPinned { + project_id: "p1".to_owned(), + pinned: true, + }; + let value = serde_json::to_value(pin.payload("c1".to_owned())).expect("json"); + assert_eq!( + value, + serde_json::json!({ + "type": "project.update", + "commandId": "c1", + "projectId": "p1", + "pinned": true, + }) + ); + let unpin = Action::SetProjectPinned { + project_id: "p1".to_owned(), + pinned: false, + }; + let value = serde_json::to_value(unpin.payload("c2".to_owned())).expect("json"); + assert_eq!(value["pinned"], false); + } + + #[test] + fn settle_consumes_only_the_outcome_of_a_pending_action() { + let mut pending = HashMap::from([("7".to_owned(), "pin project")]); + let failed = |id: &str| Event::Failed { + request_id: id.to_owned(), + message: "no".to_owned(), + }; + assert!(!settle(&mut pending, &failed("0"))); + assert!(settle(&mut pending, &failed("7"))); + assert!(pending.is_empty()); + assert!(!settle(&mut pending, &failed("7"))); + } + #[test] fn socket_url_carries_protocol_and_escaped_ticket() { let url = socket_url("http://127.0.0.1:13773", "a b&c").expect("url"); diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs index 2c146d5e077ac..3c107ae90fcd1 100644 --- a/apps/desktop-gpui/src/main.rs +++ b/apps/desktop-gpui/src/main.rs @@ -1,3 +1,4 @@ +mod assets; mod auth; mod config; mod connection; @@ -9,10 +10,10 @@ mod status; mod ui; mod wire; +use assets::AppAssets; use config::{Attach, Config}; use gpui::{App, AppContext as _, Bounds, WindowBounds, WindowOptions, px, size}; use gpui_component::{Root, Theme, ThemeMode, TitleBar}; -use gpui_kit_assets::Assets; use gpui_platform::application; use server::Supervisor; use status::Status; @@ -26,6 +27,7 @@ fn main() { .build() .expect("failed to start the async runtime"); let (status_tx, status_rx) = async_channel::unbounded(); + let (action_tx, action_rx) = async_channel::unbounded(); let attach = Attach::from_env(); let supervisor = if attach.is_some() { None @@ -34,6 +36,7 @@ fn main() { Rc::new(Supervisor::start( runtime.handle(), config, + action_rx.clone(), status_tx.clone(), )) }) @@ -42,9 +45,16 @@ fn main() { runtime.spawn(async move { let client = auth::http_client(); match server::wait_until_ready(&client, &attach.http_base).await { - Ok(label) => { + Ok(environment) => { let credential = auth::Credential::Pairing(attach.pairing_token); - connection::run(&attach.http_base, &credential, &label, &status_tx).await; + connection::run( + &attach.http_base, + &credential, + &environment, + &action_rx, + &status_tx, + ) + .await; } Err(message) => status::publish(&status_tx, Status::Failed(message)), } @@ -56,42 +66,45 @@ fn main() { Status::NotConnected }; - application().with_assets(Assets).run(move |cx: &mut App| { - gpui_component::init(cx); - Theme::change(ThemeMode::Dark, None, cx); - if let Some(supervisor) = supervisor { - // The grace period outlasts gpui's quit timeout, so block here instead of awaiting. - cx.on_app_quit(move |_| { - supervisor.stop(); - async {} - }) - .detach(); - } - let bounds = Bounds::centered(None, size(px(960.), px(640.)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..TitleBar::window_options() - }, - |window, cx| { - let view = cx.new(|cx| { - cx.spawn(async move |this, cx| { - while let Ok(status) = status_rx.recv().await { - let updated = this - .update(cx, |view: &mut MainView, cx| view.set_status(status, cx)); - if updated.is_err() { - break; + application() + .with_assets(AppAssets) + .run(move |cx: &mut App| { + gpui_component::init(cx); + Theme::change(ThemeMode::Dark, None, cx); + if let Some(supervisor) = supervisor { + // The grace period outlasts gpui's quit timeout, so block here instead of awaiting. + cx.on_app_quit(move |_| { + supervisor.stop(); + async {} + }) + .detach(); + } + let bounds = Bounds::centered(None, size(px(960.), px(640.)), cx); + cx.open_window( + WindowOptions { + window_bounds: Some(WindowBounds::Windowed(bounds)), + ..TitleBar::window_options() + }, + |window, cx| { + let view = cx.new(|cx| { + cx.spawn(async move |this, cx| { + while let Ok(status) = status_rx.recv().await { + let updated = this.update(cx, |view: &mut MainView, cx| { + view.set_status(status, cx) + }); + if updated.is_err() { + break; + } } - } - }) - .detach(); - MainView::new(initial, window, cx) - }); - cx.new(|cx| Root::new(view, window, cx)) - }, - ) - .expect("failed to open the main window"); - cx.activate(true); - }); + }) + .detach(); + MainView::new(initial, action_tx, window, cx) + }); + cx.new(|cx| Root::new(view, window, cx)) + }, + ) + .expect("failed to open the main window"); + cx.activate(true); + }); drop(runtime); } diff --git a/apps/desktop-gpui/src/projects.rs b/apps/desktop-gpui/src/projects.rs index 7f0186db0913a..b5bcc13f8b87e 100644 --- a/apps/desktop-gpui/src/projects.rs +++ b/apps/desktop-gpui/src/projects.rs @@ -50,6 +50,12 @@ impl ProjectEntry { } } +/// The pin button's tooltip, with the default `thread.pin` shortcut. +pub fn pin_tooltip(name: &str, pinned: bool) -> String { + let verb = if pinned { "Unpin" } else { "Pin" }; + format!("{verb} {name} (\u{21e7}\u{2318}P)") +} + fn words(text: &str) -> Vec<&str> { text.split(|c: char| !c.is_alphanumeric()) .filter(|word| !word.is_empty()) @@ -194,6 +200,23 @@ pub fn picker_entries<'a>( listed } +/// Rows in the order they had when the picker opened (`useOrderWhileOpen`): ids that left drop +/// out, ids that arrived follow in their current order. +pub fn order_while_open<'a>( + listed: Vec<&'a ProjectEntry>, + opened_with: &[String], +) -> Vec<&'a ProjectEntry> { + let position = |entry: &ProjectEntry| { + opened_with + .iter() + .position(|id| *id == entry.id) + .unwrap_or(opened_with.len()) + }; + let mut ordered = listed; + ordered.sort_by_key(|entry| position(entry)); + ordered +} + #[cfg(test)] mod tests { use super::*; @@ -201,6 +224,43 @@ mod tests { const SNAPSHOT: &str = include_str!("../tests/fixtures/shell-snapshot.json"); + #[test] + fn order_while_open_keeps_the_opening_order_and_appends_new_rows() { + let entry = |id: &str, pinned: bool| ProjectEntry { + id: id.to_owned(), + title: id.to_owned(), + initials: String::new(), + color: 0, + pinned, + scratch: false, + }; + let opened = ["a", "b", "c"].map(str::to_owned); + let now = [entry("d", false), entry("c", true), entry("a", false)]; + let ids = |listed: Vec<&ProjectEntry>| -> Vec { + listed.into_iter().map(|e| e.id.clone()).collect() + }; + assert_eq!( + ids(order_while_open(now.iter().collect(), &opened)), + ["a", "c", "d"] + ); + assert_eq!( + ids(order_while_open(now.iter().collect(), &[])), + ["d", "c", "a"] + ); + } + + #[test] + fn pin_tooltip_names_the_action_and_shortcut() { + assert_eq!( + pin_tooltip("t3code", false), + "Pin t3code (\u{21e7}\u{2318}P)" + ); + assert_eq!( + pin_tooltip("t3code", true), + "Unpin t3code (\u{21e7}\u{2318}P)" + ); + } + fn project(id: &str, title: &str, edit: impl FnOnce(&mut Value)) -> Project { let mut value: Value = serde_json::from_str(SNAPSHOT).expect("json"); let mut project = value["snapshot"]["projects"][0].take(); diff --git a/apps/desktop-gpui/src/rpc.rs b/apps/desktop-gpui/src/rpc.rs index dd86ded87283e..f7bcc189ed916 100644 --- a/apps/desktop-gpui/src/rpc.rs +++ b/apps/desktop-gpui/src/rpc.rs @@ -26,6 +26,15 @@ impl Method for SubscribeShell { type Success = wire::ShellItem; } +/// `projects.mutate`, which carries `project.update` among its mutations. +pub struct ProjectsMutate; + +impl Method for ProjectsMutate { + const TAG: &'static str = "projects.mutate"; + type Payload = wire::ProjectMutation; + type Success = wire::ProjectsMutateSuccess; +} + /// Request ids are bigints on the wire, so they travel as strings. fn id_text<'de, D: Deserializer<'de>>(deserializer: D) -> Result { match Value::deserialize(deserializer)? { diff --git a/apps/desktop-gpui/src/server.rs b/apps/desktop-gpui/src/server.rs index e96963438d941..a752fadefd304 100644 --- a/apps/desktop-gpui/src/server.rs +++ b/apps/desktop-gpui/src/server.rs @@ -5,7 +5,7 @@ use crate::auth; use crate::config::{Config, ServerCommand}; -use crate::connection::{self, Backoff}; +use crate::connection::{self, ActionReceiver, Backoff, Environment}; use crate::status::{Status, StatusSender, publish}; use serde::{Deserialize, Serialize}; use std::io; @@ -162,10 +162,21 @@ fn forward_output(stream: impl AsyncRead + Unpin + Send + 'static) { #[derive(Deserialize)] struct Descriptor { label: String, + #[serde(default)] + capabilities: Capabilities, } -/// Polls until the descriptor answers, returning the environment label. -pub async fn wait_until_ready(client: &reqwest::Client, http_base: &str) -> Result { +#[derive(Default, Deserialize)] +struct Capabilities { + #[serde(rename = "projectOrganization", default)] + project_organization: bool, +} + +/// Polls until the descriptor answers, returning what the connection needs from it. +pub async fn wait_until_ready( + client: &reqwest::Client, + http_base: &str, +) -> Result { let url = format!("{http_base}{READINESS_PATH}"); let deadline = Instant::now() + READINESS_TIMEOUT; loop { @@ -178,7 +189,10 @@ pub async fn wait_until_ready(client: &reqwest::Client, http_base: &str) -> Resu && response.status().is_success() && let Ok(descriptor) = response.json::().await { - return Ok(descriptor.label); + return Ok(Environment { + label: descriptor.label, + project_organization: descriptor.capabilities.project_organization, + }); } if Instant::now() >= deadline { return Err(format!( @@ -214,12 +228,24 @@ async fn sleep_or_shutdown(delay: Duration, shutdown: &mut watch::Receiver } } -async fn session(config: &Config, secret: &str, status: &StatusSender) -> String { +async fn session( + config: &Config, + secret: &str, + actions: &ActionReceiver, + status: &StatusSender, +) -> String { let client = auth::http_client(); match wait_until_ready(&client, &config.http_base()).await { - Ok(label) => { + Ok(environment) => { let credential = auth::Credential::DesktopSecret(secret.to_owned()); - connection::run(&config.http_base(), &credential, &label, status).await; + connection::run( + &config.http_base(), + &credential, + &environment, + actions, + status, + ) + .await; unreachable_session() } Err(message) => message, @@ -232,7 +258,12 @@ fn unreachable_session() -> String { } /// Runs the server and its connection until shutdown, restarting after crashes. -pub async fn supervise(config: Config, status: StatusSender, mut shutdown: watch::Receiver) { +pub async fn supervise( + config: Config, + actions: ActionReceiver, + status: StatusSender, + mut shutdown: watch::Receiver, +) { let mut backoff = Backoff::new(); loop { publish(&status, Status::Starting); @@ -285,7 +316,7 @@ pub async fn supervise(config: Config, status: StatusSender, mut shutdown: watch None => "Server exited unexpectedly".to_owned(), } } - message = session(&config, &secret, &status) => { + message = session(&config, &secret, &actions, &status) => { terminate(&mut child).await; message } @@ -307,11 +338,16 @@ pub struct Supervisor { } impl Supervisor { - pub fn start(runtime: &tokio::runtime::Handle, config: Config, status: StatusSender) -> Self { + pub fn start( + runtime: &tokio::runtime::Handle, + config: Config, + actions: ActionReceiver, + status: StatusSender, + ) -> Self { let (shutdown, shutdown_rx) = watch::channel(false); let (done_tx, done_rx) = std::sync::mpsc::channel(); runtime.spawn(async move { - supervise(config, status, shutdown_rx).await; + supervise(config, actions, status, shutdown_rx).await; let _ = done_tx.send(()); }); Self { diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index f2880041decd2..0274a48ef221e 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -11,6 +11,8 @@ pub enum Status { /// Socket is up. `shell` stays `None` until the first shell snapshot lands. Connected { label: String, + /// The server keeps project pins, so the window offers them. + can_pin: bool, shell: Option, }, Failed(String), @@ -51,12 +53,15 @@ impl fmt::Display for Status { Self::NotConnected => f.write_str("Not connected"), Self::Starting => f.write_str("Starting server..."), Self::Connecting => f.write_str("Connecting..."), - Self::Connected { label, shell: None } => write!(f, "Connected to {label}"), + Self::Connected { + label, shell: None, .. + } => write!(f, "Connected to {label}"), Self::Connected { label, shell: Some(ShellView { projects, threads, .. }), + .. } => write!( f, "Connected to {label} \u{b7} {projects} {} \u{b7} {threads} {}", @@ -84,11 +89,13 @@ mod tests { let label = "Local".to_owned(); let waiting = Status::Connected { label: label.clone(), + can_pin: false, shell: None, }; assert_eq!(waiting.to_string(), "Connected to Local"); let known = Status::Connected { label: label.clone(), + can_pin: false, shell: Some(ShellView { projects: 2, threads: 14, @@ -101,6 +108,7 @@ mod tests { ); let single = Status::Connected { label, + can_pin: false, shell: Some(ShellView { projects: 1, threads: 1, diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index d25b1a639e183..78720aa53f5d7 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -1,13 +1,14 @@ +use crate::connection::{Action, ActionSender}; use crate::projects::{self, ProjectEntry}; use crate::status::{ShellView, Status, ThreadRow}; use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; use gpui::{ - Anchor, AnyElement, Context, Entity, Focusable as _, FontWeight, Hsla, IntoElement, Keystroke, - ParentElement, Render, Styled, UniformListScrollHandle, Window, div, prelude::*, px, rgb, - uniform_list, + Anchor, AnyElement, App, Context, Entity, Focusable as _, FontWeight, Hsla, IntoElement, + Keystroke, ParentElement, Render, RenderOnce, Styled, UniformListScrollHandle, Window, div, + prelude::*, px, rgb, uniform_list, }; use gpui_component::{ - ActiveTheme, Icon, IconName, Sizable, TitleBar, + ActiveTheme, Icon, IconName, Selectable, Sizable, TitleBar, breadcrumb::{Breadcrumb, BreadcrumbItem}, button::{Button, ButtonVariants}, h_flex, @@ -30,10 +31,17 @@ pub struct MainView { list: UniformListScrollHandle, selected_project: Option, picker_open: bool, + picker_order: Option>, + actions: ActionSender, } impl MainView { - pub fn new(status: Status, window: &mut Window, cx: &mut Context) -> Self { + pub fn new( + status: Status, + actions: ActionSender, + window: &mut Window, + cx: &mut Context, + ) -> Self { let project_search = cx.new(|cx| InputState::new(window, cx).placeholder("Search projects...")); cx.subscribe(&project_search, |_, _, event: &InputEvent, cx| { @@ -54,11 +62,20 @@ impl MainView { list: UniformListScrollHandle::new(), selected_project: None, picker_open: false, + picker_order: None, + actions, } } pub fn set_status(&mut self, status: Status, cx: &mut Context) { self.status = status; + // Thread activity reorders recency on every delta, so the default must not follow it. + if self.selected_project.is_none() { + self.selected_project = self + .shell() + .and_then(|shell| active_project(None, shell)) + .map(|entry| entry.id.clone()); + } cx.notify(); } @@ -69,6 +86,18 @@ impl MainView { } } + fn can_pin(&self) -> bool { + matches!(&self.status, Status::Connected { can_pin: true, .. }) + } + + fn picker_ids(&self) -> Option> { + let shell = self.shell()?; + let active = active_project(self.selected_project.as_deref(), shell); + let entries = &shell.project_entries; + let listed = projects::picker_entries(entries, active.map(|e| e.id.as_str()), ""); + Some(listed.into_iter().map(|entry| entry.id.clone()).collect()) + } + fn environment(&self) -> String { match &self.status { Status::Connected { label, .. } => label.clone(), @@ -158,6 +187,8 @@ impl MainView { cx: &mut Context, ) -> impl IntoElement { let view = cx.entity(); + let (actions, can_pin) = (self.actions.clone(), self.can_pin()); + let order = self.picker_order.clone(); let search = self.project_search.clone(); let picker = Popover::new("project-picker") .anchor(Anchor::TopCenter) @@ -170,19 +201,18 @@ impl MainView { search.update(cx, |state, cx| state.set_value("", window, cx)); view.update(cx, |this, cx| { this.picker_open = *open; + this.picker_order = open.then(|| this.picker_ids()).flatten(); cx.notify(); }); } }) - .trigger( - Button::new("project") - .ghost() - .tooltip(project.to_owned()) - .child(div().text_2xl().child(project.to_owned())), - ) + .trigger(ProjectTrigger::new(project)) .content(move |_, _, cx| { let query = search.read(cx).value(); - let listed = projects::picker_entries(&entries, active_id.as_deref(), &query); + let mut listed = projects::picker_entries(&entries, active_id.as_deref(), &query); + if let Some(order) = &order { + listed = projects::order_while_open(listed, order); + } let theme = cx.theme(); let (dark, muted) = (theme.is_dark(), theme.muted_foreground); let (hover, selected) = (theme.list_hover, theme.list_active); @@ -190,10 +220,13 @@ impl MainView { let border = theme.border; let rows = listed.iter().enumerate().map(|(index, entry)| { let (view, id) = (view.clone(), entry.id.clone()); + let (actions, pin_id) = (actions.clone(), entry.id.clone()); + let pinned = entry.pinned; let color: Hsla = rgb(entry.color_hex(dark)).into(); let active = active_id.as_deref() == Some(entry.id.as_str()); h_flex() .id(("project", index)) + .group(PICKER_ROW_GROUP) .gap_2() .px_2() .py_1p5() @@ -225,7 +258,10 @@ impl MainView { .truncate() .child(entry.title.clone()), ) - .when(entry.pinned, |row| { + .when(can_pin, |row| { + row.child(pin_button(index, entry, actions, pin_id)) + }) + .when(!can_pin && pinned, |row| { row.child( Icon::new(gpui_kit_assets::IconName::Pin) .small() @@ -311,17 +347,130 @@ impl MainView { .bordered(false), ) .child( - h_flex().justify_end().child( - Button::new("send") - .primary() - .rounded_full() - .icon(IconName::ArrowUp) - .on_click(|_, _, _| {}), - ), + h_flex() + .justify_between() + .child( + Button::new("attach") + .ghost() + .small() + .icon(gpui_kit_assets::IconName::Paperclip) + .tooltip("Attach files") + .on_click(|_, _, _| {}), + ) + .child( + Button::new("send") + .primary() + .rounded_full() + .icon(IconName::ArrowUp) + .on_click(|_, _, _| {}), + ), ) } } +/// The project name as plain heading text that underlines on hover and while its picker is open. +#[derive(IntoElement)] +struct ProjectTrigger { + name: String, + open: bool, +} + +impl ProjectTrigger { + fn new(name: &str) -> Self { + Self { + name: name.to_owned(), + open: false, + } + } +} + +impl Selectable for ProjectTrigger { + fn selected(self, _: bool) -> Self { + self + } + + fn is_selected(&self) -> bool { + false + } + + fn open(mut self, open: bool) -> Self { + self.open = open; + self + } + + fn is_open(&self) -> bool { + self.open + } +} + +impl RenderOnce for ProjectTrigger { + fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { + let tooltip = self.name.clone(); + div() + .id("project") + .ml_2() + .min_w_0() + .max_w_full() + .truncate() + .text_2xl() + .cursor_pointer() + .when(self.open, |name| name.underline()) + .hover(|style| style.underline()) + .tooltip(move |window, cx| Tooltip::new(tooltip.clone()).build(window, cx)) + .child(self.name) + } +} + +const PICKER_ROW_GROUP: &str = "project-row"; + +/// Shown on row hover, and always for a pinned project. A click toggles the pin and, because +/// `Button` stops propagation, does not select the row. +fn pin_button( + index: usize, + entry: &ProjectEntry, + actions: ActionSender, + project_id: String, +) -> impl IntoElement + use<> { + let pinned = entry.pinned; + let icon = |name| Icon::new(name).small(); + Button::new(("pin", index)) + .ghost() + .xsmall() + .tooltip(projects::pin_tooltip(&entry.title, pinned)) + .when(!pinned, |button| { + button + .invisible() + .group_hover(PICKER_ROW_GROUP, |style| style.visible()) + }) + .child(if pinned { + div() + .group("pin-icon") + .child( + div() + .group_hover("pin-icon", |style| style.invisible()) + .child(icon(gpui_kit_assets::IconName::Pin)), + ) + .child( + div() + .absolute() + .invisible() + .group_hover("pin-icon", |style| style.visible()) + .child(icon(gpui_kit_assets::IconName::PinOff)), + ) + } else { + div().child(icon(gpui_kit_assets::IconName::Pin)) + }) + .on_click(move |_, _, _| { + let action = Action::SetProjectPinned { + project_id: project_id.clone(), + pinned: !pinned, + }; + if actions.try_send(action).is_err() { + eprintln!("[ui] could not queue the pin request"); + } + }) +} + /// The picked project while it still exists, else the newest thread's project, else the first listed. fn active_project<'a>(selected: Option<&str>, shell: &'a ShellView) -> Option<&'a ProjectEntry> { let by_id = |id: &str| shell.project_entries.iter().find(|entry| entry.id == id); diff --git a/apps/desktop-gpui/src/wire/generated.rs b/apps/desktop-gpui/src/wire/generated.rs index 829e3bf68a6d9..2e7d70feef5eb 100644 --- a/apps/desktop-gpui/src/wire/generated.rs +++ b/apps/desktop-gpui/src/wire/generated.rs @@ -2097,6 +2097,67 @@ impl ::std::convert::TryFrom<::std::string::String> for ProjectIconKind { value.parse() } } +///`ProjectMutationErrorEncoded` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ProjectMutationErrorEncoded { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub cause: ::std::option::Option<::serde_json::Value>, + #[serde(rename = "commandId")] + pub command_id: ::std::string::String, + pub message: ::std::string::String, + #[serde(rename = "_tag")] + pub tag: ProjectMutationErrorEncodedTag, +} +///`ProjectMutationErrorEncodedTag` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectMutationErrorEncodedTag { + ProjectMutationError, +} +impl ::std::fmt::Display for ProjectMutationErrorEncodedTag { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ProjectMutationError => f.write_str("ProjectMutationError"), + } + } +} +impl ::std::str::FromStr for ProjectMutationErrorEncodedTag { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "ProjectMutationError" => Ok(Self::ProjectMutationError), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectMutationErrorEncodedTag { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for ProjectMutationErrorEncodedTag { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} ///`Projects` #[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] #[serde(transparent)] @@ -2222,6 +2283,755 @@ for ProjectsEntryDefaultThreadEnvMode { value.parse() } } +///`ProjectsMutateError` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum ProjectsMutateError { + ProjectMutationErrorEncoded(ProjectMutationErrorEncoded), + EnvironmentAuthorizationErrorEncoded(EnvironmentAuthorizationErrorEncoded), +} +impl ::std::convert::From for ProjectsMutateError { + fn from(value: ProjectMutationErrorEncoded) -> Self { + Self::ProjectMutationErrorEncoded(value) + } +} +impl ::std::convert::From for ProjectsMutateError { + fn from(value: EnvironmentAuthorizationErrorEncoded) -> Self { + Self::EnvironmentAuthorizationErrorEncoded(value) + } +} +///`ProjectsMutatePayload` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum ProjectsMutatePayload { + Variant0 { + #[serde(rename = "commandId")] + command_id: ::std::string::String, + #[serde( + rename = "createWorkspaceRootIfMissing", + skip_serializing_if = "::std::option::Option::is_none" + )] + create_workspace_root_if_missing: ::std::option::Option, + #[serde( + rename = "defaultModelSelection", + skip_serializing_if = "::std::option::Option::is_none" + )] + default_model_selection: ::std::option::Option, + #[serde(rename = "projectId")] + project_id: ::std::string::String, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + scripts: ::std::option::Option, + title: ::std::string::String, + #[serde(rename = "type")] + type_: ProjectsMutatePayloadVariant0Type, + #[serde(rename = "workspaceRoot")] + workspace_root: ::std::string::String, + }, + Variant1 { + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + archived: ::std::option::Option, + #[serde( + rename = "autoPull", + skip_serializing_if = "::std::option::Option::is_none" + )] + auto_pull: ::std::option::Option, + #[serde(rename = "commandId")] + command_id: ::std::string::String, + #[serde( + rename = "defaultModelSelection", + skip_serializing_if = "::std::option::Option::is_none" + )] + default_model_selection: ::std::option::Option, + #[serde( + rename = "defaultThreadEnvMode", + skip_serializing_if = "::std::option::Option::is_none" + )] + default_thread_env_mode: ::std::option::Option< + ProjectsMutatePayloadVariant1DefaultThreadEnvMode, + >, + #[serde( + rename = "faviconPath", + skip_serializing_if = "::std::option::Option::is_none" + )] + favicon_path: ::std::option::Option<::std::string::String>, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pinned: ::std::option::Option, + #[serde( + rename = "projectIcon", + skip_serializing_if = "::std::option::Option::is_none" + )] + project_icon: ::std::option::Option, + #[serde(rename = "projectId")] + project_id: ::std::string::String, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + scripts: ::std::option::Option, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + title: ::std::option::Option<::std::string::String>, + #[serde(rename = "type")] + type_: ProjectsMutatePayloadVariant1Type, + #[serde( + rename = "workspaceRoot", + skip_serializing_if = "::std::option::Option::is_none" + )] + workspace_root: ::std::option::Option<::std::string::String>, + }, + Variant2 { + #[serde(rename = "commandId")] + command_id: ::std::string::String, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + force: ::std::option::Option, + #[serde(rename = "projectId")] + project_id: ::std::string::String, + #[serde(rename = "type")] + type_: ProjectsMutatePayloadVariant2Type, + }, +} +///`ProjectsMutatePayloadVariant0Type` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutatePayloadVariant0Type { + #[serde(rename = "project.create")] + ProjectCreate, +} +impl ::std::fmt::Display for ProjectsMutatePayloadVariant0Type { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ProjectCreate => f.write_str("project.create"), + } + } +} +impl ::std::str::FromStr for ProjectsMutatePayloadVariant0Type { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "project.create" => Ok(Self::ProjectCreate), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectsMutatePayloadVariant0Type { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutatePayloadVariant0Type { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutatePayloadVariant1DefaultThreadEnvMode` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutatePayloadVariant1DefaultThreadEnvMode { + #[serde(rename = "local")] + Local, + #[serde(rename = "worktree")] + Worktree, +} +impl ::std::fmt::Display for ProjectsMutatePayloadVariant1DefaultThreadEnvMode { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Local => f.write_str("local"), + Self::Worktree => f.write_str("worktree"), + } + } +} +impl ::std::str::FromStr for ProjectsMutatePayloadVariant1DefaultThreadEnvMode { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "local" => Ok(Self::Local), + "worktree" => Ok(Self::Worktree), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> +for ProjectsMutatePayloadVariant1DefaultThreadEnvMode { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutatePayloadVariant1DefaultThreadEnvMode { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutatePayloadVariant1ProjectIcon` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum ProjectsMutatePayloadVariant1ProjectIcon { + Variant0 { + color: ProjectsMutatePayloadVariant1ProjectIconVariant0Color, + kind: ProjectsMutatePayloadVariant1ProjectIconVariant0Kind, + name: ::std::string::String, + }, + Variant1 { + emoji: ::std::string::String, + kind: ProjectsMutatePayloadVariant1ProjectIconVariant1Kind, + }, + Variant2(ProjectIcon2), + Variant3, +} +impl ::std::convert::From for ProjectsMutatePayloadVariant1ProjectIcon { + fn from(value: ProjectIcon2) -> Self { + Self::Variant2(value) + } +} +///`ProjectsMutatePayloadVariant1ProjectIconVariant0Color` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutatePayloadVariant1ProjectIconVariant0Color { + #[serde(rename = "gray")] + Gray, + #[serde(rename = "red")] + Red, + #[serde(rename = "orange")] + Orange, + #[serde(rename = "amber")] + Amber, + #[serde(rename = "yellow")] + Yellow, + #[serde(rename = "lime")] + Lime, + #[serde(rename = "green")] + Green, + #[serde(rename = "emerald")] + Emerald, + #[serde(rename = "teal")] + Teal, + #[serde(rename = "cyan")] + Cyan, + #[serde(rename = "sky")] + Sky, + #[serde(rename = "blue")] + Blue, + #[serde(rename = "indigo")] + Indigo, + #[serde(rename = "violet")] + Violet, + #[serde(rename = "purple")] + Purple, + #[serde(rename = "fuchsia")] + Fuchsia, + #[serde(rename = "pink")] + Pink, + #[serde(rename = "rose")] + Rose, +} +impl ::std::fmt::Display for ProjectsMutatePayloadVariant1ProjectIconVariant0Color { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Gray => f.write_str("gray"), + Self::Red => f.write_str("red"), + Self::Orange => f.write_str("orange"), + Self::Amber => f.write_str("amber"), + Self::Yellow => f.write_str("yellow"), + Self::Lime => f.write_str("lime"), + Self::Green => f.write_str("green"), + Self::Emerald => f.write_str("emerald"), + Self::Teal => f.write_str("teal"), + Self::Cyan => f.write_str("cyan"), + Self::Sky => f.write_str("sky"), + Self::Blue => f.write_str("blue"), + Self::Indigo => f.write_str("indigo"), + Self::Violet => f.write_str("violet"), + Self::Purple => f.write_str("purple"), + Self::Fuchsia => f.write_str("fuchsia"), + Self::Pink => f.write_str("pink"), + Self::Rose => f.write_str("rose"), + } + } +} +impl ::std::str::FromStr for ProjectsMutatePayloadVariant1ProjectIconVariant0Color { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "gray" => Ok(Self::Gray), + "red" => Ok(Self::Red), + "orange" => Ok(Self::Orange), + "amber" => Ok(Self::Amber), + "yellow" => Ok(Self::Yellow), + "lime" => Ok(Self::Lime), + "green" => Ok(Self::Green), + "emerald" => Ok(Self::Emerald), + "teal" => Ok(Self::Teal), + "cyan" => Ok(Self::Cyan), + "sky" => Ok(Self::Sky), + "blue" => Ok(Self::Blue), + "indigo" => Ok(Self::Indigo), + "violet" => Ok(Self::Violet), + "purple" => Ok(Self::Purple), + "fuchsia" => Ok(Self::Fuchsia), + "pink" => Ok(Self::Pink), + "rose" => Ok(Self::Rose), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> +for ProjectsMutatePayloadVariant1ProjectIconVariant0Color { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutatePayloadVariant1ProjectIconVariant0Color { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutatePayloadVariant1ProjectIconVariant0Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutatePayloadVariant1ProjectIconVariant0Kind { + #[serde(rename = "lucide")] + Lucide, +} +impl ::std::fmt::Display for ProjectsMutatePayloadVariant1ProjectIconVariant0Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Lucide => f.write_str("lucide"), + } + } +} +impl ::std::str::FromStr for ProjectsMutatePayloadVariant1ProjectIconVariant0Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "lucide" => Ok(Self::Lucide), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> +for ProjectsMutatePayloadVariant1ProjectIconVariant0Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutatePayloadVariant1ProjectIconVariant0Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutatePayloadVariant1ProjectIconVariant1Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutatePayloadVariant1ProjectIconVariant1Kind { + #[serde(rename = "emoji")] + Emoji, +} +impl ::std::fmt::Display for ProjectsMutatePayloadVariant1ProjectIconVariant1Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Emoji => f.write_str("emoji"), + } + } +} +impl ::std::str::FromStr for ProjectsMutatePayloadVariant1ProjectIconVariant1Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "emoji" => Ok(Self::Emoji), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> +for ProjectsMutatePayloadVariant1ProjectIconVariant1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutatePayloadVariant1ProjectIconVariant1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutatePayloadVariant1Type` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutatePayloadVariant1Type { + #[serde(rename = "project.update")] + ProjectUpdate, +} +impl ::std::fmt::Display for ProjectsMutatePayloadVariant1Type { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ProjectUpdate => f.write_str("project.update"), + } + } +} +impl ::std::str::FromStr for ProjectsMutatePayloadVariant1Type { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "project.update" => Ok(Self::ProjectUpdate), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectsMutatePayloadVariant1Type { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutatePayloadVariant1Type { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutatePayloadVariant2Type` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutatePayloadVariant2Type { + #[serde(rename = "project.delete")] + ProjectDelete, +} +impl ::std::fmt::Display for ProjectsMutatePayloadVariant2Type { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::ProjectDelete => f.write_str("project.delete"), + } + } +} +impl ::std::str::FromStr for ProjectsMutatePayloadVariant2Type { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "project.delete" => Ok(Self::ProjectDelete), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectsMutatePayloadVariant2Type { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutatePayloadVariant2Type { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutateSuccess` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct ProjectsMutateSuccess { + #[serde( + rename = "archivedAt", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub archived_at: ::std::option::Option<::std::string::String>, + #[serde(rename = "autoPull", skip_serializing_if = "::std::option::Option::is_none")] + pub auto_pull: ::std::option::Option, + #[serde(rename = "createdAt")] + pub created_at: ::std::string::String, + #[serde( + rename = "defaultModelSelection", + deserialize_with = "::std::option::Option::deserialize" + )] + pub default_model_selection: ::std::option::Option, + #[serde( + rename = "defaultThreadEnvMode", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub default_thread_env_mode: ::std::option::Option< + ProjectsMutateSuccessDefaultThreadEnvMode, + >, + #[serde( + rename = "deletedAt", + deserialize_with = "::std::option::Option::deserialize" + )] + pub deleted_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "faviconPath", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub favicon_path: ::std::option::Option<::std::string::String>, + pub id: ::std::string::String, + #[serde(rename = "pinnedAt", skip_serializing_if = "::std::option::Option::is_none")] + pub pinned_at: ::std::option::Option<::std::string::String>, + #[serde( + rename = "projectIcon", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub project_icon: ::std::option::Option, + #[serde( + rename = "repositoryIdentity", + skip_serializing_if = "::std::option::Option::is_none" + )] + pub repository_identity: ::std::option::Option, + pub scripts: DefaultProjectScripts, + pub title: ::std::string::String, + #[serde(rename = "updatedAt")] + pub updated_at: ::std::string::String, + #[serde(rename = "workspaceRoot")] + pub workspace_root: ::std::string::String, +} +///`ProjectsMutateSuccessDefaultThreadEnvMode` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutateSuccessDefaultThreadEnvMode { + #[serde(rename = "local")] + Local, + #[serde(rename = "worktree")] + Worktree, +} +impl ::std::fmt::Display for ProjectsMutateSuccessDefaultThreadEnvMode { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Local => f.write_str("local"), + Self::Worktree => f.write_str("worktree"), + } + } +} +impl ::std::str::FromStr for ProjectsMutateSuccessDefaultThreadEnvMode { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "local" => Ok(Self::Local), + "worktree" => Ok(Self::Worktree), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectsMutateSuccessDefaultThreadEnvMode { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutateSuccessDefaultThreadEnvMode { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +///`ProjectsMutateSuccessProjectIcon` +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum ProjectsMutateSuccessProjectIcon { + Variant0(ProjectIcon), + Variant1 { + emoji: ::std::string::String, + kind: ProjectsMutateSuccessProjectIconVariant1Kind, + }, + Variant2(ProjectIcon2), + Variant3 { kind: ::std::string::String }, + Variant4, +} +impl ::std::convert::From for ProjectsMutateSuccessProjectIcon { + fn from(value: ProjectIcon) -> Self { + Self::Variant0(value) + } +} +impl ::std::convert::From for ProjectsMutateSuccessProjectIcon { + fn from(value: ProjectIcon2) -> Self { + Self::Variant2(value) + } +} +///`ProjectsMutateSuccessProjectIconVariant1Kind` +#[derive( + ::serde::Deserialize, + ::serde::Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd +)] +pub enum ProjectsMutateSuccessProjectIconVariant1Kind { + #[serde(rename = "emoji")] + Emoji, +} +impl ::std::fmt::Display for ProjectsMutateSuccessProjectIconVariant1Kind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Self::Emoji => f.write_str("emoji"), + } + } +} +impl ::std::str::FromStr for ProjectsMutateSuccessProjectIconVariant1Kind { + type Err = self::error::ConversionError; + fn from_str( + value: &str, + ) -> ::std::result::Result { + match value { + "emoji" => Ok(Self::Emoji), + _ => Err("invalid value".into()), + } + } +} +impl ::std::convert::TryFrom<&str> for ProjectsMutateSuccessProjectIconVariant1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: &str, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> +for ProjectsMutateSuccessProjectIconVariant1Kind { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} ///`PullRequests` #[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] #[serde(transparent)] diff --git a/apps/desktop-gpui/src/wire/mod.rs b/apps/desktop-gpui/src/wire/mod.rs index f1c9f2d703b5b..6a918bb921fea 100644 --- a/apps/desktop-gpui/src/wire/mod.rs +++ b/apps/desktop-gpui/src/wire/mod.rs @@ -7,5 +7,7 @@ pub mod generated; pub use generated::{ OrchestrationSubscribeShellPayload as SubscribeShellPayload, OrchestrationSubscribeShellSuccess as ShellItem, ProjectsEntry as Project, + ProjectsMutatePayload as ProjectMutation, + ProjectsMutatePayloadVariant1Type as ProjectMutationType, ProjectsMutateSuccess, ThreadsEntry as Thread, }; diff --git a/apps/desktop-gpui/tests/wire_types.rs b/apps/desktop-gpui/tests/wire_types.rs index 88f87240ef780..658107540796f 100644 --- a/apps/desktop-gpui/tests/wire_types.rs +++ b/apps/desktop-gpui/tests/wire_types.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; /// RPC methods whose payload, success and error types are generated. Add a method here when the /// client starts calling it. -const METHODS: &[&str] = &["orchestration.subscribeShell"]; +const METHODS: &[&str] = &["orchestration.subscribeShell", "projects.mutate"]; /// Properties typify cannot represent, kept as `serde_json::Value`: (definition, property). /// Gaps are listed in the README. From 79d658784424393a1e245d252db54606212ca0ec Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 02:23:19 -0500 Subject: [PATCH 09/14] feat(desktop-gpui): group projects by repository and badge other environments The picker listed every project on its own and never showed which other machines had a project, so it disagreed with the web. Projects now group by repository the way the web's default mode does, with the same label and icon rules, and pinning a group pins every member. Saved environments from the Electron app's connection catalog are read, so a project that also exists elsewhere gets the "Also on" badge. Reading Electron's catalog and DPoP key is a stopgap marked TODO(gpui-credentials) until this app has its own pairing. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/Cargo.lock | 233 ++++++++ apps/desktop-gpui/Cargo.toml | 8 + apps/desktop-gpui/README.md | 9 + apps/desktop-gpui/src/assets.rs | 14 +- apps/desktop-gpui/src/auth.rs | 41 +- apps/desktop-gpui/src/catalog.rs | 635 ++++++++++++++++++++++ apps/desktop-gpui/src/config.rs | 33 ++ apps/desktop-gpui/src/connection.rs | 40 +- apps/desktop-gpui/src/electron_dpop.rs | 720 +++++++++++++++++++++++++ apps/desktop-gpui/src/environments.rs | 92 ++++ apps/desktop-gpui/src/main.rs | 11 + apps/desktop-gpui/src/projects.rs | 598 ++++++++++++++++++-- apps/desktop-gpui/src/server.rs | 56 +- apps/desktop-gpui/src/status.rs | 5 +- apps/desktop-gpui/src/ui.rs | 95 +++- 15 files changed, 2514 insertions(+), 76 deletions(-) create mode 100644 apps/desktop-gpui/src/catalog.rs create mode 100644 apps/desktop-gpui/src/electron_dpop.rs create mode 100644 apps/desktop-gpui/src/environments.rs diff --git a/apps/desktop-gpui/Cargo.lock b/apps/desktop-gpui/Cargo.lock index 1e3e164a6a4d0..23c2051c8ed5a 100644 --- a/apps/desktop-gpui/Cargo.lock +++ b/apps/desktop-gpui/Cargo.lock @@ -463,6 +463,12 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base62" version = "2.2.6" @@ -481,6 +487,12 @@ version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bit-set" version = "0.10.0" @@ -1004,6 +1016,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1062,6 +1080,22 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-bigint" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c035cb64cba03dad2fb921f3e218785e8cf9a41790da5eb3a1400f62fa2be11" +dependencies = [ + "cpubits", + "ctutils", + "getrandom 0.4.3", + "hybrid-array", + "num-traits", + "rand_core 0.10.1", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -1078,7 +1112,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ + "getrandom 0.4.3", "hybrid-array", + "rand_core 0.10.1", ] [[package]] @@ -1098,6 +1134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "261af4dc7a06ea8a527a8717d8cbba3417530d6ea14e1edb3de6b67806be474f" dependencies = [ "cmov", + "subtle", ] [[package]] @@ -1112,6 +1149,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" +[[package]] +name = "der" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a996a108feb8b427a6e12fcf6af4aa83409b79d72c15337d7741368131ee004" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.9" @@ -1275,12 +1322,47 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "ecdsa" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0" +dependencies = [ + "der", + "digest 0.11.3", + "elliptic-curve", + "rfc6979", + "signature", + "spki", + "zeroize", +] + [[package]] name = "either" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" +[[package]] +name = "elliptic-curve" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900f37e7e18d74a2347389af6cd69e3a30d5cc9e697f40004865679aa75e9c7c" +dependencies = [ + "base16ct", + "crypto-bigint", + "crypto-common 0.2.2", + "digest 0.11.3", + "ff", + "group", + "hybrid-array", + "pkcs8", + "rand_core 0.10.1", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "embed-resource" version = "3.0.12" @@ -1489,6 +1571,16 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ff" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" +dependencies = [ + "rand_core 0.10.1", + "subtle", +] + [[package]] name = "find-msvc-tools" version = "0.1.15" @@ -2517,6 +2609,17 @@ dependencies = [ "smallvec", ] +[[package]] +name = "group" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" +dependencies = [ + "ff", + "rand_core 0.10.1", + "subtle", +] + [[package]] name = "h2" version = "0.4.20" @@ -2710,7 +2813,9 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" dependencies = [ + "subtle", "typenum", + "zeroize", ] [[package]] @@ -4334,6 +4439,19 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "p256" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.1", +] + [[package]] name = "parking" version = "2.2.1" @@ -4491,6 +4609,16 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.34" @@ -4615,6 +4743,33 @@ dependencies = [ "syn 3.0.9", ] +[[package]] +name = "primefield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4" +dependencies = [ + "crypto-bigint", + "crypto-common 0.2.2", + "ff", + "rand_core 0.10.1", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06" +dependencies = [ + "elliptic-curve", + "once_cell", + "primefield", + "serdect", + "wnaf", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -5171,6 +5326,16 @@ dependencies = [ "zune-jpeg 0.5.15", ] +[[package]] +name = "rfc6979" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b" +dependencies = [ + "crypto-bigint", + "hmac 0.13.0", +] + [[package]] name = "rgb" version = "0.8.53" @@ -5561,6 +5726,20 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "sec1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" +dependencies = [ + "base16ct", + "ctutils", + "der", + "hybrid-array", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "3.7.0" @@ -5747,6 +5926,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha1" version = "0.11.0" @@ -5811,6 +6000,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core 0.10.1", +] + [[package]] name = "simd-adler32" version = "0.3.10" @@ -5934,6 +6133,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "snap" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "199905e6153d6405f9728fe44daace35f8f837bbf830bb6e85fbd5828709a886" + [[package]] name = "socket2" version = "0.6.5" @@ -5971,6 +6176,16 @@ dependencies = [ "bitflags 2.13.2", ] +[[package]] +name = "spki" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef958a98b9d5da290cfc78946e9f3e61e1e62a18db0d92cac0b83cc161491a9" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -6177,7 +6392,9 @@ dependencies = [ name = "t3-desktop-gpui" version = "0.1.0" dependencies = [ + "aes", "async-channel", + "cbc", "futures-util", "getrandom 0.4.3", "gpui-component", @@ -6187,12 +6404,16 @@ dependencies = [ "hex", "hmac 0.13.0", "libc", + "p256", "prettyplease", "reqwest", "schemars 0.8.22", + "security-framework", "serde", "serde_json", + "sha1", "sha2 0.11.1", + "snap", "syn 3.0.9", "tokio", "tokio-tungstenite", @@ -7992,6 +8213,18 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "wnaf" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795ca18b3fdb5e62bf982199278341ddcf7ebf7d32e25e212ad05d496e95f6fa" +dependencies = [ + "ff", + "group", + "hybrid-array", + "primefield", +] + [[package]] name = "writeable" version = "0.6.4" diff --git a/apps/desktop-gpui/Cargo.toml b/apps/desktop-gpui/Cargo.toml index 2d27e6b534c73..a28db8ac1b16e 100644 --- a/apps/desktop-gpui/Cargo.toml +++ b/apps/desktop-gpui/Cargo.toml @@ -6,7 +6,9 @@ license = "MIT" publish = false [dependencies] +aes = "0.8.4" async-channel = "2.5.0" +cbc = { version = "0.1.2", features = ["alloc"] } futures-util = "0.3.34" getrandom = "0.4.3" gpui = { package = "gpui-pre", version = "=0.3.11" } @@ -16,10 +18,13 @@ gpui_platform = { package = "gpui-pre-platform", version = "=0.3.11", features = hex = "0.4.3" hmac = "0.13.0" libc = "0.2.191" +p256 = { version = "0.14", default-features = false, features = ["ecdsa", "pkcs8", "std"] } reqwest = { version = "0.13.5", default-features = false, features = ["form", "json", "rustls"] } serde = { version = "1.0.229", features = ["derive"] } serde_json = "1.0.154" +sha1 = "0.11.0" sha2 = "0.11.1" +snap = "1" tokio = { version = "1.54.1", features = [ "rt-multi-thread", "process", @@ -40,3 +45,6 @@ prettyplease = "0.3.0" schemars = "0.8" syn = "3.0.9" typify = "0.10.0-alpha.3" + +[target.'cfg(target_os = "macos")'.dependencies] +security-framework = "3.7.0" diff --git a/apps/desktop-gpui/README.md b/apps/desktop-gpui/README.md index 7f7fa8b2ea0a6..f4647bc2a7dc5 100644 --- a/apps/desktop-gpui/README.md +++ b/apps/desktop-gpui/README.md @@ -30,6 +30,15 @@ To see real data, attach to a server that is already running (for example your ` | `T3CODE_GPUI_SERVER_URL` | HTTP origin of the running server. Takes precedence. | | `T3CODE_GPUI_PAIRING_TOKEN` | The `Token:` value printed by `pair`. It works only once. | +When attached, the project picker also shows the Electron app's environment badge ("Also on ") for projects that exist on your other saved environments. It reads the Electron app's `connection-catalog.json`, decrypting it with the safeStorage key from the macOS Keychain (the OS may ask for access once). Only direct `http` environments with a saved bearer token are used; relay, T3 Connect, SSH and `https` entries are skipped with a log line. + +| Variable | Meaning | +| -------------------------------- | ------------------------------------------------------------------------------------ | +| `T3CODE_GPUI_CONNECTION_CATALOG` | Catalog path. Defaults to `connection-catalog.json` in `T3CODE_GPUI_HOME`. | +| `T3CODE_GPUI_ELECTRON_IDB` | Electron IndexedDB LevelDB directory (DPoP key). Defaults to the `t3code-dev` app's. | + +TODO(gpui-credentials): HACK. Saved T3 Connect tokens (`remoteDpopTokens`) are used by reading the Electron app's private IndexedDB for its non-extractable DPoP key (`src/electron_dpop.rs`). Replace with GPUI's own pairing + Keychain before shipping. + Other scripts: ```sh diff --git a/apps/desktop-gpui/src/assets.rs b/apps/desktop-gpui/src/assets.rs index 9cca92b6de3f0..dc388b45670d4 100644 --- a/apps/desktop-gpui/src/assets.rs +++ b/apps/desktop-gpui/src/assets.rs @@ -2,7 +2,12 @@ use gpui::{AssetSource, Result, SharedString}; use std::borrow::Cow; // `Assets` carries only gpui-component's default icons; anything else from the catalog renders blank. -gpui_kit_assets::icon_assets!(ExtraIcons, [Paperclip, Pin, PinOff]); +gpui_kit_assets::icon_assets!( + ExtraIcons, + [ + Paperclip, Pin, PinOff, Server, Cloud, Terminal, Monitor, Laptop, HardDrive, Computer + ] +); pub struct AppAssets; @@ -33,6 +38,13 @@ mod tests { IconName::Pin, IconName::PinOff, IconName::Search, + IconName::Server, + IconName::Cloud, + IconName::Terminal, + IconName::Monitor, + IconName::Laptop, + IconName::HardDrive, + IconName::Computer, ] { let loaded = AppAssets.load(&icon.path()).ok().flatten(); assert!(loaded.is_some(), "{} is not embedded", icon.path()); diff --git a/apps/desktop-gpui/src/auth.rs b/apps/desktop-gpui/src/auth.rs index 20ad54b2b85a9..fd5c0ed429df6 100644 --- a/apps/desktop-gpui/src/auth.rs +++ b/apps/desktop-gpui/src/auth.rs @@ -2,10 +2,13 @@ //! Mirrors packages/shared/src/desktopBootstrapToken.ts and //! packages/client-runtime/src/authorization/remote.ts. +use crate::electron_dpop; use hmac::{Hmac, KeyInit, Mac}; +use p256::ecdsa::SigningKey; use serde::Deserialize; use sha2::Sha256; use std::fmt::Write; +use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; const TOKEN_WINDOW_MS: u64 = 12 * 60 * 60 * 1000; @@ -80,17 +83,25 @@ struct TicketResult { } /// What the app proves itself with: the secret it handed a server it spawned, -/// or a pairing token minted by `t3 pair` for a server it attaches to. +/// a pairing token minted by `t3 pair` for a server it attaches to, or a bearer +/// session another client already saved for it (the Electron app's catalog). pub enum Credential { DesktopSecret(String), Pairing(String), + Bearer(String), + // TODO(gpui-credentials): HACK. Reads Electron's private IndexedDB and non-extractable DPoP key. Replace with GPUI's own pairing + Keychain before shipping. + /// A DPoP-bound access token and the proof key it is bound to (`Authorization: DPoP`). + Dpop { + token: String, + key: Arc, + }, } impl Credential { fn subject_token(&self) -> String { match self { Self::DesktopSecret(secret) => bootstrap_token(secret, now_ms()), - Self::Pairing(token) => token.clone(), + Self::Pairing(token) | Self::Bearer(token) | Self::Dpop { token, .. } => token.clone(), } } } @@ -101,6 +112,10 @@ pub async fn exchange_bearer( http_base: &str, credential: &Credential, ) -> Result { + // A saved bearer or DPoP access token is already the session; there is nothing to exchange. + if let Credential::Bearer(token) | Credential::Dpop { token, .. } = credential { + return Ok(token.clone()); + } let credential = credential.subject_token(); let response = client .post(format!("{http_base}/oauth/token")) @@ -123,18 +138,28 @@ pub async fn exchange_bearer( } /// Mints an RPC WebSocket ticket. A server without audience routes answers 404 -/// for `/:use`, and its bare-path tickets are valid for RPC. +/// for `/:use`, and its bare-path tickets are valid for RPC. A DPoP credential signs a proof for +/// each path it tries (packages/client-runtime/src/authorization/remote.ts). pub async fn issue_ws_ticket( client: &reqwest::Client, http_base: &str, + credential: &Credential, bearer: &str, ) -> Result { let mint = |path: &'static str| async move { - client - .post(format!("{http_base}{path}")) - .bearer_auth(bearer) - .send() - .await + let url = format!("{http_base}{path}"); + let request = client.post(&url); + let request = match credential { + Credential::Dpop { key, .. } => { + let proof = electron_dpop::create_proof(key, "POST", &url, Some(bearer), now_ms()) + .map_err(AuthError::Failed)?; + request + .header("authorization", format!("DPoP {bearer}")) + .header("dpop", proof) + } + _ => request.bearer_auth(bearer), + }; + request.send().await.map_err(AuthError::from) }; let mut response = mint("/api/auth/websocket-ticket/rpc").await?; if response.status() == reqwest::StatusCode::NOT_FOUND { diff --git a/apps/desktop-gpui/src/catalog.rs b/apps/desktop-gpui/src/catalog.rs new file mode 100644 index 0000000000000..c57805df4bde1 --- /dev/null +++ b/apps/desktop-gpui/src/catalog.rs @@ -0,0 +1,635 @@ +//! The Electron desktop app's saved connections: `connection-catalog.json` holds the catalog +//! encrypted with Electron safeStorage (DesktopConnectionCatalogStore.ts). The plaintext schema is +//! `ConnectionCatalogDocument` in packages/client-runtime/src/platform/storageDocument.ts. +//! Read-only: this module never writes the catalog and never logs a decrypted value. +//! +//! TODO(gpui-credentials): HACK. Reads Electron's private IndexedDB and non-extractable DPoP key. Replace with GPUI's own pairing + Keychain before shipping. +//! (`remoteDpopTokens` are only usable with Electron's DPoP key; see `electron_dpop`.) + +use crate::auth::{self, Credential}; +use crate::electron_dpop; +use aes::cipher::{BlockDecryptMut, KeyIvInit, block_padding::Pkcs7}; +use hmac::{Hmac, KeyInit, Mac}; +use p256::ecdsa::SigningKey; +use serde::Deserialize; +use sha1::Sha1; +use std::collections::HashMap; +use std::path::Path; +use std::sync::Arc; + +type Aes128CbcDec = cbc::Decryptor; + +/// Chromium os_crypt_mac: key = PBKDF2-HMAC-SHA1(password, "saltysalt", 1003) of 16 bytes, +/// IV = 16 spaces, ciphertext prefixed with "v10". +const SAFE_STORAGE_PREFIX: &[u8] = b"v10"; +const SAFE_STORAGE_SALT: &[u8] = b"saltysalt"; +const SAFE_STORAGE_ITERATIONS: u32 = 1003; + +/// Keychain items Electron's safeStorage uses as (service, account). The service is named after +/// the app at launch: a dev launcher that runs the stock Electron binary stores `Electron`. +#[cfg(target_os = "macos")] +const KEYCHAIN_ITEMS: [(&str, &str); 2] = [ + ("Electron Safe Storage", "Electron Key"), + ("T3 Code (Dev) Safe Storage", "T3 Code (Dev) Key"), +]; + +pub const CATALOG_FILE: &str = "connection-catalog.json"; + +/// A directly reachable environment: a plain `http` base URL and the bearer token saved for it. +pub struct SavedEnvironment { + pub environment_id: String, + pub label: String, + pub http_base: String, + pub credential: Credential, +} + +/// What `load` found: environments to connect to, and the `(label, kind)` of those it cannot. +#[derive(Default)] +pub struct Catalog { + pub environments: Vec, + pub skipped: Vec<(String, &'static str)>, + /// One-line explanations for environments that are skipped for a specific reason. + pub notices: Vec, +} + +#[derive(Deserialize)] +struct Envelope { + version: u32, + #[serde(rename = "encryptedCatalog")] + encrypted_catalog: String, +} + +// Variant names are the wire `_tag` values. +#[allow(clippy::enum_variant_names)] +#[derive(Deserialize)] +#[serde(tag = "_tag")] +enum Target { + BearerConnectionTarget { + #[serde(rename = "environmentId")] + environment_id: String, + label: String, + #[serde(rename = "connectionId")] + connection_id: String, + }, + RelayConnectionTarget { + label: String, + }, + SshConnectionTarget { + label: String, + }, + #[serde(other)] + Unknown, +} + +#[derive(Deserialize)] +#[serde(tag = "_tag")] +enum Profile { + BearerConnectionProfile { + #[serde(rename = "connectionId")] + connection_id: String, + #[serde(rename = "httpBaseUrl")] + http_base_url: String, + authorization: Option, + }, + #[serde(other)] + Other, +} + +#[derive(Deserialize)] +#[serde(tag = "_tag")] +enum StoredToken { + BearerConnectionCredential { + token: String, + }, + #[serde(other)] + Other, +} + +#[derive(Deserialize)] +struct StoredCredential { + #[serde(rename = "connectionId")] + connection_id: String, + credential: StoredToken, +} + +/// `RemoteDpopAccessToken` (packages/client-runtime/src/authorization/tokenStore.ts). +#[derive(Deserialize)] +struct DpopToken { + #[serde(rename = "environmentId")] + environment_id: String, + endpoint: DpopEndpoint, + #[serde(rename = "accessToken")] + access_token: String, + #[serde(rename = "expiresAtEpochMs")] + expires_at_epoch_ms: f64, + #[serde(rename = "dpopThumbprint")] + dpop_thumbprint: String, +} + +#[derive(Deserialize)] +struct DpopEndpoint { + #[serde(rename = "httpBaseUrl")] + http_base_url: String, +} + +#[derive(Deserialize)] +struct Document { + #[serde(default)] + targets: Vec, + #[serde(default)] + profiles: Vec, + #[serde(default)] + credentials: Vec, + #[serde(rename = "remoteDpopTokens", default)] + remote_dpop_tokens: Vec, + #[serde(rename = "disabledEnvironmentIds", default)] + disabled_environment_ids: Vec, +} + +/// Why a bearer target cannot be dialed directly, as a short kind for the log. +fn bearer_kind(http_base: &str, authorization: Option<&str>) -> Option<&'static str> { + if authorization == Some("t3-connect") { + return Some("t3-connect"); + } + // tokio-tungstenite is built without TLS, so a wss socket cannot open. + (!http_base.starts_with("http://")).then_some("https") +} + +type KeyLoader<'a> = &'a mut dyn FnMut() -> Result, String>; + +/// Decodes the decrypted catalog JSON into the environments this client can reach. +/// `load_key` supplies Electron's DPoP key, and is called at most once, only when a saved +/// DPoP token is worth using. +pub fn parse(plaintext: &str, now_ms: u64, load_key: KeyLoader) -> Result { + // serde messages can quote the offending value, and this document holds tokens. + let document: Document = serde_json::from_str(plaintext).map_err(|error| { + format!( + "unreadable catalog document at {}:{}", + error.line(), + error.column() + ) + })?; + let profiles: HashMap<&str, (&str, Option<&str>)> = document + .profiles + .iter() + .filter_map(|profile| match profile { + Profile::BearerConnectionProfile { + connection_id, + http_base_url, + authorization, + } => Some(( + connection_id.as_str(), + (http_base_url.as_str(), authorization.as_deref()), + )), + Profile::Other => None, + }) + .collect(); + let tokens: HashMap<&str, &str> = document + .credentials + .iter() + .filter_map(|stored| match &stored.credential { + StoredToken::BearerConnectionCredential { token } => { + Some((stored.connection_id.as_str(), token.as_str())) + } + StoredToken::Other => None, + }) + .collect(); + let dpop_tokens: HashMap<&str, &DpopToken> = document + .remote_dpop_tokens + .iter() + .map(|token| (token.environment_id.as_str(), token)) + .collect(); + let mut key: Option, String>> = None; + let mut catalog = Catalog::default(); + for target in &document.targets { + match target { + Target::RelayConnectionTarget { label } => { + catalog.skipped.push((label.clone(), "relay")); + } + Target::SshConnectionTarget { label } => catalog.skipped.push((label.clone(), "ssh")), + Target::Unknown => {} + Target::BearerConnectionTarget { + environment_id, + label, + connection_id, + } => { + if document.disabled_environment_ids.contains(environment_id) { + continue; + } + let Some((http_base, authorization)) = profiles.get(connection_id.as_str()) else { + catalog.skipped.push((label.clone(), "no-profile")); + continue; + }; + let bearer = tokens.get(connection_id.as_str()); + // Electron signs in with the DPoP token for T3 Connect profiles, and for any + // profile that has no bearer of its own. + let dpop = dpop_tokens + .get(environment_id.as_str()) + .filter(|_| *authorization == Some("t3-connect") || bearer.is_none()); + if let Some(saved) = dpop { + match dpop_environment( + saved, + environment_id, + label, + http_base, + now_ms, + &mut key, + load_key, + ) { + Ok(environment) => catalog.environments.push(environment), + Err(DpopSkip::Notice(notice)) => catalog.notices.push(notice), + Err(DpopSkip::Kind(kind)) => catalog.skipped.push((label.clone(), kind)), + } + continue; + } + if let Some(kind) = bearer_kind(http_base, *authorization) { + catalog.skipped.push((label.clone(), kind)); + continue; + } + let Some(token) = bearer else { + catalog.skipped.push((label.clone(), "no-credential")); + continue; + }; + catalog.environments.push(SavedEnvironment { + environment_id: environment_id.clone(), + label: label.clone(), + http_base: http_base.trim_end_matches('/').to_owned(), + credential: Credential::Bearer((*token).to_owned()), + }); + } + } + } + // The same environment can be saved more than once (one target per pairing). Keep the first + // connectable entry, and do not report siblings of a connectable environment as skipped. + let mut seen = std::collections::HashSet::new(); + catalog + .environments + .retain(|saved| seen.insert(saved.environment_id.clone())); + catalog.skipped.retain(|(skipped, _)| { + !catalog + .environments + .iter() + .any(|saved| &saved.label == skipped) + }); + Ok(catalog) +} + +enum DpopSkip { + Notice(String), + Kind(&'static str), +} + +/// Maps a saved DPoP token to a connectable environment, or says why it cannot be used. The +/// direct profile address wins over the token's own endpoint; both must be plain `http`. +fn dpop_environment( + saved: &DpopToken, + environment_id: &str, + label: &str, + profile_base: &str, + now_ms: u64, + key: &mut Option, String>>, + load_key: KeyLoader, +) -> Result { + // The token is minted through the T3 Connect relay, which this client cannot reach, so an + // expired one cannot be renewed here. + if saved.expires_at_epoch_ms <= now_ms as f64 { + return Err(DpopSkip::Notice(format!( + "{label}: saved token expired, open the Electron app to refresh" + ))); + } + let http_base = [profile_base, saved.endpoint.http_base_url.as_str()] + .into_iter() + .find(|base| base.starts_with("http://")) + .ok_or(DpopSkip::Kind("https"))?; + let key = key.get_or_insert_with(load_key); + let key = key + .as_ref() + .map_err(|error| DpopSkip::Notice(format!("{label}: no Electron DPoP key ({error})")))?; + if electron_dpop::thumbprint(key) != saved.dpop_thumbprint { + return Err(DpopSkip::Notice(format!( + "{label}: Electron's DPoP key does not match the saved token" + ))); + } + Ok(SavedEnvironment { + environment_id: environment_id.to_owned(), + label: label.to_owned(), + http_base: http_base.trim_end_matches('/').to_owned(), + credential: Credential::Dpop { + token: saved.access_token.clone(), + key: key.clone(), + }, + }) +} + +/// PBKDF2-HMAC-SHA1 for one 16-byte block (SHA-1 yields 20 bytes, so a single block suffices). +fn pbkdf2_sha1_block(password: &[u8], salt: &[u8], iterations: u32) -> [u8; 16] { + let prf = |data: &[u8]| { + let mut mac = Hmac::::new_from_slice(password).expect("HMAC takes any key length"); + mac.update(data); + mac.finalize().into_bytes() + }; + let mut first = salt.to_vec(); + first.extend_from_slice(&1u32.to_be_bytes()); + let mut u = prf(&first); + let mut t = u; + for _ in 1..iterations { + u = prf(&u); + for (acc, byte) in t.iter_mut().zip(u.iter()) { + *acc ^= byte; + } + } + let mut key = [0u8; 16]; + key.copy_from_slice(&t[..16]); + key +} + +/// Decrypts Electron safeStorage ciphertext (`v10` + AES-128-CBC) with the Keychain password. +pub fn decrypt(ciphertext: &[u8], password: &[u8]) -> Result, String> { + let body = ciphertext + .strip_prefix(SAFE_STORAGE_PREFIX) + .ok_or("ciphertext is not v10 safeStorage data")?; + let key = pbkdf2_sha1_block(password, SAFE_STORAGE_SALT, SAFE_STORAGE_ITERATIONS); + Aes128CbcDec::new(&key.into(), &[b' '; 16].into()) + .decrypt_padded_vec_mut::(body) + .map_err(|_| "could not decrypt the catalog (wrong Keychain key?)".to_owned()) +} + +#[cfg(target_os = "macos")] +fn keychain_password() -> Result, String> { + use security_framework::passwords::get_generic_password; + let mut last = String::new(); + for (service, account) in KEYCHAIN_ITEMS { + match get_generic_password(service, account) { + Ok(password) => return Ok(password), + Err(error) => last = format!("{service}: {error}"), + } + } + Err(format!("could not read the safeStorage key ({last})")) +} + +#[cfg(not(target_os = "macos"))] +fn keychain_password() -> Result, String> { + Err("reading the safeStorage key is only supported on macOS".to_owned()) +} + +/// Reads, decrypts and decodes the catalog at `path`. Blocking: the Keychain may prompt. +pub fn load(path: &Path) -> Result { + let raw = std::fs::read_to_string(path).map_err(|error| error.to_string())?; + let envelope: Envelope = + serde_json::from_str(&raw).map_err(|error| format!("unreadable catalog file: {error}"))?; + if envelope.version != 1 { + return Err(format!("unsupported catalog version {}", envelope.version)); + } + let ciphertext = b64::decode(&envelope.encrypted_catalog)?; + let plaintext = decrypt(&ciphertext, &keychain_password()?)?; + let plaintext = String::from_utf8(plaintext).map_err(|_| "catalog is not UTF-8".to_owned())?; + // TODO(gpui-credentials): HACK. Reads Electron's private IndexedDB and non-extractable DPoP key. Replace with GPUI's own pairing + Keychain before shipping. + parse(&plaintext, auth::now_ms(), &mut || { + let dir = electron_dpop::default_idb_dir().ok_or("no IndexedDB location")?; + electron_dpop::load_key(&dir).map(Arc::new) + }) +} + +/// Standard base64 (RFC 4648, padded), enough for the envelope; no extra crate. +pub(crate) mod b64 { + pub fn decode(input: &str) -> Result, String> { + let mut out = Vec::with_capacity(input.len() * 3 / 4); + let (mut buffer, mut bits) = (0u32, 0u32); + for byte in input.trim().bytes() { + let value = match byte { + b'A'..=b'Z' => byte - b'A', + b'a'..=b'z' => byte - b'a' + 26, + b'0'..=b'9' => byte - b'0' + 52, + b'+' => 62, + b'/' => 63, + b'=' => break, + _ => return Err("catalog is not valid base64".to_owned()), + }; + buffer = (buffer << 6) | u32::from(value); + bits += 6; + if bits >= 8 { + bits -= 8; + out.push((buffer >> bits) as u8); + buffer &= (1 << bits) - 1; + } + } + Ok(out) + } + + #[cfg(test)] + pub fn encode(input: &[u8]) -> String { + const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let mut out = String::new(); + for chunk in input.chunks(3) { + let n = chunk + .iter() + .enumerate() + .fold(0u32, |n, (i, b)| n | u32::from(*b) << (16 - 8 * i)); + for i in 0..=chunk.len() { + out.push(ALPHABET[(n >> (18 - 6 * i) & 63) as usize] as char); + } + out.extend(std::iter::repeat_n('=', 3 - chunk.len())); + } + out + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aes::cipher::BlockEncryptMut; + + type Aes128CbcEnc = cbc::Encryptor; + + /// Encrypts like safeStorage, with a synthetic password. + fn seal(plaintext: &[u8], password: &[u8]) -> Vec { + let key = pbkdf2_sha1_block(password, SAFE_STORAGE_SALT, SAFE_STORAGE_ITERATIONS); + let mut sealed = SAFE_STORAGE_PREFIX.to_vec(); + sealed.extend( + Aes128CbcEnc::new(&key.into(), &[b' '; 16].into()) + .encrypt_padded_vec_mut::(plaintext), + ); + sealed + } + + #[test] + fn pbkdf2_matches_the_rfc_6070_vector() { + // RFC 6070, P="password", S="salt", c=2, first 16 bytes of the derived key. + let key = pbkdf2_sha1_block(b"password", b"salt", 2); + assert_eq!(hex::encode(key), "ea6c014dc72d6f8ccd1ed92ace1d41f0"); + } + + #[test] + fn base64_round_trips() { + for sample in [&b""[..], b"a", b"ab", b"abc", b"abcd", &[0, 255, 128, 7, 9]] { + assert_eq!(b64::decode(&b64::encode(sample)).unwrap(), sample); + } + assert!(b64::decode("a*b").is_err()); + } + + #[test] + fn decrypts_what_safe_storage_would_produce() { + let sealed = seal(b"synthetic plaintext", b"synthetic-password"); + assert_eq!( + decrypt(&sealed, b"synthetic-password").unwrap(), + b"synthetic plaintext" + ); + assert!(decrypt(&sealed, b"another-password").is_err()); + assert!(decrypt(b"v11whatever", b"synthetic-password").is_err()); + assert!(decrypt(&sealed[..sealed.len() - 1], b"synthetic-password").is_err()); + } + + fn no_key() -> Result, String> { + Err("no key".to_owned()) + } + + const DOCUMENT: &str = r#"{ + "schemaVersion": 1, + "targets": [ + {"_tag":"BearerConnectionTarget","environmentId":"env-direct","label":"Direct","connectionId":"bearer:env-direct"}, + {"_tag":"BearerConnectionTarget","environmentId":"env-off","label":"Off","connectionId":"bearer:env-off"}, + {"_tag":"BearerConnectionTarget","environmentId":"env-connect","label":"Tunnel","connectionId":"bearer:env-connect"}, + {"_tag":"BearerConnectionTarget","environmentId":"env-tls","label":"Secure","connectionId":"bearer:env-tls"}, + {"_tag":"BearerConnectionTarget","environmentId":"env-bare","label":"Bare","connectionId":"bearer:env-bare"}, + {"_tag":"RelayConnectionTarget","environmentId":"env-relay","label":"Relayed"}, + {"_tag":"SshConnectionTarget","environmentId":"env-ssh","label":"Shell","connectionId":"ssh:env-ssh"}, + {"_tag":"FutureTarget","environmentId":"x","label":"Future"} + ], + "profiles": [ + {"_tag":"BearerConnectionProfile","connectionId":"bearer:env-direct","environmentId":"env-direct","label":"Direct","httpBaseUrl":"http://10.0.0.2:3773/","wsBaseUrl":"ws://10.0.0.2:3773/"}, + {"_tag":"BearerConnectionProfile","connectionId":"bearer:env-off","environmentId":"env-off","label":"Off","httpBaseUrl":"http://10.0.0.3:3773","wsBaseUrl":"ws://10.0.0.3:3773"}, + {"_tag":"BearerConnectionProfile","connectionId":"bearer:env-connect","environmentId":"env-connect","label":"Tunnel","httpBaseUrl":"http://tunnel.invalid","wsBaseUrl":"ws://tunnel.invalid","authorization":"t3-connect"}, + {"_tag":"BearerConnectionProfile","connectionId":"bearer:env-tls","environmentId":"env-tls","label":"Secure","httpBaseUrl":"https://host.invalid","wsBaseUrl":"wss://host.invalid"}, + {"_tag":"BearerConnectionProfile","connectionId":"bearer:env-bare","environmentId":"env-bare","label":"Bare","httpBaseUrl":"http://10.0.0.4:3773","wsBaseUrl":"ws://10.0.0.4:3773"}, + {"_tag":"SshConnectionProfile","connectionId":"ssh:env-ssh","environmentId":"env-ssh","label":"Shell","target":{}} + ], + "credentials": [ + {"connectionId":"bearer:env-direct","credential":{"_tag":"BearerConnectionCredential","token":"synthetic-token"}}, + {"connectionId":"bearer:env-off","credential":{"_tag":"BearerConnectionCredential","token":"synthetic-token-2"}} + ], + "remoteDpopTokens": [], + "disabledEnvironmentIds": ["env-off"] + }"#; + + #[test] + fn parse_keeps_direct_bearer_environments_and_names_the_rest() { + let catalog = parse(DOCUMENT, 0, &mut no_key).unwrap(); + assert_eq!(catalog.environments.len(), 1); + let direct = &catalog.environments[0]; + assert_eq!(direct.environment_id, "env-direct"); + assert_eq!(direct.label, "Direct"); + assert_eq!(direct.http_base, "http://10.0.0.2:3773"); + assert!(matches!(&direct.credential, Credential::Bearer(t) if t == "synthetic-token")); + assert_eq!( + catalog.skipped, + [ + ("Tunnel".to_owned(), "t3-connect"), + ("Secure".to_owned(), "https"), + ("Bare".to_owned(), "no-credential"), + ("Relayed".to_owned(), "relay"), + ("Shell".to_owned(), "ssh"), + ] + ); + assert!(parse("{", 0, &mut no_key).is_err()); + } + + fn dpop_document(thumbprint: &str, expires_at: u64, authorization: &str, base: &str) -> String { + format!( + r#"{{"targets":[{{"_tag":"BearerConnectionTarget","environmentId":"env-a","label":"Devbox","connectionId":"bearer:env-a"}}], + "profiles":[{{"_tag":"BearerConnectionProfile","connectionId":"bearer:env-a","httpBaseUrl":"{base}"{authorization}}}], + "credentials":[], + "remoteDpopTokens":[{{"environmentId":"env-a","label":"Devbox","accessToken":"synthetic-access", + "endpoint":{{"httpBaseUrl":"https://relay.invalid","wsBaseUrl":"wss://relay.invalid","providerKind":"t3_relay"}}, + "expiresAtEpochMs":{expires_at},"dpopThumbprint":"{thumbprint}"}}]}}"# + ) + } + + #[test] + fn dpop_tokens_become_credentials_when_fresh_and_bound_to_the_electron_key() { + let mut seed = [0u8; 32]; + getrandom::fill(&mut seed).unwrap(); + let key = Arc::new(SigningKey::from_slice(&seed).unwrap()); + let bound = electron_dpop::thumbprint(&key); + let mut loads = 0; + let mut load = || { + loads += 1; + Ok(key.clone()) + }; + + let fresh = dpop_document(&bound, 2_000, "", "http://10.0.0.5:3773/"); + let catalog = parse(&fresh, 1_000, &mut load).unwrap(); + assert_eq!(catalog.environments.len(), 1); + let saved = &catalog.environments[0]; + assert_eq!(saved.http_base, "http://10.0.0.5:3773"); + assert!(matches!( + &saved.credential, + Credential::Dpop { token, .. } if token == "synthetic-access" + )); + + // The token's own relay endpoint is https, so a t3-connect profile still dials its direct address. + let connect = dpop_document( + &bound, + 2_000, + r#","authorization":"t3-connect""#, + "http://10.0.0.5:3773", + ); + assert_eq!( + parse(&connect, 1_000, &mut load) + .unwrap() + .environments + .len(), + 1 + ); + + let expired = parse(&fresh, 2_000, &mut load).unwrap(); + assert!(expired.environments.is_empty()); + assert_eq!( + expired.notices, + ["Devbox: saved token expired, open the Electron app to refresh"] + ); + + let other = dpop_document("someone-else", 2_000, "", "http://10.0.0.5:3773"); + let mismatched = parse(&other, 1_000, &mut load).unwrap(); + assert!(mismatched.environments.is_empty()); + assert!(mismatched.notices[0].contains("does not match")); + + let tls = dpop_document(&bound, 2_000, "", "https://host.invalid"); + assert_eq!( + parse(&tls, 1_000, &mut load).unwrap().skipped, + [("Devbox".to_owned(), "https")] + ); + + let keyless = parse(&fresh, 1_000, &mut no_key).unwrap(); + assert!(keyless.environments.is_empty()); + assert!(keyless.notices[0].contains("no Electron DPoP key")); + // Expired tokens never touch the IndexedDB. + assert_eq!(loads, 3); + } + + #[test] + fn load_reads_the_envelope_and_rejects_other_versions() { + let dir = std::env::temp_dir().join(format!("t3-gpui-catalog-{}", std::process::id())); + std::fs::create_dir_all(&dir).unwrap(); + let path = dir.join(CATALOG_FILE); + std::fs::write(&path, r#"{"version":2,"encryptedCatalog":""}"#).unwrap(); + assert!(load(&path).err().unwrap().contains("version")); + std::fs::write(&path, "nope").unwrap(); + assert!(load(&path).is_err()); + std::fs::remove_dir_all(&dir).unwrap(); + assert!(load(&path).is_err()); + } + + #[test] + fn duplicate_saves_of_one_environment_collapse_to_the_connectable_one() { + let document = r#"{"targets":[ + {"_tag":"BearerConnectionTarget","environmentId":"e","label":"Box","connectionId":"c1"}, + {"_tag":"BearerConnectionTarget","environmentId":"e","label":"Box","connectionId":"c2"}], + "profiles":[ + {"_tag":"BearerConnectionProfile","connectionId":"c1","httpBaseUrl":"http://10.0.0.9:1"}, + {"_tag":"BearerConnectionProfile","connectionId":"c2","httpBaseUrl":"http://10.0.0.9:1"}], + "credentials":[{"connectionId":"c2","credential":{"_tag":"BearerConnectionCredential","token":"t"}}]}"#; + let catalog = parse(document, 0, &mut no_key).unwrap(); + assert_eq!(catalog.environments.len(), 1); + assert!(catalog.skipped.is_empty()); + } +} diff --git a/apps/desktop-gpui/src/config.rs b/apps/desktop-gpui/src/config.rs index d9bf28e9233b9..6aa1cfaf930ed 100644 --- a/apps/desktop-gpui/src/config.rs +++ b/apps/desktop-gpui/src/config.rs @@ -6,6 +6,22 @@ pub const ENV_COMMAND: &str = "T3CODE_GPUI_SERVER_COMMAND"; pub const ENV_ENTRY: &str = "T3CODE_GPUI_SERVER_ENTRY"; pub const ENV_ATTACH_URL: &str = "T3CODE_GPUI_SERVER_URL"; pub const ENV_PAIRING_TOKEN: &str = "T3CODE_GPUI_PAIRING_TOKEN"; +pub const ENV_CONNECTION_CATALOG: &str = "T3CODE_GPUI_CONNECTION_CATALOG"; + +/// The Electron app's saved connections: the override, else the catalog inside the T3 home. +pub fn connection_catalog_path() -> Option { + catalog_path_from(|name| std::env::var(name).ok()) +} + +fn catalog_path_from(get: impl Fn(&str) -> Option) -> Option { + let non_empty = |name| get(name).filter(|value| !value.trim().is_empty()); + non_empty(ENV_CONNECTION_CATALOG) + .map(std::path::PathBuf::from) + .or_else(|| { + non_empty(ENV_HOME) + .map(|home| std::path::Path::new(&home).join(crate::catalog::CATALOG_FILE)) + }) +} /// A server someone else runs (a dev server, `npx t3`, a remote host), reached /// with a pairing token instead of being spawned. @@ -125,4 +141,21 @@ mod tests { None ); } + + #[test] + fn catalog_path_prefers_the_override_then_the_t3_home() { + let path = |pairs| catalog_path_from(lookup(pairs)); + assert_eq!( + path(&[ + (ENV_CONNECTION_CATALOG, "/c/catalog.json"), + (ENV_HOME, "/h") + ]), + Some("/c/catalog.json".into()) + ); + assert_eq!( + path(&[(ENV_HOME, "/h")]), + Some("/h/connection-catalog.json".into()) + ); + assert_eq!(path(&[]), None); + } } diff --git a/apps/desktop-gpui/src/connection.rs b/apps/desktop-gpui/src/connection.rs index e51b83dfa4b81..39ca9e93cb2dd 100644 --- a/apps/desktop-gpui/src/connection.rs +++ b/apps/desktop-gpui/src/connection.rs @@ -1,6 +1,8 @@ //! The authenticated RPC WebSocket: connect, keep alive, reconnect. use crate::auth::{self, AuthError, Credential}; +use crate::electron_dpop; +use crate::projects::MachineKind; use crate::rpc::{Event, PING_FRAME, ProjectsMutate, Session, SubscribeShell}; use crate::shell::ShellState; use crate::status::{Status, StatusSender, publish}; @@ -19,7 +21,10 @@ const ORCHESTRATION_PROTOCOL_VERSION: &str = "2"; /// What the connection needs to know about the server it dialed. pub struct Environment { + pub id: String, pub label: String, + /// The machine icon the server's descriptor reports (`platform.machine`). + pub machine: MachineKind, /// The server keeps `pinned` from `project.update` (`capabilities.projectOrganization`). pub project_organization: bool, } @@ -112,6 +117,24 @@ fn socket_url(http_base: &str, ticket: &str) -> Result { .map_err(|error| error.to_string()) } +/// The HTTP request a WebSocket upgrade is, for the proof's `htu`: scheme swapped, no query. +fn upgrade_htu(url: &reqwest::Url) -> String { + let scheme = if url.scheme() == "wss" { + "https" + } else { + "http" + }; + let port = url + .port() + .map(|port| format!(":{port}")) + .unwrap_or_default(); + format!( + "{scheme}://{}{port}{}", + url.host_str().unwrap_or_default(), + url.path() + ) +} + /// Connects and stays connected until cancelled, reporting through `status`. pub async fn run( http_base: &str, @@ -164,7 +187,7 @@ async fn connect( token } }; - let ticket = match auth::issue_ws_ticket(client, http_base, &token).await { + let ticket = match auth::issue_ws_ticket(client, http_base, credential, &token).await { Ok(ticket) => ticket, Err(error) => { if matches!(error, AuthError::Rejected(_)) { @@ -173,7 +196,20 @@ async fn connect( return Err(error.to_string()); } }; - let url = socket_url(http_base, &ticket)?; + let mut url = socket_url(http_base, &ticket)?; + // TODO(gpui-credentials): HACK. Reads Electron's private IndexedDB and non-extractable DPoP key. Replace with GPUI's own pairing + Keychain before shipping. + if let Credential::Dpop { key, .. } = credential { + // A DPoP ticket is key-bound: the upgrade carries a proof for the socket URL in `dpop`, + // with the ticket as the token it hashes (remote.ts resolveRemoteDpopWebSocketConnectionUrl). + let proof = electron_dpop::create_proof( + key, + "GET", + &upgrade_htu(&url), + Some(&ticket), + auth::now_ms(), + )?; + url.query_pairs_mut().append_pair("dpop", &proof); + } let (socket, _) = tokio_tungstenite::connect_async(url.as_str()) .await .map_err(|error| error.to_string())?; diff --git a/apps/desktop-gpui/src/electron_dpop.rs b/apps/desktop-gpui/src/electron_dpop.rs new file mode 100644 index 0000000000000..b83c293a3b95d --- /dev/null +++ b/apps/desktop-gpui/src/electron_dpop.rs @@ -0,0 +1,720 @@ +// TODO(gpui-credentials): HACK. Reads Electron's private IndexedDB and non-extractable DPoP key. Replace with GPUI's own pairing + Keychain before shipping. +// +//! The Electron app's DPoP proof key, and RFC 9449 proofs signed with it. +//! +//! apps/web/src/cloud/dpop.ts keeps the key as `{privateKey: CryptoKey, publicJwk, thumbprint}` in +//! IndexedDB (`t3code:cloud-auth` / `keys` / `relay-dpop-proof-key`). Chromium stores that in a +//! LevelDB directory as a Blink structured clone, where an EC CryptoKey is the `K` tag followed by +//! its algorithm, curve, usages and PKCS8 bytes. This module copies the directory to a temp dir, +//! scans the table and log files for the record (no LevelDB crate: the `idb_cmp1` comparator is +//! irrelevant to a scan), and decodes the key. Claims mirror +//! packages/client-runtime/src/relay/dpopProof.ts and packages/shared/src/dpopCommon.ts. +//! Nothing here logs or returns key material beyond the `SigningKey` itself. + +use p256::ecdsa::{Signature, SigningKey, signature::Signer}; +use p256::pkcs8::DecodePrivateKey; +use serde_json::json; +use sha2::{Digest, Sha256}; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +/// Where Electron's dev app keeps IndexedDB for the `t3code-dev` app. +pub const ENV_IDB_DIR: &str = "T3CODE_GPUI_ELECTRON_IDB"; +const KEY_ID: &str = "relay-dpop-proof-key"; + +pub fn default_idb_dir() -> Option { + if let Some(dir) = std::env::var_os(ENV_IDB_DIR).filter(|dir| !dir.is_empty()) { + return Some(dir.into()); + } + let home = std::env::var_os("HOME")?; + Some( + Path::new(&home) + .join("Library/Application Support/t3code-dev/IndexedDB") + .join("t3code-dev_app_0.indexeddb.leveldb"), + ) +} + +// --------------------------------------------------------------------------------------------- +// base64url (RFC 4648 section 5, no padding) + +pub fn b64url_encode(input: &[u8]) -> String { + const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + let mut out = String::with_capacity(input.len().div_ceil(3) * 4); + for chunk in input.chunks(3) { + let n = chunk + .iter() + .enumerate() + .fold(0u32, |n, (i, b)| n | u32::from(*b) << (16 - 8 * i)); + for i in 0..=chunk.len() { + out.push(ALPHABET[(n >> (18 - 6 * i) & 63) as usize] as char); + } + } + out +} + +// --------------------------------------------------------------------------------------------- +// Proof signing + +/// The key's public JWK members, in RFC 7638 order. +fn public_jwk(key: &SigningKey) -> serde_json::Value { + let point = key.verifying_key().to_sec1_point(false); + let (x, y) = ( + point.x().expect("uncompressed point has x"), + point.y().expect("uncompressed point has y"), + ); + json!({"crv": "P-256", "kty": "EC", "x": b64url_encode(x), "y": b64url_encode(y)}) +} + +/// RFC 7638 thumbprint, as stored in `RemoteDpopAccessToken.dpopThumbprint`. +pub fn thumbprint(key: &SigningKey) -> String { + // serde_json's map is sorted, which is the lexicographic order the RFC asks for. + b64url_encode(&Sha256::digest(public_jwk(key).to_string().as_bytes())) +} + +/// `normalizeDpopHtu`: the URL without query or fragment. +pub fn normalize_htu(url: &str) -> Result { + let mut parsed = reqwest::Url::parse(url).map_err(|_| "DPoP URL is invalid.".to_owned())?; + parsed.set_fragment(None); + parsed.set_query(None); + Ok(parsed.to_string()) +} + +fn random_uuid_v4() -> Result { + let mut bytes = [0u8; 16]; + getrandom::fill(&mut bytes).map_err(|error| format!("could not read random bytes: {error}"))?; + bytes[6] = bytes[6] & 0x0f | 0x40; + bytes[8] = bytes[8] & 0x3f | 0x80; + let hex = hex::encode(bytes); + Ok(format!( + "{}-{}-{}-{}-{}", + &hex[..8], + &hex[8..12], + &hex[12..16], + &hex[16..20], + &hex[20..] + )) +} + +/// `createProof`: an ES256 JWS with `typ: dpop+jwt` and the public JWK, bound to the request +/// method and URL, and to `access_token` through `ath` when there is one. +pub fn create_proof( + key: &SigningKey, + method: &str, + url: &str, + access_token: Option<&str>, + now_ms: u64, +) -> Result { + let header = json!({"typ": "dpop+jwt", "alg": "ES256", "jwk": public_jwk(key)}); + let mut claims = json!({ + "htm": method.to_uppercase(), + "htu": normalize_htu(url)?, + "jti": random_uuid_v4()?, + "iat": now_ms / 1_000, + }); + if let Some(token) = access_token.filter(|token| !token.is_empty()) { + claims["ath"] = json!(b64url_encode(&Sha256::digest(token.as_bytes()))); + } + let signing_input = format!( + "{}.{}", + b64url_encode(header.to_string().as_bytes()), + b64url_encode(claims.to_string().as_bytes()) + ); + let signature: Signature = key.sign(signing_input.as_bytes()); + Ok(format!( + "{signing_input}.{}", + b64url_encode(&signature.to_bytes()) + )) +} + +// --------------------------------------------------------------------------------------------- +// Blink CryptoKey + +/// V8 `ValueSerializer` varint (base-128, little endian). +fn read_varint(bytes: &[u8], at: &mut usize) -> Option { + let (mut value, mut shift) = (0u64, 0u32); + loop { + let byte = *bytes.get(*at)?; + *at += 1; + if shift >= 64 { + return None; + } + value |= u64::from(byte & 0x7f) << shift; + if byte & 0x80 == 0 { + return Some(value); + } + shift += 7; + } +} + +const CRYPTO_KEY_TAG: u8 = b'K'; +const EC_KEY_TAG: u8 = 5; +const PRIVATE_KEY_TYPE: u64 = 2; +const P256_CURVE_TAG: u64 = 1; + +/// Decodes the EC private `CryptoKey` that starts at `bytes[0]` (`V8ScriptValueSerializerForModules` +/// layout: `K`, EC sub-tag, algorithm, curve, key type, usages, length, PKCS8 bytes). +fn decode_crypto_key_at(bytes: &[u8]) -> Option { + let mut at = 0; + if bytes.get(at)? != &CRYPTO_KEY_TAG || bytes.get(at + 1)? != &EC_KEY_TAG { + return None; + } + at += 2; + let _algorithm = read_varint(bytes, &mut at)?; + if read_varint(bytes, &mut at)? != P256_CURVE_TAG { + return None; + } + if read_varint(bytes, &mut at)? != PRIVATE_KEY_TYPE { + return None; + } + let _usages = read_varint(bytes, &mut at)?; + let length = usize::try_from(read_varint(bytes, &mut at)?).ok()?; + let pkcs8 = bytes.get(at..at.checked_add(length)?)?; + SigningKey::from_pkcs8_der(pkcs8).ok() +} + +/// Finds the stored private CryptoKey anywhere in an IndexedDB record value. The surrounding +/// object (`publicJwk`, `thumbprint`) is skipped rather than parsed: the key's tag bytes are +/// validated by their PKCS8 payload, so a false hit cannot decode. +pub fn decode_crypto_key(value: &[u8]) -> Option { + (0..value.len()) + .filter(|&at| value[at] == CRYPTO_KEY_TAG) + .find_map(|at| decode_crypto_key_at(&value[at..])) +} + +/// A record value may be wrapped in Chromium's snappy envelope (tag 0x11 after the version). +fn key_from_record(value: &[u8]) -> Option { + decode_crypto_key(value).or_else(|| { + (0..value.len().min(16)) + .filter(|&at| value[at] == 0x11) + .find_map(|at| { + let inflated = snap::raw::Decoder::new() + .decompress_vec(&value[at + 1..]) + .ok()?; + decode_crypto_key(&inflated) + }) + }) +} + +// --------------------------------------------------------------------------------------------- +// LevelDB scan + +struct Record { + sequence: u64, + deleted: bool, + value: Vec, +} + +/// IndexedDB's key for `relay-dpop-proof-key` in an object store: a prefix (database, store and +/// index ids; index 1 is the object store data) and the string key (`0x01`, length, UTF-16BE). +fn is_key_record(user_key: &[u8]) -> bool { + let Some(&flags) = user_key.first() else { + return false; + }; + let db_bytes = usize::from(flags >> 5 & 7) + 1; + let store_bytes = usize::from(flags >> 2 & 7) + 1; + let index_bytes = usize::from(flags & 3) + 1; + let prefix = 1 + db_bytes + store_bytes + index_bytes; + let Some(index) = user_key.get(prefix - index_bytes..prefix) else { + return false; + }; + let index_id = index + .iter() + .enumerate() + .fold(0u64, |id, (i, byte)| id | u64::from(*byte) << (8 * i)); + let mut expected = vec![0x01, KEY_ID.len() as u8]; + expected.extend(KEY_ID.encode_utf16().flat_map(u16::to_be_bytes)); + index_id == 1 && user_key.get(prefix..) == Some(expected.as_slice()) +} + +fn read_u32_le(bytes: &[u8], at: usize) -> Option { + Some(u32::from_le_bytes(bytes.get(at..at + 4)?.try_into().ok()?)) +} + +fn read_slice<'a>(bytes: &'a [u8], at: &mut usize) -> Option<&'a [u8]> { + let length = usize::try_from(read_varint(bytes, at)?).ok()?; + let slice = bytes.get(*at..at.checked_add(length)?)?; + *at += length; + Some(slice) +} + +fn keep_newest(found: &mut HashMap, Record>, user_key: &[u8], record: Record) { + if !is_key_record(user_key) { + return; + } + match found.get(user_key) { + Some(existing) if existing.sequence >= record.sequence => {} + _ => { + found.insert(user_key.to_vec(), record); + } + } +} + +/// `.log` files: 32 KiB blocks of `crc(4) length(2) type(1)` fragments that reassemble into +/// WriteBatches (`sequence(8) count(4)` then `type key [value]` entries). +fn scan_log(data: &[u8], found: &mut HashMap, Record>) { + const BLOCK: usize = 32 * 1024; + let mut batch = Vec::new(); + let mut batches = Vec::new(); + for block in data.chunks(BLOCK) { + let mut at = 0; + while at + 7 <= block.len() { + let length = usize::from(u16::from_le_bytes([block[at + 4], block[at + 5]])); + let kind = block[at + 6]; + let Some(payload) = block.get(at + 7..at + 7 + length) else { + break; + }; + at += 7 + length; + match kind { + 1 => batches.push(payload.to_vec()), + 2 => batch = payload.to_vec(), + 3 => batch.extend_from_slice(payload), + 4 => { + batch.extend_from_slice(payload); + batches.push(std::mem::take(&mut batch)); + } + _ => {} + } + } + } + for batch in batches { + let Some(sequence) = batch + .get(..8) + .map(|bytes| u64::from_le_bytes(bytes.try_into().expect("8 bytes"))) + else { + continue; + }; + let Some(count) = read_u32_le(&batch, 8) else { + continue; + }; + let mut at = 12; + for index in 0..u64::from(count) { + let Some(&kind) = batch.get(at) else { break }; + at += 1; + let Some(key) = read_slice(&batch, &mut at) else { + break; + }; + let value = if kind == 1 { + match read_slice(&batch, &mut at) { + Some(value) => value, + None => break, + } + } else { + &[] + }; + keep_newest( + found, + key, + Record { + sequence: sequence + index, + deleted: kind == 0, + value: value.to_vec(), + }, + ); + } + } +} + +const TABLE_MAGIC: [u8; 8] = [0x57, 0xfb, 0x80, 0x8b, 0x24, 0x75, 0x47, 0xdb]; + +fn read_handle(bytes: &[u8], at: &mut usize) -> Option<(usize, usize)> { + let offset = usize::try_from(read_varint(bytes, at)?).ok()?; + let size = usize::try_from(read_varint(bytes, at)?).ok()?; + Some((offset, size)) +} + +/// A table block's contents, snappy-inflated when its trailer says so. +fn read_block(data: &[u8], (offset, size): (usize, usize)) -> Option> { + let contents = data.get(offset..offset.checked_add(size)?)?; + match data.get(offset + size)? { + 0 => Some(contents.to_vec()), + 1 => snap::raw::Decoder::new().decompress_vec(contents).ok(), + _ => None, + } +} + +/// The `(key, value)` entries of a block (prefix-compressed, restart array at the end). +fn block_entries(block: &[u8]) -> Vec<(Vec, &[u8])> { + let mut entries = Vec::new(); + let Some(restarts) = read_u32_le(block, block.len().saturating_sub(4)) else { + return entries; + }; + let Some(end) = block.len().checked_sub(4 + 4 * restarts as usize) else { + return entries; + }; + let (mut at, mut key) = (0, Vec::new()); + while at < end { + let (Some(shared), Some(unshared), Some(value_length)) = ( + read_varint(block, &mut at), + read_varint(block, &mut at), + read_varint(block, &mut at), + ) else { + break; + }; + let (shared, unshared, value_length) = + (shared as usize, unshared as usize, value_length as usize); + let (Some(delta), Some(value)) = ( + block.get(at..at.saturating_add(unshared)), + block.get(at + unshared..(at + unshared).saturating_add(value_length)), + ) else { + break; + }; + if shared > key.len() { + break; + } + key.truncate(shared); + key.extend_from_slice(delta); + at += unshared + value_length; + entries.push((key.clone(), value)); + } + entries +} + +/// `.ldb` files: footer, index block of data block handles, data blocks of internal keys +/// (`user key` + `sequence << 8 | type`). +fn scan_table(data: &[u8], found: &mut HashMap, Record>) { + let Some(footer) = data.len().checked_sub(48).map(|start| &data[start..]) else { + return; + }; + if footer[40..] != TABLE_MAGIC { + return; + } + let mut at = 0; + if read_handle(footer, &mut at).is_none() { + return; + } + let Some(index_handle) = read_handle(footer, &mut at) else { + return; + }; + let Some(index) = read_block(data, index_handle) else { + return; + }; + for (_, handle) in block_entries(&index) { + let mut at = 0; + let Some(block) = read_handle(handle, &mut at).and_then(|h| read_block(data, h)) else { + continue; + }; + for (internal_key, value) in block_entries(&block) { + let Some(split) = internal_key.len().checked_sub(8) else { + continue; + }; + let trailer = u64::from_le_bytes(internal_key[split..].try_into().expect("8 bytes")); + keep_newest( + found, + &internal_key[..split], + Record { + sequence: trailer >> 8, + deleted: trailer & 0xff == 0, + value: value.to_vec(), + }, + ); + } + } +} + +/// Removes the temp copy on every exit path. +struct TempCopy(PathBuf); + +impl Drop for TempCopy { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.0); + } +} + +fn scan_directory(dir: &Path) -> Result, String> { + let mut found = HashMap::new(); + let entries = std::fs::read_dir(dir).map_err(|error| error.to_string())?; + for entry in entries.flatten() { + let path = entry.path(); + let Ok(data) = std::fs::read(&path) else { + continue; + }; + match path.extension().and_then(|extension| extension.to_str()) { + Some("ldb") | Some("sst") => scan_table(&data, &mut found), + Some("log") => scan_log(&data, &mut found), + _ => {} + } + } + Ok(found.into_values().filter(|r| !r.deleted).collect()) +} + +/// Reads the Electron app's DPoP proof key from its IndexedDB directory. Only a copy of the +/// `.ldb` and `.log` files is read, and the copy is deleted before this returns. +pub fn load_key(idb_dir: &Path) -> Result { + let mut suffix = [0u8; 8]; + getrandom::fill(&mut suffix).map_err(|error| error.to_string())?; + let copy = TempCopy(std::env::temp_dir().join(format!("t3-gpui-idb-{}", hex::encode(suffix)))); + std::fs::create_dir(©.0).map_err(|error| error.to_string())?; + let entries = std::fs::read_dir(idb_dir) + .map_err(|error| format!("could not read the Electron IndexedDB: {error}"))?; + for entry in entries.flatten() { + let path = entry.path(); + if matches!( + path.extension().and_then(|extension| extension.to_str()), + Some("ldb") | Some("sst") | Some("log") + ) && let Some(name) = path.file_name() + { + std::fs::copy(&path, copy.0.join(name)).map_err(|error| error.to_string())?; + } + } + let mut records = scan_directory(©.0)?; + records.sort_by_key(|record| std::cmp::Reverse(record.sequence)); + records + .iter() + .find_map(|record| key_from_record(&record.value)) + .ok_or_else(|| "no DPoP proof key in the Electron IndexedDB".to_owned()) +} + +#[cfg(test)] +mod tests { + use super::*; + use p256::pkcs8::EncodePrivateKey; + + fn varint(mut value: u64) -> Vec { + let mut out = Vec::new(); + loop { + let byte = (value & 0x7f) as u8; + value >>= 7; + if value == 0 { + out.push(byte); + return out; + } + out.push(byte | 0x80); + } + } + + fn fresh_key() -> SigningKey { + let mut seed = [0u8; 32]; + getrandom::fill(&mut seed).unwrap(); + SigningKey::from_slice(&seed).unwrap() + } + + /// What Blink writes for an extractable-false ECDSA P-256 private key with the `sign` usage. + fn serialized_crypto_key(key: &SigningKey) -> Vec { + let pkcs8 = key.to_pkcs8_der().unwrap(); + let mut out = vec![CRYPTO_KEY_TAG, EC_KEY_TAG]; + out.extend(varint(13)); // ECDSA + out.extend(varint(P256_CURVE_TAG)); + out.extend(varint(PRIVATE_KEY_TYPE)); + out.extend(varint(1 << 3)); // sign + out.extend(varint(pkcs8.as_bytes().len() as u64)); + out.extend_from_slice(pkcs8.as_bytes()); + out + } + + /// An IndexedDB value: version, wrapper bytes, then an object whose first property is the key. + fn record_value(key: &SigningKey) -> Vec { + let mut value = vec![0x00, 0xff, 0x11, 0xff, 0x0f, b'o', b'"', 10]; + value.extend_from_slice(b"privateKey"); + value.extend(serialized_crypto_key(key)); + value.extend_from_slice(b"\"\x09publicJwk{"); + value + } + + #[test] + fn decodes_a_blink_serialized_key_inside_a_record() { + let key = fresh_key(); + let decoded = decode_crypto_key(&record_value(&key)).expect("key"); + assert_eq!(thumbprint(&decoded), thumbprint(&key)); + assert!(decode_crypto_key(b"K\x05\x0d\x01\x02").is_none()); + assert!(decode_crypto_key(&[]).is_none()); + } + + #[test] + fn rejects_public_and_other_curve_keys() { + let mut other_curve = serialized_crypto_key(&fresh_key()); + other_curve[3] = 2; // P-384 + assert!(decode_crypto_key(&other_curve).is_none()); + let mut public = serialized_crypto_key(&fresh_key()); + public[4] = 1; + assert!(decode_crypto_key(&public).is_none()); + } + + #[test] + fn decodes_a_snappy_wrapped_record() { + let key = fresh_key(); + let mut value = vec![0x00, 0x11]; + value.extend( + snap::raw::Encoder::new() + .compress_vec(&record_value(&key)) + .unwrap(), + ); + // Without the envelope the compressed bytes hold no key, so the wrapper path is the one used. + let decoded = key_from_record(&value).expect("key"); + assert_eq!(thumbprint(&decoded), thumbprint(&key)); + } + + #[test] + fn recognises_the_idb_object_store_key() { + let mut key = vec![0x00, 0x01, 0x02, 0x01]; // 1-byte ids, db 1, store 2, index 1 + key.extend([0x01, KEY_ID.len() as u8]); + key.extend(KEY_ID.encode_utf16().flat_map(u16::to_be_bytes)); + assert!(is_key_record(&key)); + let mut other_index = key.clone(); + other_index[3] = 2; + assert!(!is_key_record(&other_index)); + assert!(!is_key_record(&key[..key.len() - 2])); + assert!(!is_key_record(&[])); + } + + #[test] + fn scans_a_leveldb_log_and_keeps_the_newest_value() { + let key = fresh_key(); + let mut user_key = vec![0x00, 0x01, 0x02, 0x01, 0x01, KEY_ID.len() as u8]; + user_key.extend(KEY_ID.encode_utf16().flat_map(u16::to_be_bytes)); + let batch = |sequence: u64, value: &[u8]| { + let mut batch = sequence.to_le_bytes().to_vec(); + batch.extend(1u32.to_le_bytes()); + batch.push(1); + batch.extend(varint(user_key.len() as u64)); + batch.extend(&user_key); + batch.extend(varint(value.len() as u64)); + batch.extend(value); + batch + }; + let mut log = Vec::new(); + for (sequence, value) in [(5, b"stale".to_vec()), (9, record_value(&key))] { + let payload = batch(sequence, &value); + log.extend([0u8; 4]); + log.extend((payload.len() as u16).to_le_bytes()); + log.push(1); + log.extend(payload); + } + let mut found = HashMap::new(); + scan_log(&log, &mut found); + let record = found.get(&user_key).expect("record"); + assert_eq!(record.sequence, 9); + let decoded = key_from_record(&record.value).expect("key"); + assert_eq!(thumbprint(&decoded), thumbprint(&key)); + } + + #[test] + fn scans_a_table_block_with_prefix_compression() { + let mut user_key = vec![0x00, 0x01, 0x02, 0x01, 0x01, KEY_ID.len() as u8]; + user_key.extend(KEY_ID.encode_utf16().flat_map(u16::to_be_bytes)); + let mut internal = user_key.clone(); + internal.extend(((7u64 << 8) | 1).to_le_bytes()); + let key = fresh_key(); + let value = record_value(&key); + // Two entries: a full key, then the same key sharing all but its trailer. + let mut block = Vec::new(); + block.extend(varint(0)); + block.extend(varint(internal.len() as u64)); + block.extend(varint(value.len() as u64)); + block.extend(&internal); + block.extend(&value); + let mut second = user_key.clone(); + second.extend(((8u64 << 8) | 1).to_le_bytes()); + block.extend(varint(user_key.len() as u64)); + block.extend(varint(8)); + block.extend(varint(1)); + block.extend(&second[user_key.len()..]); + block.push(0); + block.extend(0u32.to_le_bytes()); + block.extend(1u32.to_le_bytes()); + let entries = block_entries(&block); + assert_eq!(entries.len(), 2); + assert_eq!(entries[1].0, second); + + let mut table = Vec::new(); + let data_handle = (table.len(), block.len()); + table.extend(&block); + table.extend([0, 0, 0, 0, 0]); + let mut index = Vec::new(); + let mut handle = varint(data_handle.0 as u64); + handle.extend(varint(data_handle.1 as u64)); + index.extend(varint(0)); + index.extend(varint(second.len() as u64)); + index.extend(varint(handle.len() as u64)); + index.extend(&second); + index.extend(&handle); + index.extend(0u32.to_le_bytes()); + index.extend(1u32.to_le_bytes()); + let index_handle = (table.len(), index.len()); + table.extend(&index); + table.extend([0, 0, 0, 0, 0]); + let mut footer = varint(0); + footer.extend(varint(0)); + footer.extend(varint(index_handle.0 as u64)); + footer.extend(varint(index_handle.1 as u64)); + footer.resize(40, 0); + footer.extend(TABLE_MAGIC); + table.extend(footer); + + let mut found = HashMap::new(); + scan_table(&table, &mut found); + let record = found.get(&user_key).expect("record"); + assert_eq!(record.sequence, 8); + assert!(!record.deleted); + } + + #[test] + fn proof_has_the_client_runtime_shape_and_verifies() { + use p256::ecdsa::{VerifyingKey, signature::Verifier}; + let key = fresh_key(); + let proof = create_proof( + &key, + "post", + "https://Host.example:443/api/auth/websocket-ticket/rpc?x=1#frag", + Some("access"), + 1_700_000_123_456, + ) + .unwrap(); + let parts: Vec<&str> = proof.split('.').collect(); + assert_eq!(parts.len(), 3); + let decode = |part: &str| { + let mut padded = part.replace('-', "+").replace('_', "/"); + while !padded.len().is_multiple_of(4) { + padded.push('='); + } + crate::catalog::b64::decode(&padded).unwrap() + }; + let header: serde_json::Value = serde_json::from_slice(&decode(parts[0])).unwrap(); + let claims: serde_json::Value = serde_json::from_slice(&decode(parts[1])).unwrap(); + assert_eq!(header["typ"], "dpop+jwt"); + assert_eq!(header["alg"], "ES256"); + assert_eq!(header["jwk"], public_jwk(&key)); + assert!(header["jwk"].get("d").is_none()); + assert_eq!(claims["htm"], "POST"); + assert_eq!( + claims["htu"], + "https://host.example/api/auth/websocket-ticket/rpc" + ); + assert_eq!(claims["iat"], 1_700_000_123); + assert_eq!(claims["jti"].as_str().unwrap().len(), 36); + // SHA-256("access"), base64url. + assert_eq!( + claims["ath"], + "oFYf1knNtrqnhAVfBRuteW6gr-8X_KOCGVSd7rpOjBo".to_owned() + ); + let signature = Signature::from_slice(&decode(parts[2])).unwrap(); + VerifyingKey::from(&key) + .verify(format!("{}.{}", parts[0], parts[1]).as_bytes(), &signature) + .unwrap(); + let no_ath = create_proof(&key, "GET", "http://h/ws", None, 0).unwrap(); + assert!(!no_ath.contains("ath")); + assert!(create_proof(&key, "GET", "not a url", None, 0).is_err()); + } + + #[test] + fn thumbprint_matches_rfc_7638_over_the_public_jwk() { + let key = fresh_key(); + let jwk = public_jwk(&key); + let canonical = format!( + r#"{{"crv":"P-256","kty":"EC","x":"{}","y":"{}"}}"#, + jwk["x"].as_str().unwrap(), + jwk["y"].as_str().unwrap() + ); + assert_eq!( + thumbprint(&key), + b64url_encode(&Sha256::digest(canonical.as_bytes())) + ); + } + + #[test] + fn base64url_matches_known_vectors() { + assert_eq!(b64url_encode(b""), ""); + assert_eq!(b64url_encode(b"f"), "Zg"); + assert_eq!(b64url_encode(b"fo"), "Zm8"); + assert_eq!(b64url_encode(&[0xfb, 0xff]), "-_8"); + } +} diff --git a/apps/desktop-gpui/src/environments.rs b/apps/desktop-gpui/src/environments.rs new file mode 100644 index 0000000000000..fe55b506010ce --- /dev/null +++ b/apps/desktop-gpui/src/environments.rs @@ -0,0 +1,92 @@ +//! The user's other saved environments (the Electron app's catalog): one connection each, whose +//! projects the picker uses to show where else a project lives. + +use crate::auth; +use crate::catalog::{self, SavedEnvironment}; +use crate::connection; +use crate::projects::EnvironmentProjects; +use crate::server; +use crate::status::{Status, StatusSender, publish}; +use std::path::PathBuf; +use tokio::time::sleep; + +/// Loads the catalog and keeps a connection to each reachable environment other than `primary_id`. +/// Any failure costs only the badges, and is logged once. +pub async fn run(primary_id: String, path: PathBuf, status: StatusSender) { + let loaded = tokio::task::spawn_blocking(move || catalog::load(&path)).await; + let catalog = match loaded { + Ok(Ok(catalog)) => catalog, + Ok(Err(message)) => return eprintln!("[environments] no environment badges: {message}"), + Err(error) => return eprintln!("[environments] no environment badges: {error}"), + }; + for (label, kind) in &catalog.skipped { + eprintln!("[environments] skipping {label}: {kind} connections are not supported"); + } + for notice in &catalog.notices { + eprintln!("[environments] {notice}"); + } + for saved in catalog + .environments + .into_iter() + .filter(|saved| saved.environment_id != primary_id) + { + tokio::spawn(follow(saved, status.clone())); + } +} + +/// Mirrors one environment's projects into `status` for as long as the app runs. +async fn follow(saved: SavedEnvironment, status: StatusSender) { + let client = auth::http_client(); + let (own_tx, own_rx) = async_channel::unbounded(); + // The connection ends its session when the action channel closes, so keep the sender here. + let (_actions_tx, actions_rx) = async_channel::unbounded(); + let mut backoff = connection::Backoff::new(); + let environment = loop { + match server::fetch_environment(&client, &saved.http_base).await { + Ok(environment) if environment.id == saved.environment_id => break environment, + Ok(_) => { + return eprintln!( + "[environments] {} answers as a different environment; not connecting", + saved.label + ); + } + Err(_) => sleep(backoff.next_delay()).await, + } + }; + let machine = environment.machine; + let connection = connection::Environment { + label: saved.label.clone(), + ..environment + }; + let forward = async { + let mut last: Option = None; + while let Ok(update) = own_rx.recv().await { + let projects = match update { + Status::Connected { + shell: Some(shell), .. + } => shell.project_entries, + Status::Connected { .. } => continue, + // A dropped socket must not leave a stale "Also on". + _ => Vec::new(), + }; + let next = EnvironmentProjects { + id: saved.environment_id.clone(), + label: saved.label.clone(), + machine, + projects, + }; + if last.as_ref() != Some(&next) { + publish(&status, Status::Environment(next.clone())); + last = Some(next); + } + } + }; + let session = connection::run( + &saved.http_base, + &saved.credential, + &connection, + &actions_rx, + &own_tx, + ); + tokio::join!(forward, session); +} diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs index 3c107ae90fcd1..b2b06324884fe 100644 --- a/apps/desktop-gpui/src/main.rs +++ b/apps/desktop-gpui/src/main.rs @@ -1,7 +1,10 @@ mod assets; mod auth; +mod catalog; mod config; mod connection; +mod electron_dpop; +mod environments; mod projects; mod rpc; mod server; @@ -46,6 +49,14 @@ fn main() { let client = auth::http_client(); match server::wait_until_ready(&client, &attach.http_base).await { Ok(environment) => { + // Only an attached server shares the Electron app's home, hence its catalog. + if let Some(path) = config::connection_catalog_path() { + tokio::spawn(environments::run( + environment.id.clone(), + path, + status_tx.clone(), + )); + } let credential = auth::Credential::Pairing(attach.pairing_token); connection::run( &attach.http_base, diff --git a/apps/desktop-gpui/src/projects.rs b/apps/desktop-gpui/src/projects.rs index b5bcc13f8b87e..9e834311932ab 100644 --- a/apps/desktop-gpui/src/projects.rs +++ b/apps/desktop-gpui/src/projects.rs @@ -1,7 +1,9 @@ -//! The project picker's rows, mirroring the web picker: `sortLogicalProjectsForSidebar` -//! (Sidebar.logic.ts), `pickableProjectGroups` (projectGrouping.ts), `deriveProjectIdentity` -//! (projectIdentity.ts) and `buildSidebarProjectPickerEntries` (sidebarProjectGrouping.ts). +//! The project picker's rows, mirroring the web picker in its default `repository` grouping mode: +//! `buildProjectGroups` and `pickableProjectGroups` (projectGrouping.ts), +//! `sortLogicalProjectsForSidebar` (Sidebar.logic.ts) and `buildSidebarProjectPickerEntries` +//! (sidebarProjectGrouping.ts). Each row is a group of projects that share a repository. +use crate::wire::generated::RepositoryIdentity; use crate::wire::{Project, Thread}; use std::collections::HashMap; @@ -31,18 +33,83 @@ const COLORS: [(&str, u32, u32); 18] = [ const SCRATCH_TITLE: &str = "No project"; const SCRATCH_FOLDER: &str = "scratch"; +/// `EnvironmentMachineKind` (packages/contracts/src/environment.ts); unknown values read as a server. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MachineKind { + Server, + Cloud, + Linux, + Desktop, + Laptop, + MacMini, + MacStudio, +} + +impl MachineKind { + pub fn parse(kind: &str) -> Self { + match kind { + "cloud" => Self::Cloud, + "linux" => Self::Linux, + "desktop" => Self::Desktop, + "laptop" => Self::Laptop, + "mac-mini" => Self::MacMini, + "mac-studio" => Self::MacStudio, + _ => Self::Server, + } + } +} + +/// Another environment that also has this project. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RemoteMember { + pub label: String, + pub machine: MachineKind, +} + +/// A saved environment other than the one this window attaches to, with its projects. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EnvironmentProjects { + pub id: String, + pub label: String, + pub machine: MachineKind, + pub projects: Vec, +} + +/// One picker row: a `ProjectGroup` of the web, seen from the attached environment. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ProjectEntry { + /// The group key: the repository's canonical key, else the project's physical path. + pub key: String, + /// The target project (the representative): what picking the row selects. pub id: String, + /// Every project of the group on the attached environment, the pin writes all of them. + pub member_ids: Vec, + /// The group label (`deriveProjectGroupLabel`). pub title: String, + /// From the representative: its monogram override, else its title. pub initials: String, /// Index into the shared palette. pub color: usize, + /// Any member is pinned. pub pinned: bool, pub scratch: bool, + /// The repository key shared across environments, when the group has a repository. + pub logical_key: Option, + /// Other environments that have the same logical project. + pub remote: Vec, } impl ProjectEntry { + pub fn contains(&self, project_id: &str) -> bool { + self.member_ids.iter().any(|id| id == project_id) + } + + /// The project picking the row selects: the active one when it belongs to the group, as the + /// web's `targetProject`, else the representative. + pub fn target<'a>(&'a self, active_id: Option<&'a str>) -> &'a str { + active_id.filter(|id| self.contains(id)).unwrap_or(&self.id) + } + /// The badge text color for the theme; the badge background is this at 14% alpha. pub fn color_hex(&self, dark: bool) -> u32 { let (_, light, night) = COLORS[self.color]; @@ -100,6 +167,18 @@ fn color_index(name: &str) -> usize { }) } +/// The cross-environment identity of `projectGrouping.ts` in the default `repository` mode: the +/// repository's canonical key. Without one the web falls back to a per-environment path key, which +/// never matches another environment, so there is nothing to join on. +fn logical_key(project: &Project) -> Option { + let identity = project.repository_identity.as_ref()?; + let key = identity + .origin + .as_ref() + .map_or(&identity.canonical_key, |origin| &origin.canonical_key); + (!key.is_empty() && !key.contains('\0')).then(|| key.clone()) +} + /// A monogram icon override wins, as in `ProjectFavicon`; other icon kinds fall back to the derived one. fn monogram_override(project: &Project) -> Option<(String, usize)> { let icon = project.project_icon.as_ref()?; @@ -131,8 +210,159 @@ fn thread_activity(thread: &Thread) -> &str { .unwrap_or(&thread.updated_at) } -/// Unarchived projects, pinned first, then most recent thread activity (the project's own -/// `updatedAt` when it has no threads), then title. +/// `normalizeProjectPathForComparison`: trimmed, no trailing separators, Windows paths folded. +fn normalize_path(path: &str) -> String { + let trimmed = path.trim(); + let stripped = trimmed.trim_end_matches(['/', '\\']); + let normalized = if stripped.is_empty() && !trimmed.is_empty() { + &trimmed[..1] + } else { + stripped + }; + let bytes = normalized.as_bytes(); + let drive = bytes.len() >= 2 && bytes[0].is_ascii_alphabetic() && bytes[1] == b':'; + if drive || normalized.starts_with("\\\\") { + normalized.replace('/', "\\").to_lowercase() + } else { + normalized.to_owned() + } +} + +/// `shouldReplacePhysicalProjectWinner`: the fresher project wins, then the greater id. Server +/// timestamps are fixed-width UTC, so text order is time order. +fn fresher(candidate: &Project, existing: &Project) -> bool { + let key = |project: &Project| (project.updated_at.clone(), project.id.clone()); + key(candidate) > key(existing) +} + +fn unique_non_empty<'a>(values: impl IntoIterator>) -> Vec<&'a str> { + let mut unique: Vec<&str> = Vec::new(); + for value in values.into_iter().flatten().map(str::trim) { + if !value.is_empty() && !unique.contains(&value) { + unique.push(value); + } + } + unique +} + +/// `repositoryGroupingDisplayNameOf`. +fn grouping_display_name(identity: &RepositoryIdentity) -> Option<&str> { + match &identity.origin { + Some(origin) => Some( + origin + .display_name + .as_deref() + .unwrap_or(&origin.canonical_key), + ), + None => identity.display_name.as_deref(), + } +} + +/// `deriveProjectGroupLabel`, for groups of more than one member. +fn group_label(representative: &Project, members: &[&Project]) -> String { + let titles = unique_non_empty(members.iter().map(|m| Some(m.title.as_str()))); + let display_names = unique_non_empty(members.iter().map(|m| { + m.repository_identity + .as_ref() + .and_then(grouping_display_name) + })); + let names = unique_non_empty(members.iter().map(|m| { + m.repository_identity + .as_ref() + .and_then(|i| i.name.as_deref()) + })); + if let [title] = titles[..] + && !display_names.contains(&title) + && !names.contains(&title) + { + return title.to_owned(); + } + if let [name] = display_names[..] { + return name.to_owned(); + } + if let [name] = names[..] { + return name.to_owned(); + } + representative.title.clone() +} + +/// Group keys without a repository; the NUL keeps them out of the repository key space. +const PATH_KEY_PREFIX: &str = "\0path:"; + +/// A group under construction: the physical winners, plus every project they stand for. +struct Group<'a> { + key: String, + winners: Vec<&'a Project>, + all: Vec<&'a Project>, +} + +/// `buildProjectGroups` in `repository` mode: projects with the same normalized workspace root +/// collapse to the freshest, then groups form on the repository key (the project's own path when +/// it has none). The web's `projects` input lists a server's projects in snapshot order, which is +/// creation order, so members are ordered by `createdAt` here (the shell state is a map) and the +/// first one is the representative. Every project here is on the attached environment. +fn build_groups<'a>(projects: impl IntoIterator) -> Vec> { + let mut ordered: Vec<&Project> = projects.into_iter().collect(); + ordered.sort_by(|a, b| (&a.created_at, &a.id).cmp(&(&b.created_at, &b.id))); + + let mut physical: Vec<(String, Vec<&Project>)> = Vec::new(); + for project in ordered { + let key = normalize_path(&project.workspace_root); + match physical.iter_mut().find(|(existing, _)| *existing == key) { + Some((_, members)) => members.push(project), + None => physical.push((key, vec![project])), + } + } + + let mut groups: Vec = Vec::new(); + for (path, projects) in physical { + let winner = projects + .iter() + .copied() + .reduce(|current, candidate| { + if fresher(candidate, current) { + candidate + } else { + current + } + }) + .expect("a physical key has a project"); + // `selectProjectIdentitySource`: a winner without a repository borrows the freshest identity. + let source = if winner.repository_identity.is_some() { + winner + } else { + projects + .iter() + .copied() + .filter(|project| project.repository_identity.is_some()) + .reduce(|current, candidate| { + if fresher(candidate, current) { + candidate + } else { + current + } + }) + .unwrap_or(winner) + }; + let key = logical_key(source).unwrap_or_else(|| format!("{PATH_KEY_PREFIX}{path}")); + match groups.iter_mut().find(|group| group.key == key) { + Some(group) => { + group.winners.push(winner); + group.all.extend(projects); + } + None => groups.push(Group { + key, + winners: vec![winner], + all: projects, + }), + } + } + groups +} + +/// The picker's groups: pinned first, then most recent thread activity across the group (the +/// representative's `updatedAt` when it has no threads), then label. Groups whose members are all +/// archived are left out. pub fn build_entries<'a>( projects: impl IntoIterator, threads: impl IntoIterator, @@ -143,42 +373,111 @@ pub fn build_entries<'a>( let slot = latest.entry(thread.project_id.as_str()).or_insert(activity); *slot = (*slot).max(activity); } - let mut ranked: Vec<(&Project, &str)> = projects + let mut ranked: Vec<(ProjectEntry, String)> = build_groups(projects) .into_iter() - .filter(|project| project.archived_at.is_none()) - .map(|project| { - let activity = latest - .get(project.id.as_str()) - .copied() - .unwrap_or(&project.updated_at); - (project, activity) + .filter(|group| group.winners.iter().any(|p| p.archived_at.is_none())) + .map(|group| { + let representative = group.winners[0]; + let label = if group.winners.len() > 1 { + group_label(representative, &group.winners) + } else { + representative.title.clone() + }; + let (initials, color) = monogram_override(representative).unwrap_or_else(|| { + ( + initials(&representative.title), + color_index(&representative.title), + ) + }); + let activity = group + .all + .iter() + .filter_map(|p| latest.get(p.id.as_str()).copied()) + .max() + .unwrap_or(&representative.updated_at) + .to_owned(); + let entry = ProjectEntry { + logical_key: (!group.key.starts_with(PATH_KEY_PREFIX)).then(|| group.key.clone()), + key: group.key, + id: representative.id.clone(), + member_ids: group.all.iter().map(|p| p.id.clone()).collect(), + title: label, + initials, + color, + pinned: group.winners.iter().any(|p| p.pinned_at.is_some()), + scratch: is_scratch(representative), + remote: Vec::new(), + }; + (entry, activity) }) .collect(); ranked.sort_by(|(a, a_at), (b, b_at)| { - b.pinned_at - .is_some() - .cmp(&a.pinned_at.is_some()) + b.pinned + .cmp(&a.pinned) .then_with(|| b_at.cmp(a_at)) .then_with(|| a.title.to_lowercase().cmp(&b.title.to_lowercase())) - .then_with(|| a.id.cmp(&b.id)) + .then_with(|| a.key.cmp(&b.key)) }); - ranked - .into_iter() - .map(|(project, _)| { - let (initials, color) = monogram_override(project) - .unwrap_or_else(|| (initials(&project.title), color_index(&project.title))); - ProjectEntry { - id: project.id.clone(), - title: project.title.clone(), - initials, - color, - pinned: project.pinned_at.is_some(), - scratch: is_scratch(project), + ranked.into_iter().map(|(entry, _)| entry).collect() +} + +/// Adds to each entry the other environments that have the same logical project. +pub fn with_remote_members( + entries: &[ProjectEntry], + others: &[EnvironmentProjects], +) -> Vec { + entries + .iter() + .map(|entry| { + let mut entry = entry.clone(); + if let Some(key) = &entry.logical_key { + entry.remote = others + .iter() + .filter(|other| { + other + .projects + .iter() + .any(|project| project.logical_key.as_ref() == Some(key)) + }) + .map(|other| RemoteMember { + label: if other.label.trim().is_empty() { + "Remote".to_owned() + } else { + other.label.clone() + }, + machine: other.machine, + }) + .collect(); } + entry }) .collect() } +/// Members ordered by label, as `ProjectEnvironmentBadge` sorts them. +fn sorted_members(members: &[RemoteMember]) -> Vec<&RemoteMember> { + let mut sorted: Vec<&RemoteMember> = members.iter().collect(); + sorted.sort_by_cached_key(|member| (member.label.to_lowercase(), member.label.clone())); + sorted +} + +/// The environment badge's tooltip. Picker rows are always on the attached environment, so this is +/// the web's "Also on" case; "On" only applies to groups the attached environment lacks. +pub fn badge_text(members: &[RemoteMember]) -> Option { + let mut labels: Vec<&str> = Vec::new(); + for member in sorted_members(members) { + if !labels.contains(&member.label.as_str()) { + labels.push(&member.label); + } + } + (!labels.is_empty()).then(|| format!("Also on {}", labels.join(", "))) +} + +/// The badge icon follows the first member by label. +pub fn badge_machine(members: &[RemoteMember]) -> Option { + sorted_members(members).first().map(|member| member.machine) +} + /// What the picker lists: no scratch project, the active project leading the unpinned ones /// (pinned stay first), then the case-insensitive title filter. pub fn picker_entries<'a>( @@ -189,7 +488,7 @@ pub fn picker_entries<'a>( let mut listed: Vec<&ProjectEntry> = entries.iter().filter(|entry| !entry.scratch).collect(); if let Some(at) = listed .iter() - .position(|entry| Some(entry.id.as_str()) == active_id && !entry.pinned) + .position(|entry| active_id.is_some_and(|id| entry.contains(id)) && !entry.pinned) { let active = listed.remove(at); let pinned = listed.iter().filter(|entry| entry.pinned).count(); @@ -200,8 +499,8 @@ pub fn picker_entries<'a>( listed } -/// Rows in the order they had when the picker opened (`useOrderWhileOpen`): ids that left drop -/// out, ids that arrived follow in their current order. +/// Rows in the order they had when the picker opened (`useOrderWhileOpen`): keys that left drop +/// out, keys that arrived follow in their current order. pub fn order_while_open<'a>( listed: Vec<&'a ProjectEntry>, opened_with: &[String], @@ -209,7 +508,7 @@ pub fn order_while_open<'a>( let position = |entry: &ProjectEntry| { opened_with .iter() - .position(|id| *id == entry.id) + .position(|key| *key == entry.key) .unwrap_or(opened_with.len()) }; let mut ordered = listed; @@ -227,12 +526,16 @@ mod tests { #[test] fn order_while_open_keeps_the_opening_order_and_appends_new_rows() { let entry = |id: &str, pinned: bool| ProjectEntry { + key: id.to_owned(), id: id.to_owned(), + member_ids: vec![id.to_owned()], title: id.to_owned(), initials: String::new(), color: 0, pinned, scratch: false, + logical_key: None, + remote: Vec::new(), }; let opened = ["a", "b", "c"].map(str::to_owned); let now = [entry("d", false), entry("c", true), entry("a", false)]; @@ -269,6 +572,9 @@ mod tests { project["archivedAt"] = Value::Null; project["pinnedAt"] = Value::Null; project["updatedAt"] = "2026-01-01T00:00:00.000Z".into(); + project["createdAt"] = "2026-01-01T00:00:00.000Z".into(); + project["workspaceRoot"] = format!("/work/{id}").into(); + project["repositoryIdentity"] = Value::Null; edit(&mut project); serde_json::from_value(project).expect("project decodes") } @@ -416,4 +722,230 @@ mod tests { ); assert!(picker_entries(&entries, None, "zzz").is_empty()); } + + fn repo(key: &str) -> serde_json::Value { + json!({ + "canonicalKey": key, + "locator": {"remoteName": "origin", "remoteUrl": "git@example.invalid:o/r.git", "source": "git-remote"}, + }) + } + + #[test] + fn logical_key_prefers_the_origin_key_and_needs_a_repository() { + let plain = project("a", "a", |p| p["repositoryIdentity"] = repo("host/o/r")); + let forked = project("b", "b", |p| { + let mut identity = repo("host/fork/r"); + identity["origin"] = json!({"canonicalKey": "host/o/r"}); + p["repositoryIdentity"] = identity; + }); + let bare = project("c", "c", |p| p["repositoryIdentity"] = Value::Null); + let keys: Vec<_> = [&plain, &forked, &bare] + .iter() + .map(|p| logical_key(p)) + .collect(); + assert_eq!( + keys, + [ + Some("host/o/r".to_owned()), + Some("host/o/r".to_owned()), + None + ] + ); + } + + #[test] + fn remote_members_join_on_the_logical_key() { + let entries = build_entries( + &[ + project("a", "atlas", |p| { + p["repositoryIdentity"] = repo("host/o/atlas") + }), + project("b", "solo", |p| p["repositoryIdentity"] = Value::Null), + ], + [], + ); + let other = |label: &str, machine, key: &str| EnvironmentProjects { + id: label.to_owned(), + label: label.to_owned(), + machine, + projects: build_entries( + &[project("r", "atlas", |p| { + p["repositoryIdentity"] = repo(key) + })], + [], + ), + }; + let others = [ + other("devbox1", MachineKind::Linux, "host/o/atlas"), + other("elsewhere", MachineKind::Cloud, "host/o/other"), + ]; + let joined = with_remote_members(&entries, &others); + let by_title = |t: &str| joined.iter().find(|e| e.title == t).unwrap(); + assert_eq!( + by_title("atlas").remote, + [RemoteMember { + label: "devbox1".to_owned(), + machine: MachineKind::Linux + }] + ); + assert!(by_title("solo").remote.is_empty()); + + // A group on the other environment joins the same way, through its repository key. + let grouped = EnvironmentProjects { + projects: build_entries( + &[ + project("r1", "atlas", |p| { + p["repositoryIdentity"] = repo("host/o/atlas") + }), + project("r2", "api", |p| { + p["repositoryIdentity"] = repo("host/o/atlas") + }), + ], + [], + ), + ..other("devbox2", MachineKind::Server, "x") + }; + let joined = with_remote_members(&entries, &[grouped]); + assert_eq!(by_title("atlas").remote.len(), 1); + assert_eq!( + joined.iter().find(|e| e.title == "atlas").unwrap().remote[0].label, + "devbox2" + ); + } + + #[test] + fn badge_lists_unique_labels_and_takes_the_first_machine() { + let member = |label: &str, machine| RemoteMember { + label: label.to_owned(), + machine, + }; + assert_eq!(badge_text(&[]), None); + assert_eq!(badge_machine(&[]), None); + let members = [ + member("devbox1", MachineKind::Linux), + member("Air", MachineKind::Laptop), + member("devbox1", MachineKind::Linux), + ]; + assert_eq!( + badge_text(&members).as_deref(), + Some("Also on Air, devbox1") + ); + assert_eq!(badge_machine(&members), Some(MachineKind::Laptop)); + assert_eq!(MachineKind::parse("mac-studio"), MachineKind::MacStudio); + assert_eq!(MachineKind::parse("quantum"), MachineKind::Server); + } + + fn named_repo(key: &str, display: &str, name: &str) -> Value { + let mut identity = repo(key); + identity["displayName"] = display.into(); + identity["name"] = name.into(); + identity + } + + #[test] + fn same_repository_projects_form_one_group_like_the_web() { + let identity = || named_repo("host/trogon/t3code", "TrogonStack/t3code", "t3code"); + let projects = [ + project("root", "t3code", |p| { + p["workspaceRoot"] = "/work/t3code/".into(); + p["repositoryIdentity"] = identity(); + p["createdAt"] = "2026-01-01T00:00:00.000Z".into(); + p["projectIcon"] = json!({"kind": "monogram", "text": "ZZ", "color": "rose"}); + }), + project("server", "server", |p| { + p["workspaceRoot"] = "/work/t3code/apps/server".into(); + p["repositoryIdentity"] = identity(); + p["createdAt"] = "2026-02-01T00:00:00.000Z".into(); + p["pinnedAt"] = "2026-03-01T00:00:00.000Z".into(); + }), + // Same physical path as "root": the fresher one wins, the loser still counts as a member. + project("root-dup", "t3code", |p| { + p["workspaceRoot"] = "/work/t3code".into(); + p["updatedAt"] = "2026-01-02T00:00:00.000Z".into(); + p["createdAt"] = "2026-03-01T00:00:00.000Z".into(); + }), + project("lone", "lone", |_| {}), + ]; + let entries = build_entries(&projects, []); + assert_eq!( + titles(&entries.iter().collect::>()), + ["TrogonStack/t3code", "lone"] + ); + let group = &entries[0]; + assert_eq!(group.key, "host/trogon/t3code"); + assert_eq!(group.logical_key.as_deref(), Some("host/trogon/t3code")); + // The duplicate path takes its identity from the freshest identified project, and the + // oldest winner (root-dup replaced root as winner) leads. + assert_eq!(group.member_ids, ["root", "root-dup", "server"]); + assert_eq!(group.id, "root-dup"); + assert!(group.pinned, "any member pinned"); + assert!(group.contains("server") && !group.contains("lone")); + assert_eq!(group.target(Some("server")), "server"); + assert_eq!(group.target(Some("lone")), "root-dup"); + assert_eq!(entries[1].key, "\0path:/work/lone"); + assert_eq!(entries[1].logical_key, None); + // The icon comes from the representative: root-dup has none, so its title is used. + assert_eq!(group.initials, "T3"); + + let mixed = [ + project("a", "a", |p| { + p["repositoryIdentity"] = repo("k"); + p["archivedAt"] = "2026-01-01T00:00:00.000Z".into(); + }), + project("b", "b", |p| p["repositoryIdentity"] = repo("k")), + ]; + assert_eq!( + build_entries(&mixed, []).len(), + 1, + "archived only when every member is" + ); + let all_archived = [ + project("a", "a", |p| { + p["repositoryIdentity"] = repo("k"); + p["archivedAt"] = "2026-01-01T00:00:00.000Z".into(); + }), + project("b", "b", |p| { + p["repositoryIdentity"] = repo("k"); + p["archivedAt"] = "2026-01-01T00:00:00.000Z".into(); + }), + ]; + assert!(build_entries(&all_archived, []).is_empty()); + } + + #[test] + fn group_label_follows_derive_project_group_label() { + let label = |rows: &[(&str, Value)]| { + let projects: Vec = rows + .iter() + .enumerate() + .map(|(index, (title, identity))| { + project(&format!("p{index}"), title, |p| { + p["repositoryIdentity"] = identity.clone(); + p["createdAt"] = format!("2026-01-0{}T00:00:00.000Z", index + 1).into(); + }) + }) + .collect(); + build_entries(&projects, [])[0].title.clone() + }; + let named = || named_repo("k", "Org/app", "app"); + let nameless = || { + let mut identity = repo("k"); + identity["name"] = "app".into(); + identity + }; + // A shared title wins unless it is the display or repository name. + assert_eq!(label(&[("mono", named()), ("mono", named())]), "mono"); + assert_eq!( + label(&[("Org/app", named()), ("Org/app", named())]), + "Org/app" + ); + assert_eq!(label(&[("app", named()), ("app", named())]), "Org/app"); + // Different titles: the single display name, else the single repository name. + assert_eq!(label(&[("app", named()), ("server", named())]), "Org/app"); + assert_eq!(label(&[("web", nameless()), ("server", nameless())]), "app"); + // Nothing shared: the representative's title. + assert_eq!(label(&[("web", repo("k")), ("server", repo("k"))]), "web"); + // A lone project keeps its own title. + assert_eq!(label(&[("app", named())]), "app"); + } } diff --git a/apps/desktop-gpui/src/server.rs b/apps/desktop-gpui/src/server.rs index a752fadefd304..2b760d331bd22 100644 --- a/apps/desktop-gpui/src/server.rs +++ b/apps/desktop-gpui/src/server.rs @@ -6,6 +6,7 @@ use crate::auth; use crate::config::{Config, ServerCommand}; use crate::connection::{self, ActionReceiver, Backoff, Environment}; +use crate::projects::MachineKind; use crate::status::{Status, StatusSender, publish}; use serde::{Deserialize, Serialize}; use std::io; @@ -161,38 +162,65 @@ fn forward_output(stream: impl AsyncRead + Unpin + Send + 'static) { #[derive(Deserialize)] struct Descriptor { + #[serde(rename = "environmentId", default)] + environment_id: String, label: String, #[serde(default)] + platform: Platform, + #[serde(default)] capabilities: Capabilities, } +#[derive(Default, Deserialize)] +struct Platform { + machine: Option, +} + #[derive(Default, Deserialize)] struct Capabilities { #[serde(rename = "projectOrganization", default)] project_organization: bool, } +/// One attempt at the environment descriptor. +pub async fn fetch_environment( + client: &reqwest::Client, + http_base: &str, +) -> Result { + let response = client + .get(format!("{http_base}{READINESS_PATH}")) + .timeout(READINESS_REQUEST_TIMEOUT) + .send() + .await + .map_err(|error| error.without_url().to_string())?; + if !response.status().is_success() { + return Err(format!("HTTP {}", response.status().as_u16())); + } + let descriptor: Descriptor = response + .json() + .await + .map_err(|error| error.without_url().to_string())?; + Ok(Environment { + id: descriptor.environment_id, + label: descriptor.label, + machine: descriptor + .platform + .machine + .as_deref() + .map_or(MachineKind::Server, MachineKind::parse), + project_organization: descriptor.capabilities.project_organization, + }) +} + /// Polls until the descriptor answers, returning what the connection needs from it. pub async fn wait_until_ready( client: &reqwest::Client, http_base: &str, ) -> Result { - let url = format!("{http_base}{READINESS_PATH}"); let deadline = Instant::now() + READINESS_TIMEOUT; loop { - let attempt = client - .get(&url) - .timeout(READINESS_REQUEST_TIMEOUT) - .send() - .await; - if let Ok(response) = attempt - && response.status().is_success() - && let Ok(descriptor) = response.json::().await - { - return Ok(Environment { - label: descriptor.label, - project_organization: descriptor.capabilities.project_organization, - }); + if let Ok(environment) = fetch_environment(client, http_base).await { + return Ok(environment); } if Instant::now() >= deadline { return Err(format!( diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index 0274a48ef221e..f1559520ad80d 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -1,4 +1,4 @@ -use crate::projects::ProjectEntry; +use crate::projects::{EnvironmentProjects, ProjectEntry}; use crate::wire::generated::ThreadsEntryStatus; use std::fmt; @@ -16,6 +16,8 @@ pub enum Status { shell: Option, }, Failed(String), + /// Another saved environment's projects; says nothing about the primary connection. + Environment(EnvironmentProjects), } /// What the window needs from the shell state, newest thread first. @@ -69,6 +71,7 @@ impl fmt::Display for Status { plural(*threads, "thread"), ), Self::Failed(message) => f.write_str(message), + Self::Environment(other) => write!(f, "Environment {}", other.label), } } } diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index 78720aa53f5d7..26b4652087e8b 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -1,5 +1,5 @@ use crate::connection::{Action, ActionSender}; -use crate::projects::{self, ProjectEntry}; +use crate::projects::{self, EnvironmentProjects, MachineKind, ProjectEntry}; use crate::status::{ShellView, Status, ThreadRow}; use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; use gpui::{ @@ -32,6 +32,8 @@ pub struct MainView { selected_project: Option, picker_open: bool, picker_order: Option>, + /// The other saved environments, by id. + environments: Vec, actions: ActionSender, } @@ -63,11 +65,19 @@ impl MainView { selected_project: None, picker_open: false, picker_order: None, + environments: Vec::new(), actions, } } pub fn set_status(&mut self, status: Status, cx: &mut Context) { + if let Status::Environment(other) = status { + match self.environments.iter_mut().find(|e| e.id == other.id) { + Some(known) => *known = other, + None => self.environments.push(other), + } + return cx.notify(); + } self.status = status; // Thread activity reorders recency on every delta, so the default must not follow it. if self.selected_project.is_none() { @@ -94,8 +104,8 @@ impl MainView { let shell = self.shell()?; let active = active_project(self.selected_project.as_deref(), shell); let entries = &shell.project_entries; - let listed = projects::picker_entries(entries, active.map(|e| e.id.as_str()), ""); - Some(listed.into_iter().map(|entry| entry.id.clone()).collect()) + let listed = projects::picker_entries(entries, selected_id(self, active), ""); + Some(listed.into_iter().map(|entry| entry.key.clone()).collect()) } fn environment(&self) -> String { @@ -170,8 +180,8 @@ impl MainView { .gap_6() .child(self.hero( &project, - active.map(|entry| entry.id.clone()), - shell.project_entries.clone(), + selected_id(self, active).map(str::to_owned), + projects::with_remote_members(&shell.project_entries, &self.environments), cx, )) .child(self.composer_box(cx)), @@ -219,11 +229,11 @@ impl MainView { let mono = theme.mono_font_family.clone(); let border = theme.border; let rows = listed.iter().enumerate().map(|(index, entry)| { - let (view, id) = (view.clone(), entry.id.clone()); - let (actions, pin_id) = (actions.clone(), entry.id.clone()); + let (view, id) = (view.clone(), entry.target(active_id.as_deref()).to_owned()); + let (actions, pin_ids) = (actions.clone(), entry.member_ids.clone()); let pinned = entry.pinned; let color: Hsla = rgb(entry.color_hex(dark)).into(); - let active = active_id.as_deref() == Some(entry.id.as_str()); + let active = active_id.as_deref().is_some_and(|id| entry.contains(id)); h_flex() .id(("project", index)) .group(PICKER_ROW_GROUP) @@ -259,7 +269,7 @@ impl MainView { .child(entry.title.clone()), ) .when(can_pin, |row| { - row.child(pin_button(index, entry, actions, pin_id)) + row.child(pin_button(index, entry, actions, pin_ids)) }) .when(!can_pin && pinned, |row| { row.child( @@ -268,6 +278,7 @@ impl MainView { .text_color(muted), ) }) + .children(environment_badge(index, entry, muted)) .on_click(move |_, _, cx| { view.update(cx, |this, cx| { this.selected_project = Some(id.clone()); @@ -421,6 +432,36 @@ impl RenderOnce for ProjectTrigger { } } +fn machine_icon(kind: MachineKind) -> gpui_kit_assets::IconName { + use gpui_kit_assets::IconName as Icons; + match kind { + MachineKind::Server => Icons::Server, + MachineKind::Cloud => Icons::Cloud, + MachineKind::Linux => Icons::Terminal, + MachineKind::Desktop => Icons::Monitor, + MachineKind::Laptop => Icons::Laptop, + MachineKind::MacMini => Icons::HardDrive, + MachineKind::MacStudio => Icons::Computer, + } +} + +/// The web's `ProjectEnvironmentBadge`: a machine icon with "Also on ", right of the pin. +fn environment_badge( + index: usize, + entry: &ProjectEntry, + muted: Hsla, +) -> Option> { + let text = projects::badge_text(&entry.remote)?; + let machine = projects::badge_machine(&entry.remote)?; + Some( + div() + .id(("environment", index)) + .flex_shrink_0() + .tooltip(move |window, cx| Tooltip::new(text.clone()).build(window, cx)) + .child(Icon::new(machine_icon(machine)).small().text_color(muted)), + ) +} + const PICKER_ROW_GROUP: &str = "project-row"; /// Shown on row hover, and always for a pinned project. A click toggles the pin and, because @@ -429,7 +470,7 @@ fn pin_button( index: usize, entry: &ProjectEntry, actions: ActionSender, - project_id: String, + project_ids: Vec, ) -> impl IntoElement + use<> { let pinned = entry.pinned; let icon = |name| Icon::new(name).small(); @@ -461,19 +502,35 @@ fn pin_button( div().child(icon(gpui_kit_assets::IconName::Pin)) }) .on_click(move |_, _, _| { - let action = Action::SetProjectPinned { - project_id: project_id.clone(), - pinned: !pinned, - }; - if actions.try_send(action).is_err() { - eprintln!("[ui] could not queue the pin request"); + // Clients write every member of the group together. + for project_id in &project_ids { + let action = Action::SetProjectPinned { + project_id: project_id.clone(), + pinned: !pinned, + }; + if actions.try_send(action).is_err() { + eprintln!("[ui] could not queue the pin request"); + } } }) } +/// The picked project id while its row still exists, else the active row's target. +fn selected_id<'a>(view: &'a MainView, active: Option<&'a ProjectEntry>) -> Option<&'a str> { + view.selected_project + .as_deref() + .filter(|id| active.is_some_and(|entry| entry.contains(id))) + .or(active.map(|entry| entry.id.as_str())) +} + /// The picked project while it still exists, else the newest thread's project, else the first listed. fn active_project<'a>(selected: Option<&str>, shell: &'a ShellView) -> Option<&'a ProjectEntry> { - let by_id = |id: &str| shell.project_entries.iter().find(|entry| entry.id == id); + let by_id = |id: &str| { + shell + .project_entries + .iter() + .find(|entry| entry.contains(id)) + }; selected .and_then(by_id) .or_else(|| { @@ -579,12 +636,16 @@ mod tests { fn entry(id: &str, scratch: bool) -> ProjectEntry { ProjectEntry { + key: id.to_owned(), id: id.to_owned(), + member_ids: vec![id.to_owned()], title: id.to_owned(), initials: "XX".to_owned(), color: 0, pinned: false, scratch, + logical_key: None, + remote: Vec::new(), } } From 932e357502405528d24d0e661e79b9707a184e8a Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 02:33:40 -0500 Subject: [PATCH 10/14] fix(desktop-gpui): thread list no longer reorders on every status change Rows were sorted by updatedAt, which the server bumps on each status change, so the list jumped while agents worked. The list now follows the web sidebar's default sections and anchors: pinned and active threads first, then collapsible Working, Snoozed and Settled shelves, each sorted the way the web sorts it. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/README.md | 2 +- apps/desktop-gpui/src/shell.rs | 615 ++++++++++++++++++++++++++++++-- apps/desktop-gpui/src/status.rs | 15 +- apps/desktop-gpui/src/ui.rs | 156 +++++++- 4 files changed, 753 insertions(+), 35 deletions(-) diff --git a/apps/desktop-gpui/README.md b/apps/desktop-gpui/README.md index f4647bc2a7dc5..54e901b44ef87 100644 --- a/apps/desktop-gpui/README.md +++ b/apps/desktop-gpui/README.md @@ -2,7 +2,7 @@ Native desktop client written in Rust with [GPUI](https://github.com/zed-industries/zed/tree/main/crates/gpui). It is meant to replace `apps/desktop` (Electron) over time and never embeds Node or Chromium. It starts the T3 server as a child process, authenticates without pairing, and opens the RPC WebSocket. -Status: connection shell. The window shows the connection state ("Starting server...", "Connecting...", "Connected to · N projects · M threads", or the error). The counts come from a live `orchestration.subscribeShell` subscription. Once connected, it draws a static skeleton of the main screen with [gpui-component](https://github.com/longbridge/gpui-component): a thread list (newest first) and an empty composer. Nothing is interactive yet. +Status: connection shell. The window shows the connection state ("Starting server...", "Connecting...", "Connected to · N projects · M threads", or the error). The counts come from a live `orchestration.subscribeShell` subscription. Once connected, it draws a static skeleton of the main screen with [gpui-component](https://github.com/longbridge/gpui-component): a thread list laid out like the web sidebar (pinned and active threads, then collapsible Working, Snoozed and Settled shelves) and an empty composer. Only the shelf headers are interactive. ## Run diff --git a/apps/desktop-gpui/src/shell.rs b/apps/desktop-gpui/src/shell.rs index c5471e51e907d..15aa77c499bbb 100644 --- a/apps/desktop-gpui/src/shell.rs +++ b/apps/desktop-gpui/src/shell.rs @@ -2,14 +2,287 @@ //! `packages/client-runtime/src/state/shellReducer.ts` for what this client shows. use crate::projects; -use crate::status::{ShellView, ThreadRow}; +use crate::status::{Section, ShellView, ThreadRow}; use crate::wire::generated::{ OrchestrationSubscribeShellSuccess as Item, OrchestrationSubscribeShellSuccessVariant4Location as Location, OrchestrationSubscribeShellSuccessVariant5Location as RemovedLocation, }; +use crate::wire::generated::{ + ThreadsEntryActivityRunStatus as Activity, ThreadsEntryInteractionMode as InteractionMode, + ThreadsEntryPendingRuntimeRequestKind as RequestKind, ThreadsEntrySettledOverride as Override, + ThreadsEntryStatus as RunStatus, +}; use crate::wire::{Project, Thread}; +use std::cmp::Ordering; use std::collections::HashMap; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Epoch milliseconds of an RFC 3339 timestamp, as `Date.parse` reads the server's. +fn timestamp_ms(text: &str) -> Option { + let bytes = text.as_bytes(); + let number = |from: usize, len: usize| -> Option { + let digits = text.get(from..from + len)?; + digits + .bytes() + .all(|b| b.is_ascii_digit()) + .then(|| digits.parse().ok())? + }; + let (year, month, day) = (number(0, 4)?, number(5, 2)?, number(8, 2)?); + let (hour, minute, second) = (number(11, 2)?, number(14, 2)?, number(17, 2)?); + if bytes.get(4) != Some(&b'-') + || bytes.get(7) != Some(&b'-') + || !matches!(bytes.get(10), Some(b'T' | b't' | b' ')) + || bytes.get(13) != Some(&b':') + || bytes.get(16) != Some(&b':') + || !(1..=12).contains(&month) + || !(1..=31).contains(&day) + || hour > 23 + || minute > 59 + || second > 59 + { + return None; + } + let mut at = 19; + let mut millis = 0; + if bytes.get(at) == Some(&b'.') { + let digits = text[at + 1..] + .bytes() + .take_while(u8::is_ascii_digit) + .count(); + if digits == 0 { + return None; + } + let fraction = &text[at + 1..at + 1 + digits.min(3)]; + millis = fraction.parse::().ok()? * 10_i64.pow(3 - fraction.len() as u32); + at += 1 + digits; + } + let offset_minutes = match text.get(at..)? { + "Z" | "z" => 0, + zone if zone.len() == 6 && (zone.starts_with('+') || zone.starts_with('-')) => { + let hours = number(at + 1, 2)?; + let minutes = number(at + 4, 2)?; + let total = hours * 60 + minutes; + if zone.starts_with('-') { -total } else { total } + } + _ => return None, + }; + let (y, m) = if month <= 2 { + (year - 1, month + 9) + } else { + (year, month - 3) + }; + let era = y.div_euclid(400); + let year_of_era = y - era * 400; + let day_of_year = (153 * m + 2) / 5 + day - 1; + let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year; + let days = era * 146_097 + day_of_era - 719_468; + let seconds = days * 86_400 + hour * 3_600 + minute * 60 + second - offset_minutes * 60; + Some(seconds * 1_000 + millis) +} + +const SECTION_ORDER: [Section; 5] = [ + Section::Pinned, + Section::Active, + Section::Working, + Section::Snoozed, + Section::Settled, +]; + +fn ms(text: Option<&str>) -> Option { + text.and_then(timestamp_ms) +} + +fn is_active(status: RunStatus) -> bool { + matches!( + status, + RunStatus::Preparing + | RunStatus::Queued + | RunStatus::Starting + | RunStatus::Running + | RunStatus::Waiting + ) +} + +/// The runtime status the web derives: the activity run's while one is open, else the thread's. +fn runtime_status(thread: &Thread) -> Option { + if thread.latest_run_id.is_none() && thread.active_provider_thread_id.is_none() { + return None; + } + Some(match thread.activity_run_status { + Some(activity) => match activity { + Activity::Preparing => RunStatus::Preparing, + Activity::Starting => RunStatus::Starting, + Activity::Running => RunStatus::Running, + Activity::Waiting => RunStatus::Waiting, + }, + None => thread.status, + }) +} + +fn pending_request(thread: &Thread) -> Option { + thread.pending_runtime_request.as_ref().map(|r| r.kind) +} + +/// `isThreadWorking` in `packages/client-runtime/src/state/threadInbox.ts`. +fn is_working(thread: &Thread) -> bool { + if pending_request(thread).is_some_and(|kind| kind != RequestKind::AuthRefresh) { + return false; + } + let status = runtime_status(thread); + if status.is_none_or(|s| !is_active(s) && s != RunStatus::Idle) { + return false; + } + let run_settled = thread.latest_run_id.is_some() + && !is_active(run_status(thread)) + && thread.active_run_id != thread.latest_run_id; + !(thread.interaction_mode == InteractionMode::Plan + && thread.has_actionable_proposed_plan + && run_settled) +} + +fn run_status(thread: &Thread) -> RunStatus { + match thread.status { + RunStatus::Idle => RunStatus::Completed, + other => other, + } +} + +fn run_completed_ms(thread: &Thread) -> Option { + thread.latest_run_id.as_ref()?; + match thread.latest_run_completed_at.as_deref() { + Some(at) => timestamp_ms(at), + None if thread.status == RunStatus::Idle || !is_active(thread.status) => { + timestamp_ms(&thread.updated_at) + } + None => None, + } +} + +/// `threadRaisedHandWhileSnoozed` without the subagent-request case, which needs other threads. +fn raised_hand(thread: &Thread) -> bool { + if pending_request(thread).is_some() || thread.pending_guest_message_count.unwrap_or(0) > 0 { + return true; + } + if runtime_status(thread) != Some(RunStatus::Failed) { + return false; + } + let failed_at = if thread.status == RunStatus::Failed { + run_completed_ms(thread) + } else { + timestamp_ms(&thread.updated_at) + }; + match ms(thread.snoozed_at.as_deref()) { + None => true, + Some(snoozed) => failed_at.is_some_and(|failed| failed > snoozed), + } +} + +/// `effectiveSnoozed` in `packages/client-runtime/src/state/threadSettled.ts`. +fn is_snoozed(thread: &Thread, now_ms: i64) -> bool { + ms(thread.snoozed_until.as_deref()).is_some_and(|wake| wake > now_ms) && !raised_hand(thread) +} + +/// Same precedence as `resolveSidebarThreadSection`, then the Working shelf split. +fn section_of(thread: &Thread, now_ms: i64) -> Section { + if is_snoozed(thread, now_ms) { + Section::Snoozed + } else if thread.settled_override == Some(Override::Settled) { + Section::Settled + } else if thread.pinned_at.is_some() { + Section::Pinned + } else if is_working(thread) { + Section::Working + } else { + Section::Active + } +} + +fn newest_first(a: (i64, &Thread), b: (i64, &Thread)) -> Ordering { + b.0.cmp(&a.0).then_with(|| a.1.id.cmp(&b.1.id)) +} + +fn created_ms(thread: &Thread) -> i64 { + timestamp_ms(&thread.created_at).unwrap_or(0) +} + +/// `sortPinnedThreadsByOrderKey`: keyed threads by key, then keyless newest created first. +fn sort_pinned(threads: &mut [&Thread]) { + threads.sort_by(|a, b| match (&a.pin_order_key, &b.pin_order_key) { + (Some(left), Some(right)) => left.cmp(right).then_with(|| a.id.cmp(&b.id)), + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + (None, None) => newest_first((created_ms(a), a), (created_ms(b), b)), + }); +} + +/// `sortInboxThreadsByReturn` with no observed returns. +fn sort_inbox(threads: &mut [&Thread]) { + let anchor = |t: &Thread| { + [ + Some(created_ms(t)), + ms(t.unsettled_at.as_deref()), + t.latest_run_id + .as_ref() + .and(ms(t.latest_run_requested_at.as_deref())), + run_completed_ms(t), + ] + .into_iter() + .flatten() + .max() + .unwrap_or(0) + }; + threads.sort_by(|a, b| newest_first((anchor(a), a), (anchor(b), b))); +} + +/// `sortWorkingThreadsBySend`. +fn sort_working(threads: &mut [&Thread]) { + let anchor = |t: &Thread| { + let sent = t.latest_user_authored_message_at.as_deref().or_else(|| { + t.latest_run_id + .as_ref() + .and(t.latest_run_requested_at.as_deref()) + }); + created_ms(t).max(ms(sent).unwrap_or(0)) + }; + threads.sort_by(|a, b| newest_first((anchor(a), a), (anchor(b), b))); +} + +/// Oldest wake first. +fn sort_snoozed(threads: &mut [&Thread]) { + let wake = |t: &Thread| ms(t.snoozed_until.as_deref()).unwrap_or(0); + threads.sort_by(|a, b| wake(a).cmp(&wake(b)).then_with(|| a.id.cmp(&b.id))); +} + +/// `sortSettledThreads` with `resolveSettledThreadTimestamp`. +fn sort_settled(threads: &mut [&Thread]) { + let anchor = |t: &Thread| { + if let Some(settled) = ms(t.settled_at.as_deref()) { + return settled; + } + let latest = [ + ms(t.latest_user_message_at.as_deref()), + t.latest_run_id + .as_ref() + .and(ms(t.latest_run_requested_at.as_deref())), + t.latest_run_id + .as_ref() + .and(ms(t.latest_run_started_at.as_deref())), + run_completed_ms(t), + ] + .into_iter() + .flatten() + .max(); + latest.or_else(|| timestamp_ms(&t.updated_at)).unwrap_or(0) + }; + threads.sort_by(|a, b| newest_first((anchor(a), a), (anchor(b), b))); +} + +fn now_ms() -> i64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |d| d.as_millis() as i64) +} #[derive(Default)] pub struct ShellState { @@ -34,30 +307,44 @@ impl ShellState { self.threads.len() } - /// Threads newest first (ties by id); the first project is the oldest by creation. + /// Rows in the web sidebar's default order (see `Section`); the first project is the + /// oldest by creation. pub fn view(&self) -> ShellView { - let mut rows: Vec = self - .threads - .values() - .map(|thread| ThreadRow { - id: thread.id.clone(), - project_id: thread.project_id.clone(), - project: self - .projects - .get(&thread.project_id) - .map(|project| project.title.clone()) - .unwrap_or_default(), - title: thread.title.clone(), - branch: thread.branch.clone(), - status: thread.status, - updated_at: thread.updated_at.clone(), - }) + self.view_at(now_ms()) + } + + /// Mirrors `partitionSidebarThreads` in `apps/web/src/components/Sidebar.tsx` under the + /// default settings: Working shelf on, no project folders, nothing dragged. + pub fn view_at(&self, now_ms: i64) -> ShellView { + let mut sections: [Vec<&Thread>; 5] = Default::default(); + for thread in self.threads.values() { + sections[section_of(thread, now_ms) as usize].push(thread); + } + let [pinned, active, working, snoozed, settled] = &mut sections; + sort_pinned(pinned); + sort_inbox(active); + sort_working(working); + sort_snoozed(snoozed); + sort_settled(settled); + let row = |thread: &Thread, section| ThreadRow { + id: thread.id.clone(), + project_id: thread.project_id.clone(), + project: self + .projects + .get(&thread.project_id) + .map(|project| project.title.clone()) + .unwrap_or_default(), + title: thread.title.clone(), + branch: thread.branch.clone(), + status: thread.status, + section, + }; + let rows = SECTION_ORDER + .into_iter() + .zip(§ions) + .flat_map(|(section, threads)| threads.iter().map(move |thread| (section, *thread))) + .map(|(section, thread)| row(thread, section)) .collect(); - rows.sort_by(|a, b| { - b.updated_at - .cmp(&a.updated_at) - .then_with(|| a.id.cmp(&b.id)) - }); ShellView { projects: self.project_count(), threads: self.thread_count(), @@ -210,12 +497,288 @@ mod tests { assert_eq!(state.project_count(), 0); } + const NOW: i64 = 1_800_000_000_000; + + fn thread(id: &str, edit: impl FnOnce(&mut Value)) -> Thread { + let mut value: Value = serde_json::from_str(THREAD_UPDATED).expect("json"); + let mut thread = value["thread"].take(); + thread["id"] = id.into(); + edit(&mut thread); + serde_json::from_value(thread).expect("thread decodes") + } + + fn view_of(threads: Vec) -> ShellView { + let mut state = seeded(); + state.threads = threads.into_iter().map(|t| (t.id.clone(), t)).collect(); + state.view_at(NOW) + } + + fn ids(view: &ShellView) -> Vec<&str> { + view.rows.iter().map(|row| row.id.as_str()).collect() + } + + fn set(thread: &mut Value, fields: &[(&str, Value)]) { + for (key, value) in fields { + thread[*key] = value.clone(); + } + } + + #[test] + fn timestamps_parse_like_date_parse() { + for (text, expected) in [ + ("1970-01-01T00:00:00.000Z", Some(0)), + ("2000-03-01T00:00:00Z", Some(951_868_800_000)), + ("2026-07-01T00:00:00.123Z", Some(1_782_864_000_123)), + ("2026-07-01T02:00:00.5+02:00", Some(1_782_864_000_500)), + ("2026-07-01", None), + ("nope", None), + ] { + assert_eq!(timestamp_ms(text), expected, "{text}"); + } + } + + #[test] + fn an_updated_at_bump_does_not_reorder_active_threads() { + let old = |bump: &str| { + thread("old", |t| { + set( + t, + &[ + ("createdAt", "2026-01-01T00:00:00.000Z".into()), + ("updatedAt", bump.into()), + ], + ) + }) + }; + let new = thread("new", |t| { + set(t, &[("createdAt", "2026-02-01T00:00:00.000Z".into())]) + }); + for bump in ["2026-01-01T00:00:00.000Z", "2030-12-01T00:00:00.000Z"] { + let view = view_of(vec![old(bump), new.clone()]); + assert_eq!(ids(&view), ["new", "old"], "updatedAt {bump}"); + } + } + + #[test] + fn active_threads_sort_by_latest_anchor_and_ignore_the_order_key() { + let at = |id: &str, fields: Vec<(&str, Value)>| thread(id, |t| set(t, &fields)); + let created = |m: &str| Value::from(format!("2026-{m}-01T00:00:00.000Z")); + let view = view_of(vec![ + at("a-created", vec![("createdAt", created("01"))]), + at( + "b-unsettled", + vec![("createdAt", created("01")), ("unsettledAt", created("05"))], + ), + at( + "c-keyed", + vec![("createdAt", created("02")), ("activeOrderKey", "a".into())], + ), + at( + "d-run", + vec![ + ("createdAt", created("01")), + ("latestRunId", "run".into()), + ("status", "completed".into()), + ("latestRunRequestedAt", created("03")), + ("latestRunCompletedAt", created("04")), + ], + ), + at("e-tie", vec![("createdAt", created("01"))]), + ]); + assert_eq!( + ids(&view), + ["b-unsettled", "d-run", "c-keyed", "a-created", "e-tie"] + ); + } + + #[test] + fn pinned_threads_sort_keyed_first_then_newest_created() { + let pinned = |id: &str, key: Option<&str>, created: &str| { + thread(id, |t| { + set( + t, + &[ + ("pinnedAt", "2026-03-01T00:00:00.000Z".into()), + ("createdAt", created.into()), + ], + ); + if let Some(key) = key { + t["pinOrderKey"] = key.into(); + } + }) + }; + let view = view_of(vec![ + pinned("keyless-old", None, "2026-01-01T00:00:00.000Z"), + pinned("keyed-b", Some("b"), "2026-01-01T00:00:00.000Z"), + pinned("keyless-new", None, "2026-02-01T00:00:00.000Z"), + pinned("keyed-a", Some("a"), "2026-02-01T00:00:00.000Z"), + ]); + assert_eq!( + ids(&view), + ["keyed-a", "keyed-b", "keyless-new", "keyless-old"] + ); + assert!(view.rows.iter().all(|r| r.section == Section::Pinned)); + } + + #[test] + fn settled_threads_sort_by_settled_at_then_the_fallback_chain() { + let settled = |id: &str, fields: Vec<(&str, Value)>| { + thread(id, |t| { + set(t, &[("settledOverride", "settled".into())]); + set(t, &fields); + }) + }; + let day = |d: u32| Value::from(format!("2026-05-{d:02}T00:00:00.000Z")); + let view = view_of(vec![ + settled("settled-at", vec![("settledAt", day(20))]), + settled("user-message", vec![("latestUserMessageAt", day(15))]), + settled( + "run-started", + vec![ + ("latestRunId", "r".into()), + ("status", "completed".into()), + ("latestRunStartedAt", day(18)), + ("latestRunCompletedAt", day(10)), + ], + ), + settled("updated", vec![("updatedAt", day(12))]), + settled("unparsable", vec![("updatedAt", "garbage".into())]), + ]); + assert_eq!( + ids(&view), + [ + "settled-at", + "run-started", + "user-message", + "updated", + "unparsable" + ] + ); + assert!(view.rows.iter().all(|r| r.section == Section::Settled)); + } + + #[test] + fn sections_follow_snooze_then_settled_then_pinned_then_working() { + let future = "2030-12-01T00:00:00.000Z"; + let running = |t: &mut Value| { + set( + t, + &[ + ("latestRunId", "run".into()), + ("activeRunId", "run".into()), + ("status", "running".into()), + ], + ) + }; + let view = view_of(vec![ + thread("idle", |_| {}), + thread("working", running), + thread("pinned-working", |t| { + running(t); + t["pinnedAt"] = "2026-03-01T00:00:00.000Z".into(); + }), + thread("settled-pinned", |t| { + set( + t, + &[ + ("settledOverride", "settled".into()), + ("pinnedAt", "2026-03-01T00:00:00.000Z".into()), + ], + ) + }), + thread("snoozed", |t| { + set( + t, + &[ + ("snoozedUntil", future.into()), + ("snoozedAt", "2026-03-01T00:00:00.000Z".into()), + ], + ) + }), + thread("woken", |t| { + t["snoozedUntil"] = "2026-01-01T00:00:00.000Z".into() + }), + thread("snoozed-with-approval", |t| { + set( + t, + &[ + ("snoozedUntil", future.into()), + ( + "pendingRuntimeRequest", + serde_json::json!({"id": "q", "kind": "command", "createdAt": "2026-03-01T00:00:00.000Z"}), + ), + ], + ) + }), + thread("plan-ready", |t| { + set( + t, + &[ + ("interactionMode", "plan".into()), + ("hasActionableProposedPlan", true.into()), + ("latestRunId", "run".into()), + ("activeRunId", Value::Null), + ("status", "completed".into()), + ], + ) + }), + ]); + let sections: HashMap<_, _> = view + .rows + .iter() + .map(|row| (row.id.as_str(), row.section)) + .collect(); + assert_eq!( + sections, + HashMap::from([ + ("idle", Section::Active), + ("working", Section::Working), + ("pinned-working", Section::Pinned), + ("settled-pinned", Section::Settled), + ("snoozed", Section::Snoozed), + ("woken", Section::Active), + ("snoozed-with-approval", Section::Active), + ("plan-ready", Section::Active), + ]) + ); + let order: Vec<_> = view.rows.iter().map(|r| r.section).collect(); + assert!(order.is_sorted_by_key(|s| *s as u8)); + } + + #[test] + fn working_threads_sort_by_latest_send_and_snoozed_by_wake() { + let running = |id: &str, sent: &str| { + thread(id, |t| { + set( + t, + &[ + ("latestRunId", "run".into()), + ("activeRunId", "run".into()), + ("status", "running".into()), + ("latestUserAuthoredMessageAt", sent.into()), + ], + ) + }) + }; + let snoozed = |id: &str, until: &str| thread(id, |t| t["snoozedUntil"] = until.into()); + let view = view_of(vec![ + running("sent-early", "2026-08-01T00:00:00.000Z"), + running("sent-late", "2026-09-01T00:00:00.000Z"), + snoozed("wakes-late", "2030-02-01T00:00:00.000Z"), + snoozed("wakes-soon", "2030-01-01T00:00:00.000Z"), + ]); + assert_eq!( + ids(&view), + ["sent-late", "sent-early", "wakes-soon", "wakes-late"] + ); + } + #[test] - fn view_lists_newest_threads_first_with_project_names() { + fn view_names_projects_and_counts_threads() { let mut state = seeded(); state.apply(with(THREAD_UPDATED, |value| { value["thread"]["id"] = "thread-new".into(); - value["thread"]["updatedAt"] = "2026-07-01T00:00:00.000Z".into(); + value["thread"]["createdAt"] = "2026-07-01T00:00:00.000Z".into(); })); let view = state.view(); assert_eq!((view.projects, view.threads), (1, 2)); @@ -225,8 +788,6 @@ mod tests { .map(|e| e.title.as_str()) .collect(); assert_eq!(titles, ["Project"]); - let ids: Vec<_> = view.rows.iter().map(|row| row.id.as_str()).collect(); - assert_eq!(ids, ["thread-new", "thread-v2"]); assert!(view.rows.iter().all(|row| row.project == "Project")); } diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index f1559520ad80d..4199193a84f6d 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -20,7 +20,18 @@ pub enum Status { Environment(EnvironmentProjects), } -/// What the window needs from the shell state, newest thread first. +/// Where the web sidebar lists a thread. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Section { + Pinned, + Active, + Working, + Snoozed, + Settled, +} + +/// What the window needs from the shell state, `rows` in sidebar order: sections in +/// `Section` order, each sorted as the web sorts it. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct ShellView { pub projects: usize, @@ -38,7 +49,7 @@ pub struct ThreadRow { pub title: String, pub branch: Option, pub status: ThreadsEntryStatus, - pub updated_at: String, + pub section: Section, } fn plural(count: usize, noun: &str) -> String { diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index 26b4652087e8b..735624efe285f 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -1,6 +1,6 @@ use crate::connection::{Action, ActionSender}; use crate::projects::{self, EnvironmentProjects, MachineKind, ProjectEntry}; -use crate::status::{ShellView, Status, ThreadRow}; +use crate::status::{Section, ShellView, Status, ThreadRow}; use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; use gpui::{ Anchor, AnyElement, App, Context, Entity, Focusable as _, FontWeight, Hsla, IntoElement, @@ -29,6 +29,8 @@ pub struct MainView { project_search: Entity, composer: Entity, list: UniformListScrollHandle, + /// Working, Snoozed and Settled shelves; the web starts all of them collapsed. + shelves: [bool; 3], selected_project: Option, picker_open: bool, picker_order: Option>, @@ -62,6 +64,7 @@ impl MainView { .auto_grow(3, 10) }), list: UniformListScrollHandle::new(), + shelves: [false; 3], selected_project: None, picker_open: false, picker_order: None, @@ -116,7 +119,9 @@ impl MainView { } fn sidebar(&self, cx: &mut Context) -> impl IntoElement { - let count = self.shell().map_or(0, |shell| shell.rows.len()); + let count = self + .shell() + .map_or(0, |shell| sidebar_items(&shell.rows, self.shelves).len()); v_flex() .w(px(SIDEBAR_WIDTH)) .flex_shrink_0() @@ -136,9 +141,27 @@ impl MainView { return Vec::new(); }; let (muted, hover) = (cx.theme().muted_foreground, cx.theme().list_hover); - shell.rows[range] + let items = sidebar_items(&shell.rows, this.shelves); + items[range] .iter() - .map(|row| thread_row(row, muted, hover)) + .map(|item| match *item { + SidebarItem::Row(index) => { + thread_row(&shell.rows[index], muted, hover).into_any_element() + } + SidebarItem::Header { + section, + count, + expanded, + } => { + let shelf = shelf_index(section); + shelf_header(section, count, expanded, muted, hover) + .on_click(cx.listener(move |this, _, _, cx| { + this.shelves[shelf] = !this.shelves[shelf]; + cx.notify(); + })) + .into_any_element() + } + }) .collect() }), ) @@ -565,6 +588,92 @@ fn status_tag(status: ThreadStatus) -> Option { Some(tag.small().child(label)) } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum SidebarItem { + Row(usize), + Header { + section: Section, + count: usize, + expanded: bool, + }, +} + +fn shelf_index(section: Section) -> usize { + match section { + Section::Working => 0, + Section::Snoozed => 1, + _ => 2, + } +} + +/// The web's default list: pinned and active rows, then a header per shelf (Working and +/// Snoozed only when non-empty, Settled always) with its rows only while expanded. +fn sidebar_items(rows: &[ThreadRow], expanded: [bool; 3]) -> Vec { + if rows.is_empty() { + return Vec::new(); + } + let mut items = Vec::with_capacity(rows.len() + 3); + for (index, row) in rows.iter().enumerate() { + if matches!(row.section, Section::Pinned | Section::Active) { + items.push(SidebarItem::Row(index)); + } + } + for section in [Section::Working, Section::Snoozed, Section::Settled] { + let count = rows.iter().filter(|row| row.section == section).count(); + if count == 0 && section != Section::Settled { + continue; + } + let expanded = expanded[shelf_index(section)]; + items.push(SidebarItem::Header { + section, + count, + expanded, + }); + if expanded { + let shelf = rows + .iter() + .enumerate() + .filter(|(_, row)| row.section == section); + items.extend(shelf.map(|(index, _)| SidebarItem::Row(index))); + } + } + items +} + +fn shelf_header( + section: Section, + count: usize, + expanded: bool, + muted: Hsla, + hover: Hsla, +) -> gpui::Stateful { + let label = match section { + Section::Working => "Working", + Section::Snoozed => "Snoozed", + _ => "Settled", + }; + let chevron = if expanded { + IconName::ChevronDown + } else { + IconName::ChevronRight + }; + h_flex() + .id(label) + .h(px(ROW_HEIGHT)) + .w_full() + .px_2() + .gap_2() + .items_end() + .pb_2() + .rounded_md() + .text_xs() + .text_color(muted) + .hover(|style| style.bg(hover)) + .child(Icon::new(chevron).small()) + .child(div().flex_1().child(label)) + .child(count.to_string()) +} + fn thread_row(row: &ThreadRow, muted: Hsla, hover: Hsla) -> impl IntoElement + use<> { v_flex() .id(row.id.clone()) @@ -657,10 +766,47 @@ mod tests { title: String::new(), branch: None, status: ThreadStatus::Idle, - updated_at: String::new(), + section: Section::Active, } } + #[test] + fn sidebar_items_keep_shelves_collapsed_until_expanded() { + let in_section = |id: &str, section| ThreadRow { section, ..row(id) }; + let rows = vec![ + in_section("p", Section::Pinned), + in_section("a", Section::Active), + in_section("w", Section::Working), + in_section("s", Section::Settled), + ]; + let header = |section, count, expanded| SidebarItem::Header { + section, + count, + expanded, + }; + assert_eq!( + sidebar_items(&rows, [false; 3]), + [ + SidebarItem::Row(0), + SidebarItem::Row(1), + header(Section::Working, 1, false), + header(Section::Settled, 1, false), + ] + ); + assert_eq!( + sidebar_items(&rows, [true, false, true]), + [ + SidebarItem::Row(0), + SidebarItem::Row(1), + header(Section::Working, 1, true), + SidebarItem::Row(2), + header(Section::Settled, 1, true), + SidebarItem::Row(3), + ] + ); + assert!(sidebar_items(&[], [true; 3]).is_empty()); + } + #[test] fn active_project_prefers_a_live_pick_then_the_newest_real_project() { let shell = ShellView { From ab7f56ba686a3318e1379fa3ecb9f99ff8b31415 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 02:43:44 -0500 Subject: [PATCH 11/14] fix(desktop-gpui): tooltips and the scratch link match the web Tooltips used gpui-component's default text-sm and a key chip, and the "or start without a project" link stayed muted on hover. Tooltips now use the web TooltipPopup sizing with the shortcut as plain text, and the link is medium weight and turns foreground with an underline on hover, like the web's muted InlineButton. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/src/ui.rs | 56 ++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index 735624efe285f..8d1de4e01c66a 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -220,6 +220,7 @@ impl MainView { cx: &mut Context, ) -> impl IntoElement { let view = cx.entity(); + let foreground = cx.theme().foreground; let (actions, can_pin) = (self.actions.clone(), self.can_pin()); let order = self.picker_order.clone(); let search = self.project_search.clone(); @@ -352,15 +353,13 @@ impl MainView { div() .id("start-without-project") .text_sm() + .font_weight(FontWeight::MEDIUM) .text_color(cx.theme().muted_foreground) .cursor_pointer() - .hover(|style| style.underline()) - .tooltip(|window, cx| { - Tooltip::element(|_, _| { - Kbd::new(Keystroke::parse("alt-cmd-n").expect("valid keystroke")) - }) - .build(window, cx) - }) + .hover(|style| style.underline().text_color(foreground)) + .tooltip(tooltip(Kbd::format( + &Keystroke::parse("alt-cmd-n").expect("valid keystroke"), + ))) .child("or start without a project"), ) } @@ -384,12 +383,16 @@ impl MainView { h_flex() .justify_between() .child( - Button::new("attach") - .ghost() - .small() - .icon(gpui_kit_assets::IconName::Paperclip) - .tooltip("Attach files") - .on_click(|_, _, _| {}), + div() + .id("attach-tooltip") + .tooltip(tooltip("Attach files")) + .child( + Button::new("attach") + .ghost() + .small() + .icon(gpui_kit_assets::IconName::Paperclip) + .on_click(|_, _, _| {}), + ), ) .child( Button::new("send") @@ -439,7 +442,7 @@ impl Selectable for ProjectTrigger { impl RenderOnce for ProjectTrigger { fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { - let tooltip = self.name.clone(); + let name = self.name.clone(); div() .id("project") .ml_2() @@ -450,11 +453,25 @@ impl RenderOnce for ProjectTrigger { .cursor_pointer() .when(self.open, |name| name.underline()) .hover(|style| style.underline()) - .tooltip(move |window, cx| Tooltip::new(tooltip.clone()).build(window, cx)) + .tooltip(tooltip(name)) .child(self.name) } } +/// The web's `TooltipPopup` sizing: `text-xs`, `px-2 py-1`, `rounded-md`. +fn tooltip( + text: impl Into, +) -> impl Fn(&mut Window, &mut App) -> gpui::AnyView + 'static { + let text = text.into(); + move |window, cx| { + Tooltip::new(text.clone()) + .text_xs() + .py_1() + .rounded(px(6.)) + .build(window, cx) + } +} + fn machine_icon(kind: MachineKind) -> gpui_kit_assets::IconName { use gpui_kit_assets::IconName as Icons; match kind { @@ -480,7 +497,7 @@ fn environment_badge( div() .id(("environment", index)) .flex_shrink_0() - .tooltip(move |window, cx| Tooltip::new(text.clone()).build(window, cx)) + .tooltip(tooltip(text)) .child(Icon::new(machine_icon(machine)).small().text_color(muted)), ) } @@ -497,10 +514,10 @@ fn pin_button( ) -> impl IntoElement + use<> { let pinned = entry.pinned; let icon = |name| Icon::new(name).small(); - Button::new(("pin", index)) + let hint = tooltip(projects::pin_tooltip(&entry.title, pinned)); + let button = Button::new(("pin", index)) .ghost() .xsmall() - .tooltip(projects::pin_tooltip(&entry.title, pinned)) .when(!pinned, |button| { button .invisible() @@ -535,7 +552,8 @@ fn pin_button( eprintln!("[ui] could not queue the pin request"); } } - }) + }); + div().id(("pin-tooltip", index)).tooltip(hint).child(button) } /// The picked project id while its row still exists, else the active row's target. From 25b500d6898c7d81357aed2e93b5e7528270b2ee Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 02:55:44 -0500 Subject: [PATCH 12/14] feat(desktop-gpui): start without a project, with the web's picker popup Signed-off-by: Yordis Prieto --- apps/desktop-gpui/src/assets.rs | 27 ++++ apps/desktop-gpui/src/projects.rs | 4 +- apps/desktop-gpui/src/ui.rs | 232 ++++++++++++++++++++++++------ 3 files changed, 214 insertions(+), 49 deletions(-) diff --git a/apps/desktop-gpui/src/assets.rs b/apps/desktop-gpui/src/assets.rs index dc388b45670d4..a931d406b08c1 100644 --- a/apps/desktop-gpui/src/assets.rs +++ b/apps/desktop-gpui/src/assets.rs @@ -9,10 +9,27 @@ gpui_kit_assets::icon_assets!( ] ); +pub const MESSAGE_SQUARE_DASHED: &str = "icons/message-square-dashed.svg"; + +/// lucide-react 0.564.0 `message-square-dashed`, the icon the server gives the no-project workspace. +const MESSAGE_SQUARE_DASHED_SVG: &str = concat!( + r#""#, + r#""#, + r#""#, + r#""#, + r#""#, + r#""#, +); + pub struct AppAssets; impl AssetSource for AppAssets { fn load(&self, path: &str) -> Result>> { + if path == MESSAGE_SQUARE_DASHED { + return Ok(Some(Cow::Borrowed(MESSAGE_SQUARE_DASHED_SVG.as_bytes()))); + } match ExtraIcons.load(path)? { Some(bytes) => Ok(Some(bytes)), None => gpui_kit_assets::Assets.load(path), @@ -21,6 +38,9 @@ impl AssetSource for AppAssets { fn list(&self, path: &str) -> Result> { let mut paths = gpui_kit_assets::Assets.list(path)?; + if MESSAGE_SQUARE_DASHED.starts_with(path) { + paths.push(MESSAGE_SQUARE_DASHED.into()); + } paths.extend(ExtraIcons.list(path)?); Ok(paths) } @@ -49,5 +69,12 @@ mod tests { let loaded = AppAssets.load(&icon.path()).ok().flatten(); assert!(loaded.is_some(), "{} is not embedded", icon.path()); } + assert!( + AppAssets + .load(MESSAGE_SQUARE_DASHED) + .ok() + .flatten() + .is_some() + ); } } diff --git a/apps/desktop-gpui/src/projects.rs b/apps/desktop-gpui/src/projects.rs index 9e834311932ab..4d502fd403053 100644 --- a/apps/desktop-gpui/src/projects.rs +++ b/apps/desktop-gpui/src/projects.rs @@ -110,9 +110,9 @@ impl ProjectEntry { active_id.filter(|id| self.contains(id)).unwrap_or(&self.id) } - /// The badge text color for the theme; the badge background is this at 14% alpha. + /// The badge text color for the theme (gray for the scratch project, whose server-set icon is gray); the badge background is this at 14% alpha. pub fn color_hex(&self, dark: bool) -> u32 { - let (_, light, night) = COLORS[self.color]; + let (_, light, night) = COLORS[if self.scratch { 0 } else { self.color }]; if dark { night } else { light } } } diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index 8d1de4e01c66a..cbf9a9e5c9f9b 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -1,3 +1,4 @@ +use crate::assets::MESSAGE_SQUARE_DASHED; use crate::connection::{Action, ActionSender}; use crate::projects::{self, EnvironmentProjects, MachineKind, ProjectEntry}; use crate::status::{Section, ShellView, Status, ThreadRow}; @@ -9,7 +10,6 @@ use gpui::{ }; use gpui_component::{ ActiveTheme, Icon, IconName, Selectable, Sizable, TitleBar, - breadcrumb::{Breadcrumb, BreadcrumbItem}, button::{Button, ButtonVariants}, h_flex, input::{Input, InputEvent, InputState, Textarea, TextareaState}, @@ -22,6 +22,8 @@ use gpui_component::{ const SIDEBAR_WIDTH: f32 = 300.; const ROW_HEIGHT: f32 = 68.; +const NO_PROJECT: &str = "No project"; +const HEADLINE_SCRATCH: &str = "What should we work on?"; pub struct MainView { status: Status, @@ -179,6 +181,7 @@ impl MainView { .into_any_element(); }; let active = active_project(self.selected_project.as_deref(), shell); + let scratch = active.is_some_and(|entry| entry.scratch); let project = active.map_or_else(|| "your project".to_owned(), |entry| entry.title.clone()); v_flex() .flex_1() @@ -192,17 +195,14 @@ impl MainView { .items_center() .border_b_1() .border_color(cx.theme().border) - .child( - Breadcrumb::new() - .child(BreadcrumbItem::new(project.clone())) - .child(BreadcrumbItem::new("New thread")), - ), + .child(self.breadcrumb(active, cx)), ) .child( centered(cx) .gap_6() .child(self.hero( &project, + scratch, selected_id(self, active).map(str::to_owned), projects::with_remote_members(&shell.project_entries, &self.environments), cx, @@ -212,16 +212,82 @@ impl MainView { .into_any_element() } + fn breadcrumb( + &self, + active: Option<&ProjectEntry>, + cx: &mut Context, + ) -> impl IntoElement { + let theme = cx.theme(); + let (foreground, muted, dark) = (theme.foreground, theme.muted_foreground, theme.is_dark()); + let mono = theme.mono_font_family.clone(); + let project = active.map(|entry| { + let color: Hsla = rgb(entry.color_hex(dark)).into(); + let icon = if entry.scratch { + Icon::empty() + .path(MESSAGE_SQUARE_DASHED) + .size(px(14.)) + .text_color(color) + .into_any_element() + } else { + div() + .flex() + .flex_shrink_0() + .items_center() + .justify_center() + .size(px(14.)) + .rounded(px(3.5)) + .bg(color.opacity(0.14)) + .text_color(color) + .text_size(px(7.21875)) + .font_weight(FontWeight::BOLD) + .font_family(mono) + .child(entry.initials.clone()) + .into_any_element() + }; + h_flex() + .gap_3() + .child( + h_flex() + .id("breadcrumb-project") + .gap(px(6.)) + .min_w_0() + .text_color(muted) + .hover(move |style| style.text_color(foreground)) + .child(icon) + .child( + div() + .max_w(px(160.)) + .truncate() + .child(project_label(Some(entry))), + ), + ) + .child(div().flex_shrink_0().text_color(muted).child("/")) + }); + h_flex() + .gap_3() + .items_center() + .text_sm() + .font_weight(FontWeight::MEDIUM) + .children(project) + .child(div().text_color(foreground).child("New thread")) + } + fn hero( &self, project: &str, + scratch: bool, active_id: Option, entries: Vec, cx: &mut Context, ) -> impl IntoElement { let view = cx.entity(); + let link_view = view.clone(); let foreground = cx.theme().foreground; let (actions, can_pin) = (self.actions.clone(), self.can_pin()); + let scratch_id = self + .shell() + .and_then(|shell| scratch_entry(&shell.project_entries)) + .map(|entry| entry.id.clone()); let order = self.picker_order.clone(); let search = self.project_search.clone(); let picker = Popover::new("project-picker") @@ -240,9 +306,10 @@ impl MainView { }); } }) - .trigger(ProjectTrigger::new(project)) - .content(move |_, _, cx| { + .trigger(ProjectTrigger::new(project, scratch)) + .content(move |_, window, cx| { let query = search.read(cx).value(); + let searching = search.focus_handle(cx).is_focused(window); let mut listed = projects::picker_entries(&entries, active_id.as_deref(), &query); if let Some(order) = &order { listed = projects::order_while_open(listed, order); @@ -251,7 +318,7 @@ impl MainView { let (dark, muted) = (theme.is_dark(), theme.muted_foreground); let (hover, selected) = (theme.list_hover, theme.list_active); let mono = theme.mono_font_family.clone(); - let border = theme.border; + let (border, ring) = (theme.border, theme.ring); let rows = listed.iter().enumerate().map(|(index, entry)| { let (view, id) = (view.clone(), entry.target(active_id.as_deref()).to_owned()); let (actions, pin_ids) = (actions.clone(), entry.member_ids.clone()); @@ -311,19 +378,34 @@ impl MainView { }); }) }); + // The web's `ComboboxPopup` (`w-72`, at most 23rem tall) and `ComboboxSearchInput`. v_flex() - .w(px(400.)) + .w(px(288.)) + .rounded_lg() + .overflow_hidden() .child( - h_flex().px_3().border_b_1().border_color(border).child( - Input::new(&search) - .appearance(false) - .prefix(Icon::new(IconName::Search)), + div().px_3().pt(px(10.)).child( + h_flex() + .pb(px(6.)) + .border_b_1() + .border_color(if searching { ring } else { border.opacity(0.7) }) + .child( + Input::new(&search) + .appearance(false) + .h(px(26.)) + .px_0() + .prefix( + Icon::new(IconName::Search) + .size_4() + .text_color(muted.opacity(0.55)), + ), + ), ), ) .child( v_flex() .id("project-list") - .max_h(px(320.)) + .max_h(px(325.)) .overflow_y_scroll() .p_1() .children(rows) @@ -338,30 +420,52 @@ impl MainView { }), ) }); - v_flex() - .items_center() - .gap_1() - .child( - h_flex() - .items_center() - .text_2xl() - .child("What should we build in") - .child(picker) - .child("?"), - ) - .child( - div() - .id("start-without-project") - .text_sm() - .font_weight(FontWeight::MEDIUM) - .text_color(cx.theme().muted_foreground) - .cursor_pointer() - .hover(|style| style.underline().text_color(foreground)) - .tooltip(tooltip(Kbd::format( - &Keystroke::parse("alt-cmd-n").expect("valid keystroke"), - ))) - .child("or start without a project"), - ) + let (heading, below) = if scratch { + let heading = h_flex() + .items_center() + .text_3xl() + .text_color(foreground) + .child(HEADLINE_SCRATCH) + .into_any_element(); + (heading, picker.into_any_element()) + } else { + let heading = h_flex() + .items_center() + .text_2xl() + .child("What should we build in") + .child(picker) + .child("?") + .into_any_element(); + let link = div() + .id("start-without-project") + .font_weight(FontWeight::MEDIUM) + .text_color(cx.theme().muted_foreground) + .cursor_pointer() + .hover(|style| style.underline().text_color(foreground)) + .tooltip(tooltip(Kbd::format( + &Keystroke::parse("alt-cmd-n").expect("valid keystroke"), + ))) + .on_click(move |_, _, cx| { + let Some(id) = scratch_id.clone() else { return }; + link_view.update(cx, |this, cx| { + this.selected_project = Some(id); + this.picker_open = false; + cx.notify(); + }); + }) + .child("or start without a project") + .into_any_element(); + (heading, link) + }; + v_flex().items_center().child(heading).child( + h_flex() + .mt_2() + .h_6() + .items_center() + .justify_center() + .text_sm() + .child(below), + ) } fn composer_box(&self, cx: &mut Context) -> impl IntoElement { @@ -406,16 +510,19 @@ impl MainView { } /// The project name as plain heading text that underlines on hover and while its picker is open. +/// Under a scratch draft it is the `InlineButton` `picker` tone at `text-sm`, `font-medium`. #[derive(IntoElement)] struct ProjectTrigger { name: String, + scratch: bool, open: bool, } impl ProjectTrigger { - fn new(name: &str) -> Self { + fn new(name: &str, scratch: bool) -> Self { Self { - name: name.to_owned(), + name: if scratch { NO_PROJECT } else { name }.to_owned(), + scratch, open: false, } } @@ -443,21 +550,38 @@ impl Selectable for ProjectTrigger { impl RenderOnce for ProjectTrigger { fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { let name = self.name.clone(); - div() + let trigger = div() .id("project") - .ml_2() .min_w_0() .max_w_full() .truncate() - .text_2xl() .cursor_pointer() .when(self.open, |name| name.underline()) - .hover(|style| style.underline()) - .tooltip(tooltip(name)) - .child(self.name) + .hover(|style| style.underline()); + if self.scratch { + trigger.font_weight(FontWeight::MEDIUM).child(self.name) + } else { + trigger + .ml_2() + .text_2xl() + .tooltip(tooltip(name)) + .child(self.name) + } } } +fn project_label(entry: Option<&ProjectEntry>) -> String { + match entry { + Some(entry) if entry.scratch => NO_PROJECT.to_owned(), + Some(entry) => entry.title.clone(), + None => String::new(), + } +} + +fn scratch_entry(entries: &[ProjectEntry]) -> Option<&ProjectEntry> { + entries.iter().find(|entry| entry.scratch) +} + /// The web's `TooltipPopup` sizing: `text-xs`, `px-2 py-1`, `rounded-md`. fn tooltip( text: impl Into, @@ -825,6 +949,20 @@ mod tests { assert!(sidebar_items(&[], [true; 3]).is_empty()); } + #[test] + fn scratch_reads_as_no_project_and_is_found_in_the_entries() { + let mut none = entry("none", true); + none.title = "Whatever the server titled it".to_owned(); + assert_eq!(project_label(Some(&none)), "No project"); + assert_eq!(project_label(Some(&entry("alpha", false))), "alpha"); + assert_eq!(project_label(None), ""); + let entries = [entry("alpha", false), none]; + assert_eq!(scratch_entry(&entries).map(|e| e.id.as_str()), Some("none")); + assert!(scratch_entry(&entries[..1]).is_none()); + assert_eq!(ProjectTrigger::new("alpha", true).name, "No project"); + assert_eq!(ProjectTrigger::new("alpha", false).name, "alpha"); + } + #[test] fn active_project_prefers_a_live_pick_then_the_newest_real_project() { let shell = ShellView { From b6535f3b5b070cebe5a4914ce9c86d5460d053cb Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 5 Nov 2026 03:00:03 -0500 Subject: [PATCH 13/14] fix(desktop-gpui): project picker popup stays below its trigger Signed-off-by: Yordis Prieto --- apps/desktop-gpui/src/ui.rs | 50 +++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index cbf9a9e5c9f9b..1822c899b5015 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -4,9 +4,9 @@ use crate::projects::{self, EnvironmentProjects, MachineKind, ProjectEntry}; use crate::status::{Section, ShellView, Status, ThreadRow}; use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; use gpui::{ - Anchor, AnyElement, App, Context, Entity, Focusable as _, FontWeight, Hsla, IntoElement, - Keystroke, ParentElement, Render, RenderOnce, Styled, UniformListScrollHandle, Window, div, - prelude::*, px, rgb, uniform_list, + Anchor, AnyElement, App, Bounds, Context, Entity, Focusable as _, FontWeight, Hsla, + IntoElement, Keystroke, ParentElement, Pixels, Render, RenderOnce, Styled, + UniformListScrollHandle, Window, canvas, div, prelude::*, px, rgb, uniform_list, }; use gpui_component::{ ActiveTheme, Icon, IconName, Selectable, Sizable, TitleBar, @@ -19,11 +19,18 @@ use gpui_component::{ tooltip::Tooltip, v_flex, }; +use std::{cell::Cell, rc::Rc}; const SIDEBAR_WIDTH: f32 = 300.; const ROW_HEIGHT: f32 = 68.; const NO_PROJECT: &str = "No project"; const HEADLINE_SCRATCH: &str = "What should we work on?"; +/// The web's `ComboboxPopup` cap, `max-h-[min(var(--available-height),23rem)]`. +const PICKER_MAX_HEIGHT: f32 = 368.; +/// `ComboboxSearchInput`: `pt-2.5`, a 26px input, `pb-1.5` and its 1px border. +const PICKER_SEARCH_HEIGHT: f32 = 43.; +/// The popover's 4px offset from the trigger plus the positioner's 4px viewport margin. +const PICKER_GAP: f32 = 8.; pub struct MainView { status: Status, @@ -35,6 +42,8 @@ pub struct MainView { shelves: [bool; 3], selected_project: Option, picker_open: bool, + /// Last painted trigger bounds, so the popup can shrink to fit below it as on the web. + picker_trigger: Rc>>, picker_order: Option>, /// The other saved environments, by id. environments: Vec, @@ -69,6 +78,7 @@ impl MainView { shelves: [false; 3], selected_project: None, picker_open: false, + picker_trigger: Rc::default(), picker_order: None, environments: Vec::new(), actions, @@ -290,6 +300,7 @@ impl MainView { .map(|entry| entry.id.clone()); let order = self.picker_order.clone(); let search = self.project_search.clone(); + let trigger_bounds = self.picker_trigger.clone(); let picker = Popover::new("project-picker") .anchor(Anchor::TopCenter) .open(self.picker_open) @@ -306,10 +317,18 @@ impl MainView { }); } }) - .trigger(ProjectTrigger::new(project, scratch)) + .trigger(ProjectTrigger::new( + project, + scratch, + trigger_bounds.clone(), + )) .content(move |_, window, cx| { let query = search.read(cx).value(); let searching = search.focus_handle(cx).is_focused(window); + // Shrinking keeps the positioner from flipping the popup over the headline. + let below = window.viewport_size().height - trigger_bounds.get().bottom(); + let list_height = + (below - px(PICKER_GAP)).min(px(PICKER_MAX_HEIGHT)) - px(PICKER_SEARCH_HEIGHT); let mut listed = projects::picker_entries(&entries, active_id.as_deref(), &query); if let Some(order) = &order { listed = projects::order_while_open(listed, order); @@ -405,7 +424,7 @@ impl MainView { .child( v_flex() .id("project-list") - .max_h(px(325.)) + .max_h(list_height.max(px(0.))) .overflow_y_scroll() .p_1() .children(rows) @@ -516,14 +535,16 @@ struct ProjectTrigger { name: String, scratch: bool, open: bool, + bounds: Rc>>, } impl ProjectTrigger { - fn new(name: &str, scratch: bool) -> Self { + fn new(name: &str, scratch: bool, bounds: Rc>>) -> Self { Self { name: if scratch { NO_PROJECT } else { name }.to_owned(), scratch, open: false, + bounds, } } } @@ -550,8 +571,15 @@ impl Selectable for ProjectTrigger { impl RenderOnce for ProjectTrigger { fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { let name = self.name.clone(); + let bounds = self.bounds; let trigger = div() .id("project") + .relative() + .child( + canvas(move |rect, _, _| bounds.set(rect), |_, _, _, _| {}) + .absolute() + .inset_0(), + ) .min_w_0() .max_w_full() .truncate() @@ -959,8 +987,14 @@ mod tests { let entries = [entry("alpha", false), none]; assert_eq!(scratch_entry(&entries).map(|e| e.id.as_str()), Some("none")); assert!(scratch_entry(&entries[..1]).is_none()); - assert_eq!(ProjectTrigger::new("alpha", true).name, "No project"); - assert_eq!(ProjectTrigger::new("alpha", false).name, "alpha"); + assert_eq!( + ProjectTrigger::new("alpha", true, Rc::default()).name, + "No project" + ); + assert_eq!( + ProjectTrigger::new("alpha", false, Rc::default()).name, + "alpha" + ); } #[test] From 4ed3a1226b792cd11ea49dbaccf5797aef8b0719 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Fri, 6 Nov 2026 02:46:26 -0500 Subject: [PATCH 14/14] feat(desktop-gpui): sidebar cards, shelves, overlay drawer and composer strip match the web The desktop shell should feel identical to the web app, so the sidebar, composer and context strip follow the web's layout, breakpoints and data. Signed-off-by: Yordis Prieto --- apps/desktop-gpui/README.md | 2 + apps/desktop-gpui/src/assets.rs | 179 +- apps/desktop-gpui/src/connection.rs | 143 +- apps/desktop-gpui/src/history.rs | 65 + apps/desktop-gpui/src/main.rs | 26 +- apps/desktop-gpui/src/projects.rs | 4 + .../src/provider-icons/antigravity.png | Bin 0 -> 12862 bytes .../src/provider-icons/claudeAgent.svg | 1 + .../src/provider-icons/codex-dark.svg | 1 + .../src/provider-icons/codex-light.svg | 1 + .../src/provider-icons/cursor-dark.svg | 1 + .../src/provider-icons/cursor-light.svg | 1 + .../src/provider-icons/fx-dark.svg | 1 + .../src/provider-icons/fx-light.svg | 1 + .../src/provider-icons/grok-dark.svg | 1 + .../src/provider-icons/grok-light.svg | 1 + apps/desktop-gpui/src/provider-icons/kiro.svg | 1 + apps/desktop-gpui/src/provider-icons/muse.svg | 1 + .../src/provider-icons/opencode-dark.svg | 1 + .../src/provider-icons/opencode-light.svg | 1 + .../src/provider-icons/pi-dark.svg | 1 + .../src/provider-icons/pi-light.svg | 1 + apps/desktop-gpui/src/providers.rs | 1251 ++++++++++++ apps/desktop-gpui/src/rpc.rs | 18 + apps/desktop-gpui/src/shell.rs | 117 +- apps/desktop-gpui/src/stage-dev-button.svg | 48 + apps/desktop-gpui/src/stage-dev.svg | 62 + apps/desktop-gpui/src/status.rs | 113 +- apps/desktop-gpui/src/strip.rs | 279 +++ apps/desktop-gpui/src/ui.rs | 1805 +++++++++++++++-- 30 files changed, 3889 insertions(+), 238 deletions(-) create mode 100644 apps/desktop-gpui/src/history.rs create mode 100644 apps/desktop-gpui/src/provider-icons/antigravity.png create mode 100644 apps/desktop-gpui/src/provider-icons/claudeAgent.svg create mode 100644 apps/desktop-gpui/src/provider-icons/codex-dark.svg create mode 100644 apps/desktop-gpui/src/provider-icons/codex-light.svg create mode 100644 apps/desktop-gpui/src/provider-icons/cursor-dark.svg create mode 100644 apps/desktop-gpui/src/provider-icons/cursor-light.svg create mode 100644 apps/desktop-gpui/src/provider-icons/fx-dark.svg create mode 100644 apps/desktop-gpui/src/provider-icons/fx-light.svg create mode 100644 apps/desktop-gpui/src/provider-icons/grok-dark.svg create mode 100644 apps/desktop-gpui/src/provider-icons/grok-light.svg create mode 100644 apps/desktop-gpui/src/provider-icons/kiro.svg create mode 100644 apps/desktop-gpui/src/provider-icons/muse.svg create mode 100644 apps/desktop-gpui/src/provider-icons/opencode-dark.svg create mode 100644 apps/desktop-gpui/src/provider-icons/opencode-light.svg create mode 100644 apps/desktop-gpui/src/provider-icons/pi-dark.svg create mode 100644 apps/desktop-gpui/src/provider-icons/pi-light.svg create mode 100644 apps/desktop-gpui/src/providers.rs create mode 100644 apps/desktop-gpui/src/stage-dev-button.svg create mode 100644 apps/desktop-gpui/src/stage-dev.svg create mode 100644 apps/desktop-gpui/src/strip.rs diff --git a/apps/desktop-gpui/README.md b/apps/desktop-gpui/README.md index 54e901b44ef87..f0da0ef8ad1f4 100644 --- a/apps/desktop-gpui/README.md +++ b/apps/desktop-gpui/README.md @@ -70,6 +70,8 @@ Without the variable the same test fails when the committed file is stale. Drift Known gaps: typify cannot flatten the `projectIcon` union (an `anyOf` with a `{kind: string}` catch-all), so `ProjectsEntry.projectIcon` is kept opaque (`serde_json::Value`) through the `OPAQUE` list. Unions without a discriminator generate as `#[serde(untagged)]` enums. +`server.getConfig` is not generated: typify cannot represent its schema. `src/providers.rs` decodes the small subset the composer's model trigger reads (provider snapshots and the provider and default-model settings) straight from the JSON, dropping entries it cannot read. + ## Notes - `gpui` and `gpui_platform` are the crates.io packages `gpui-pre` and `gpui-pre-platform`, pinned to the exact version `gpui-component` requires. Bump them together with `gpui-component` and `gpui-kit-assets`. diff --git a/apps/desktop-gpui/src/assets.rs b/apps/desktop-gpui/src/assets.rs index a931d406b08c1..89a784a71f4eb 100644 --- a/apps/desktop-gpui/src/assets.rs +++ b/apps/desktop-gpui/src/assets.rs @@ -5,7 +5,41 @@ use std::borrow::Cow; gpui_kit_assets::icon_assets!( ExtraIcons, [ - Paperclip, Pin, PinOff, Server, Cloud, Terminal, Monitor, Laptop, HardDrive, Computer + Paperclip, + Pin, + PinOff, + Server, + Cloud, + Terminal, + Monitor, + Laptop, + HardDrive, + Computer, + ListFilter, + FolderPlus, + SquarePen, + Folder, + FolderGit, + FolderGit2, + GitBranch, + ShieldOff, + Lock, + PenLine, + Sparkles, + Settings, + GitPullRequest, + ChartNoAxesColumn, + CircleArrowUp, + CircleDashed, + CircleDotDashed, + CircleAlert, + Clock, + Check, + GitPullRequestArrow, + GitPullRequestDraft, + GitPullRequestClosed, + GitMerge, + Layers ] ); @@ -23,6 +57,92 @@ const MESSAGE_SQUARE_DASHED_SVG: &str = concat!( r#""#, ); +/// The web's dev `SidebarStageBackdrop` art, dark palette, faded into the sidebar. +pub const STAGE_DEV: &str = "images/stage-dev.svg"; +/// The same art cropped like the web's `StageBackdropButtonArt`, for the send button. +pub const STAGE_DEV_BUTTON: &str = "images/stage-dev-button.svg"; + +/// The web's provider icons (`ProviderInstanceIcon`), one file per color scheme where the fills differ. +const PROVIDER_ICONS: &[(&str, &[u8])] = &[ + ( + "providers/antigravity.png", + include_bytes!("provider-icons/antigravity.png") as &[u8], + ), + ( + "providers/claudeAgent.svg", + include_bytes!("provider-icons/claudeAgent.svg") as &[u8], + ), + ( + "providers/codex-dark.svg", + include_bytes!("provider-icons/codex-dark.svg") as &[u8], + ), + ( + "providers/codex-light.svg", + include_bytes!("provider-icons/codex-light.svg") as &[u8], + ), + ( + "providers/cursor-dark.svg", + include_bytes!("provider-icons/cursor-dark.svg") as &[u8], + ), + ( + "providers/cursor-light.svg", + include_bytes!("provider-icons/cursor-light.svg") as &[u8], + ), + ( + "providers/fx-dark.svg", + include_bytes!("provider-icons/fx-dark.svg") as &[u8], + ), + ( + "providers/fx-light.svg", + include_bytes!("provider-icons/fx-light.svg") as &[u8], + ), + ( + "providers/grok-dark.svg", + include_bytes!("provider-icons/grok-dark.svg") as &[u8], + ), + ( + "providers/grok-light.svg", + include_bytes!("provider-icons/grok-light.svg") as &[u8], + ), + ( + "providers/kiro.svg", + include_bytes!("provider-icons/kiro.svg") as &[u8], + ), + ( + "providers/muse.svg", + include_bytes!("provider-icons/muse.svg") as &[u8], + ), + ( + "providers/opencode-dark.svg", + include_bytes!("provider-icons/opencode-dark.svg") as &[u8], + ), + ( + "providers/opencode-light.svg", + include_bytes!("provider-icons/opencode-light.svg") as &[u8], + ), + ( + "providers/pi-dark.svg", + include_bytes!("provider-icons/pi-dark.svg") as &[u8], + ), + ( + "providers/pi-light.svg", + include_bytes!("provider-icons/pi-light.svg") as &[u8], + ), +]; + +/// The icon asset for a driver, or `None` for the drivers the web draws as initials. +pub fn provider_icon(driver: &str, dark: bool) -> Option<&'static str> { + let scheme = if dark { "dark" } else { "light" }; + [ + format!("providers/{driver}-{scheme}.svg"), + format!("providers/{driver}.svg"), + format!("providers/{driver}.png"), + ] + .iter() + .find_map(|wanted| PROVIDER_ICONS.iter().find(|(path, _)| path == wanted)) + .map(|(path, _)| *path) +} + pub struct AppAssets; impl AssetSource for AppAssets { @@ -30,6 +150,15 @@ impl AssetSource for AppAssets { if path == MESSAGE_SQUARE_DASHED { return Ok(Some(Cow::Borrowed(MESSAGE_SQUARE_DASHED_SVG.as_bytes()))); } + if path == STAGE_DEV { + return Ok(Some(Cow::Borrowed(include_bytes!("stage-dev.svg")))); + } + if path == STAGE_DEV_BUTTON { + return Ok(Some(Cow::Borrowed(include_bytes!("stage-dev-button.svg")))); + } + if let Some((_, bytes)) = PROVIDER_ICONS.iter().find(|(known, _)| *known == path) { + return Ok(Some(Cow::Borrowed(bytes))); + } match ExtraIcons.load(path)? { Some(bytes) => Ok(Some(bytes)), None => gpui_kit_assets::Assets.load(path), @@ -64,6 +193,30 @@ mod tests { IconName::Monitor, IconName::Laptop, IconName::HardDrive, + IconName::ListFilter, + IconName::FolderPlus, + IconName::SquarePen, + IconName::Folder, + IconName::FolderGit, + IconName::FolderGit2, + IconName::GitBranch, + IconName::ShieldOff, + IconName::Lock, + IconName::PenLine, + IconName::Sparkles, + IconName::GitPullRequest, + IconName::ChartNoAxesColumn, + IconName::CircleArrowUp, + IconName::CircleDashed, + IconName::CircleDotDashed, + IconName::CircleAlert, + IconName::Clock, + IconName::Check, + IconName::GitPullRequestArrow, + IconName::GitPullRequestDraft, + IconName::GitPullRequestClosed, + IconName::GitMerge, + IconName::Layers, IconName::Computer, ] { let loaded = AppAssets.load(&icon.path()).ok().flatten(); @@ -77,4 +230,28 @@ mod tests { .is_some() ); } + + #[test] + fn provider_icons_follow_the_color_scheme() { + assert_eq!( + provider_icon("codex", true), + Some("providers/codex-dark.svg") + ); + assert_eq!( + provider_icon("codex", false), + Some("providers/codex-light.svg") + ); + assert_eq!( + provider_icon("claudeAgent", true), + Some("providers/claudeAgent.svg") + ); + assert_eq!( + provider_icon("antigravity", false), + Some("providers/antigravity.png") + ); + assert_eq!(provider_icon("acpRegistry", false), None); + for (path, _) in PROVIDER_ICONS { + assert!(AppAssets.load(path).ok().flatten().is_some(), "{path}"); + } + } } diff --git a/apps/desktop-gpui/src/connection.rs b/apps/desktop-gpui/src/connection.rs index 39ca9e93cb2dd..758939d0bbdea 100644 --- a/apps/desktop-gpui/src/connection.rs +++ b/apps/desktop-gpui/src/connection.rs @@ -3,9 +3,13 @@ use crate::auth::{self, AuthError, Credential}; use crate::electron_dpop; use crate::projects::MachineKind; -use crate::rpc::{Event, PING_FRAME, ProjectsMutate, Session, SubscribeShell}; +use crate::providers::Catalog; +use crate::rpc::{ + Event, PING_FRAME, ProjectsMutate, ServerGetConfig, Session, SubscribeShell, VcsListRefs, +}; use crate::shell::ShellState; use crate::status::{Status, StatusSender, publish}; +use crate::strip::{self, Branch}; use crate::wire::{ProjectMutation, ProjectMutationType, ShellItem, SubscribeShellPayload}; use futures_util::{SinkExt, StreamExt}; use std::collections::HashMap; @@ -32,7 +36,14 @@ pub struct Environment { /// A request from the window that the connection sends as an RPC. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Action { - SetProjectPinned { project_id: String, pinned: bool }, + SetProjectPinned { + project_id: String, + pinned: bool, + }, + /// The branch checked out in a project's workspace root, for the strip under the composer. + LoadBranch { + root: String, + }, } pub type ActionSender = async_channel::Sender; @@ -43,30 +54,33 @@ impl Action { match self { Self::SetProjectPinned { pinned: true, .. } => "pin project", Self::SetProjectPinned { pinned: false, .. } => "unpin project", + Self::LoadBranch { .. } => "load the branch", } } +} - fn payload(&self, command_id: String) -> ProjectMutation { - match self { - Self::SetProjectPinned { project_id, pinned } => ProjectMutation::Variant1 { - archived: None, - auto_pull: None, - command_id, - default_model_selection: None, - default_thread_env_mode: None, - favicon_path: None, - pinned: Some(*pinned), - project_icon: None, - project_id: project_id.clone(), - scripts: None, - title: None, - type_: ProjectMutationType::ProjectUpdate, - workspace_root: None, - }, - } +fn pin_mutation(project_id: &str, pinned: bool, command_id: String) -> ProjectMutation { + ProjectMutation::Variant1 { + archived: None, + auto_pull: None, + command_id, + default_model_selection: None, + default_thread_env_mode: None, + favicon_path: None, + pinned: Some(pinned), + project_icon: None, + project_id: project_id.to_owned(), + scripts: None, + title: None, + type_: ProjectMutationType::ProjectUpdate, + workspace_root: None, } } +fn refs_query(root: &str) -> serde_json::Value { + serde_json::json!({"cwd": root, "refKind": "local", "limit": strip::REFS_LIMIT}) +} + /// Doubling delay between attempts, capped (Electron restarts the backend the same way). pub struct Backoff { next: Duration, @@ -154,6 +168,7 @@ pub async fn run( status, Status::Connected { label: environment.label.clone(), + machine: environment.machine, can_pin: environment.project_organization, shell: None, }, @@ -233,9 +248,21 @@ async fn serve( if let Err(error) = sink.send(Message::text(request)).await { return error.to_string(); } + let mut config_request = match session.request::(&serde_json::json!({})) { + Ok((id, frame)) => match sink.send(Message::text(frame)).await { + Ok(()) => Some(id), + Err(error) => return error.to_string(), + }, + Err(error) => { + eprintln!("[rpc] could not request the provider config: {error}"); + None + } + }; // Whatever was clicked while disconnected is stale by now. while actions.try_recv().is_ok() {} let mut pending: HashMap = HashMap::new(); + // Branch lookups by request id, with the workspace root each was for. + let mut branches: HashMap = HashMap::new(); let mut ping = interval(PING_INTERVAL); ping.set_missed_tick_behavior(MissedTickBehavior::Delay); let mut deadline = Instant::now() + PING_TIMEOUT; @@ -250,9 +277,21 @@ async fn serve( let Ok(action) = action else { return "window closed".to_owned(); }; - match session.request::(&action.payload(command_id())) { - Ok((id, frame)) => { - pending.insert(id, action.describe()); + let request = match &action { + Action::SetProjectPinned { project_id, pinned } => session + .request::(&pin_mutation(project_id, *pinned, command_id())) + .map(|(id, frame)| (id, frame, None)), + Action::LoadBranch { root } => session + .request::(&refs_query(root)) + .map(|(id, frame)| (id, frame, Some(root.clone()))), + }; + match request { + Ok((id, frame, root)) => { + if let Some(root) = root { + branches.insert(id, root); + } else { + pending.insert(id, action.describe()); + } if let Err(error) = sink.send(Message::text(frame)).await { return error.to_string(); } @@ -281,6 +320,15 @@ async fn serve( } } for event in reaction.events { + if config_request.as_ref().is_some_and(|id| owns(&event, id)) { + config_request = None; + publish_catalog(event, status); + continue; + } + if let Some(root) = request_id(&event).and_then(|id| branches.remove(id)) { + publish_branch(root, event, status); + continue; + } if settle(&mut pending, &event) { continue; } @@ -302,6 +350,42 @@ fn command_id() -> String { format!("gpui-{}", hex::encode(bytes)) } +fn request_id(event: &Event) -> Option<&str> { + match event { + Event::Done { request_id, .. } | Event::Failed { request_id, .. } => Some(request_id), + _ => None, + } +} + +fn owns(event: &Event, id: &str) -> bool { + request_id(event) == Some(id) +} + +/// Publishes the branch of a workspace root; a failure leaves the strip without git controls. +fn publish_branch(root: String, event: Event, status: &StatusSender) { + let branch = match event { + Event::Done { value, .. } => Branch::decode(&value), + Event::Failed { message, .. } => { + eprintln!("[rpc] could not load the branch of {root}: {message}"); + None + } + _ => return, + }; + publish(status, Status::Branch { root, branch }); +} + +/// Publishes the provider catalog; a failure leaves the composer without a model trigger. +fn publish_catalog(event: Event, status: &StatusSender) { + match event { + Event::Done { value, .. } => match Catalog::decode(value) { + Ok(catalog) => publish(status, Status::Providers(catalog)), + Err(error) => eprintln!("[rpc] unreadable provider config: {error}"), + }, + Event::Failed { message, .. } => eprintln!("[rpc] could not load providers: {message}"), + _ => {} + } +} + /// Consumes the outcome of an action's request, logging a failure; true when `event` was one. fn settle(pending: &mut HashMap, event: &Event) -> bool { match event { @@ -332,6 +416,7 @@ fn handle( status, Status::Connected { label: environment.label.clone(), + machine: environment.machine, can_pin: environment.project_organization, shell: Some(shell.view()), }, @@ -363,11 +448,7 @@ mod tests { #[test] fn pin_action_builds_a_project_update_mutation() { - let pin = Action::SetProjectPinned { - project_id: "p1".to_owned(), - pinned: true, - }; - let value = serde_json::to_value(pin.payload("c1".to_owned())).expect("json"); + let value = serde_json::to_value(pin_mutation("p1", true, "c1".to_owned())).expect("json"); assert_eq!( value, serde_json::json!({ @@ -377,11 +458,7 @@ mod tests { "pinned": true, }) ); - let unpin = Action::SetProjectPinned { - project_id: "p1".to_owned(), - pinned: false, - }; - let value = serde_json::to_value(unpin.payload("c2".to_owned())).expect("json"); + let value = serde_json::to_value(pin_mutation("p1", false, "c2".to_owned())).expect("json"); assert_eq!(value["pinned"], false); } diff --git a/apps/desktop-gpui/src/history.rs b/apps/desktop-gpui/src/history.rs new file mode 100644 index 0000000000000..a80e44a65816f --- /dev/null +++ b/apps/desktop-gpui/src/history.rs @@ -0,0 +1,65 @@ +/// Back/forward over visited projects, the GPUI stand-in for the web's route history. +#[derive(Default)] +pub struct History { + visited: Vec, + index: usize, +} + +impl History { + /// Like a route push: drops anything ahead and ignores revisiting the current entry. + pub fn visit(&mut self, id: String) { + if self.current() == Some(id.as_str()) { + return; + } + if !self.visited.is_empty() { + self.visited.truncate(self.index + 1); + self.index += 1; + } + self.visited.push(id); + } + + pub fn can_back(&self) -> bool { + self.index > 0 + } + + pub fn can_forward(&self) -> bool { + self.index + 1 < self.visited.len() + } + + pub fn back(&mut self) -> Option<&str> { + self.can_back().then(|| self.index -= 1)?; + self.current() + } + + pub fn forward(&mut self) -> Option<&str> { + self.can_forward().then(|| self.index += 1)?; + self.current() + } + + fn current(&self) -> Option<&str> { + self.visited.get(self.index).map(String::as_str) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn walks_back_and_forward_and_a_new_visit_drops_the_forward_entries() { + let mut history = History::default(); + assert!(!history.can_back() && !history.can_forward()); + history.visit("a".into()); + history.visit("a".into()); + history.visit("b".into()); + history.visit("c".into()); + assert_eq!(history.back(), Some("b")); + assert_eq!(history.back(), Some("a")); + assert_eq!(history.back(), None); + assert_eq!(history.forward(), Some("b")); + history.visit("d".into()); + assert!(!history.can_forward()); + assert_eq!(history.back(), Some("b")); + assert_eq!(history.back(), Some("a")); + } +} diff --git a/apps/desktop-gpui/src/main.rs b/apps/desktop-gpui/src/main.rs index b2b06324884fe..829e95bc83e6f 100644 --- a/apps/desktop-gpui/src/main.rs +++ b/apps/desktop-gpui/src/main.rs @@ -5,17 +5,23 @@ mod config; mod connection; mod electron_dpop; mod environments; +mod history; mod projects; +mod providers; mod rpc; mod server; mod shell; mod status; +mod strip; mod ui; mod wire; use assets::AppAssets; use config::{Attach, Config}; -use gpui::{App, AppContext as _, Bounds, WindowBounds, WindowOptions, px, size}; +use gpui::{ + App, AppContext as _, Bounds, KeyBinding, TitlebarOptions, WindowBounds, WindowOptions, point, + px, size, +}; use gpui_component::{Root, Theme, ThemeMode, TitleBar}; use gpui_platform::application; use server::Supervisor; @@ -82,6 +88,14 @@ fn main() { .run(move |cx: &mut App| { gpui_component::init(cx); Theme::change(ThemeMode::Dark, None, cx); + // The web's default `sidebar.toggle` and `navigation.*` bindings. + cx.bind_keys([ + KeyBinding::new("cmd-b", ui::ToggleSidebar, None), + KeyBinding::new("escape", ui::DismissSidebar, Some(ui::OVERLAY_CONTEXT)), + KeyBinding::new("shift-cmd-a", ui::OpenPermissions, None), + KeyBinding::new("cmd-[", ui::GoBack, None), + KeyBinding::new("cmd-]", ui::GoForward, None), + ]); if let Some(supervisor) = supervisor { // The grace period outlasts gpui's quit timeout, so block here instead of awaiting. cx.on_app_quit(move |_| { @@ -90,10 +104,18 @@ fn main() { }) .detach(); } - let bounds = Bounds::centered(None, size(px(960.), px(640.)), cx); + // Electron's `DEFAULT_MAIN_WINDOW_SIZE`; the minimum width is the shipped app's, below + // the sidebar's overlay breakpoint. + let bounds = Bounds::centered(None, size(px(1100.), px(780.)), cx); cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), + window_min_size: Some(size(px(583.), px(620.))), + // Electron's `trafficLightPosition`: centered in the 52px workspace top bar. + titlebar: Some(TitlebarOptions { + traffic_light_position: Some(point(px(16.), px(19.))), + ..TitleBar::title_bar_options() + }), ..TitleBar::window_options() }, |window, cx| { diff --git a/apps/desktop-gpui/src/projects.rs b/apps/desktop-gpui/src/projects.rs index 4d502fd403053..92489def72a81 100644 --- a/apps/desktop-gpui/src/projects.rs +++ b/apps/desktop-gpui/src/projects.rs @@ -93,6 +93,8 @@ pub struct ProjectEntry { /// Any member is pinned. pub pinned: bool, pub scratch: bool, + /// The representative's workspace root, where the composer strip reads the git branch. + pub root: String, /// The repository key shared across environments, when the group has a repository. pub logical_key: Option, /// Other environments that have the same logical project. @@ -406,6 +408,7 @@ pub fn build_entries<'a>( color, pinned: group.winners.iter().any(|p| p.pinned_at.is_some()), scratch: is_scratch(representative), + root: representative.workspace_root.clone(), remote: Vec::new(), }; (entry, activity) @@ -534,6 +537,7 @@ mod tests { color: 0, pinned, scratch: false, + root: String::new(), logical_key: None, remote: Vec::new(), }; diff --git a/apps/desktop-gpui/src/provider-icons/antigravity.png b/apps/desktop-gpui/src/provider-icons/antigravity.png new file mode 100644 index 0000000000000000000000000000000000000000..ecf863511c661ff7edaeefba93d8a88665793231 GIT binary patch literal 12862 zcmV-EGQrJ>P)>?8oT|%v0%}meIj9Xt5T!dtnZ!s0LE!@oL@^pdj6<5(+6wRS@Y82&7-W(splH4JyCd*9u9sp-_I zdbQSG!@vHuPLj2?Rj(v#YpY&s3b2*0H3itp*O~%s<*SvgXx(wa)Awk~@PIUyr%5L} zXtuvuospt<$T;00rQX(LxoX{cH-BEXlBKfB*Xf?MHh{@mxAA{Wo***|^c2X6hLtkShA72w+!J#k&i+n$`VoTw)Blp#>5 ziNeL7RtP&~yB&*~GE~hP&kL0xnWEIrUc>W6FE$0*wC=;#d|g)AT6GHWt&4Ymg_d-t zjLYL2?CfIy#E}UU_jC2zu^nhip`&8LO%>L_o~x1@(Om3!d(Cc=y+#EoR3I@|ap668 z58m(&8xtmO)vPw}7bruiO)04< zI3}A!ahxeb!LcS`3Lg6$0(db$e!jmB^Gwf9m@D&ITLahk;Eb14gdr*p|i-6D~`!p+v z1U&jT0k;SaM;px{d{-|tQ1Tn@rM zhwZf;Y5PzR*h^-p&QK1{p7Lp6v60p(13Z8_wY4k(Mx#;4)!t zoayHg`QZU00z`loE0~(tN7xs@DH1+bWrrk#_uG+&%L-a6$_0Gp^8J3HDa}0gj}E?jeuI%`wJC^YG8_Y@^+uR>FH;KMxeq zwtWL-g3z)3bMXKWu>$tKJ>S4gPziLDn)J-||LbqQBP(F77#FbaiU0o%qmuT7;4L4Q zG(rl8)tXR=*jgfJLPlt&?wk|dL841QumkgrR1oRBxdv#5xdEh#h8CC{%}v;2D*-4i zzC{t(-U1X5zD81lC4p10_`yz^>^10LEV>f=4#S-5dG`1iSpjQBSb$GlbZH#bYp z+i{;W15QRdmWL)jf{P)NV@yJp*z`B5M@px=e`pB%qNC#z9JVO{EQ)ukD zI;c2T0eHlyzd_bO6tN~d20@z2{v`!78nYeEtf?BIoo;uWNNaKjBD+-!mcWaH%sFkjND!e1YhryYKJv zt<%2z^_ygh*VJpM8R>WDJTS}Lnl-jvS!~CqK^fQ96rmOD>T&ViI-3X8cDr}Ia{Xk=MLTnd74rhnL_5JE9OV8S7@ z2aZ=Dx8k6%`FyYIn@uohfN;<@Jc2*?dW4X$3wFOn7D1rj!Ty4iqpD~J!k8x@J=yb4 ziW<>i;bS5KGzisWPoXU-G$`h;p75mm9{+NYTmM+5bWODajN9f72R#_NE1h4mm*MXZO7*fm?)tvWLW^fAd^&Xh02tmMD}Aq+8Oy{OHF7Q z6WFc!9H7AsPg=~jK-?gim_pclJKOH~8ZE&g^(;deR0oEdtSk~C zrE3tiTSm}A#VqnqqoMEP`z`OH+-38vNpjMB_+*pIxv&~V%D$=&V2mpNOedko6BGy<>6U5;Jd)u`@4 z4^ZSmTnIrkTuWv+kO!nnFAig13NbKow#e}caen|nq=Vp75ECX7IB^=0}8@{n8|Ek-!v&Z7?6Vf%~!>JFK*HI)i*!PPH6pzHK$$hDOfIe{Y7 z;y)+ZJWRO=*+ChU`_AG)+l_pUYS7UF;66YhWp<9BuP_M|l<-(QGF<>nY`fdD;(}Un z2fqSe-&sc3&;%7T18tg+#_&2~|LUJ!8B0*^Py)CY7^=)5rXAOHziy{~uT0sRN(Hds z?6j!#4yt^`#7|i zYk=P`sPdU+nEb5L#PC8vBt_cSB4a>NnNQFcAl7FE5=u3;4{lL-fDSZ;mH-I~K0GC0 z4;lLLTA4xxn7SbFyi1RNR2kOqao1;cP$%QJY#96F7S^D4WPdWuW>FY%0V0E**=w&+ zB~a-@i5JIoh~P=-sErkQ2E_F2{+L0WZBr9|A*3U%DMgFpP5n%!YFilD08|f_gYYk3 zM~qOMV%k;~i5L(%6A|Z~3l!~(z2xy1Uvjxj(V997P-fCAyIi?T!Q?K5f+oBw!cC?0 zwIFE0E1L_zhJ=$YPzpxg>Iu2$2do7UuLnw>oc_h)oxQ)!?nEF&Hyo7#CoPQSd3*z` zMWS}eAQuK43FX8f8z77v5M(gi0J&&*gqCmwk^@)>WRwYRV5$^g>X1M))hA@Kc@T09 z%@rF0$Y>jQe{12^K#-h`yMcB@0~;~K&;Jtlt3~OC@Y!)A8Cw3HtRB?LtT}?Q#qP6p zXjR`C3m@z?;h&+4h7;IxeBGWOH3NI!pyJPJ2^G&7I%rv?eXjkDELO<&ydjc?I1do> z17)=5TA+xm6BNAPz7uK+)lo<&%2Br+e)xKsqBT_(;Ec;ocvfd6-%4l~z7s_~%dCOd zb8jgFF3@)o)&;BAw=??r2+Jyy^u$a7#a@<%nIoc(&@zNWPG7M95oUP$)?vpMz_|is zhaM#-I3QDt>Yz>snn9K!`9Sa~C?sp(QqB@d>NKPlz!PfD871e$=k1HnlwmsBKJtIa zl&q;z0NXLILf)UfcmrWF*{U@CW)UoJE1n86T>}$0B7ndG zw2a`(8QWRbFfynKRB)Jg2)OubNFv(m3=f1JXd?lIY{24QHcOKg`Tu}@1(|t2R%ArxsM zZb3*0!6TV01|>3cfGI#+V7s2_Vs>%@PeMllH7xsm?TA1>0++ZC7IU$km&u3ph=b7$TJcpbqf_+qDoc zAX}CZ3{*(f!!$EDCd&pLBlbD8J0|GDZwGLV}2Y=N2^b;owL+>HA~~*7R5aO~-WR&za_l5bcA1XQ`D+fMz{l0!Z#4%)Val zmZ6H*^I0J32*24TC5UTsXs9P(@sR69d_kgMgQ53qLgG8L@!r zLJMqJ2tR6=cqFcX=_n{by-;z6%ilM&yLG;c9ZGp%lB(v>ok+|(w5;-jfunn6mSs=E6 zn89#91yIy7O!P3qb4q|go%2L+KlMN`fCMNe(#Fi#0fmBM!XFR{W=Rn}2fzi$CA9dp zj!_gc;g69cXnO7~#~i=&o*(DHZ-(Euo56nHl`mUop2$PSn zpHK{Usb>K8qXTn=f}jqE%?V2YxCb;(gXW+u)zUq1`sf_kDGIbsxtn1aSk}#?de#hK znxHMn6OS#k`VZxtabbqgpd45W>(v?bQr3r2JFxEqcK}>~At1n`4Bf;kvUc2nU=18MX^+{27ST?18@jXG-{PQUxz3l znX(RO5mE@;)58|lfIVxbfEnQ}2Mj2|?UD|YH3jfxK+n;FK5KD&000mGNkl7J*6Bv&H(Cr*zPu;nd_0w8&i2#&amaRK{ykSZeS>k3mt+x?9?Fqe2H(8^$y@y^7$ z(a^(pGc0+O69YPsz8WA6MGO(PN1&o+bv`hne*ioXa^|FwmTf|v*%W zH24VTv>em|MiF)@!=mH^zJ1J_*FEWD=iMjExRzZ3PQ3J#-Oc1b#qf|3V<$^ww>G7I1Ab&@#Y)N`2s;-oFIhFBG1(vC(E3lRp$ zKF2`|L*!crg&$6m@{ zUBDG6GX=7Rc=DE{Ubc$R3% z6@vNB5&&P2mB5C~4lnVUoq)*M`oz~C+(mv~&= z&x`LxmWDaTOT111um=PY>{+IRd<{^aDMe_15_JJn5=#YVShzJKjnCpS_BrOgQ_7YLblBbC0GK$(btvOz zpvdC!T1^8BE5I#i`QEYeb>VXjUYn85ZjtT%@IE`Ho!S3W zuYKRLaRipl0=)XuXYFPF{&5WQeCJoBPxP|10?7FjAy@!Gh5Q=q;DHOga0(cUMr7t? z=LjU9?&Sre>`mb$YZXrWuuaQA&~O>BHsL5577+mkpzk~e#zkc@ibS|U;s76!D%B44 z#ROb{KFQgjEpZD>olu8}Nn`g(699;uRGOK=w6rLQ2C_Mc@e5^{vVmn&fN^OKE5Y<3 zz{6di{1z=;ECg9R&@t~Px`&-#IGCvk>UDC;I9MEP6h(26bpiL)!VnKZpL#QABJm6D z-l*sSn9F1fBMU%|&+;9-wW%8dPvq_>7@+ga%D^VH%!*MapkS}30rQCL{C_iAgHG-J z*?Y_&?CO9KaR6$XijOJfe4Cg%mzFIZTs8$TQ~zRNxp%;#jr;pZ`$E{P0u}~_LJ~KJ zJ+_2i!S(@vTcUJ~T%WDnpa`Q1$iiO3X^&!=1Alm;MokTEV9f`_5$6C8oHbx^L(Z&3 z*(W8E1V}5Y5>q0DrRRqEy^ksYKOG814Q#|GPx9o+6_MYKgNlG#V5*5%9dXN!Q>r=Oi4 z>;g~{{ENEV)-o4*dGFw@4C=No&sQY!ns{QOt{ohX%`c8A zaL6A^1LPmJ-ggY>hc1D22+RkHIg!%a0ThZMWE6rLx+DckIJEm{V>eJikx}^Q2yZq8 z0OZ97^Z#vju#vV$X)>&AfH+|5PypM#2ZM3%xITo|@-}?8gE|ffai4k21=H!No>R5$ z*UZ5IsGd?4^1d3*@dKojz!J(X?~>RJ;KV#)ejXP@7f&MK>ri1uGe_5XS;ZOq5xji1>00$2Y%fOX|%FuW}4&#J@#QS2BbL%BpT8ai>Qew@o zgCB0y1(-9kVdZOMOYu61)RL(n z0V&`MWDwx>G3e;YQpOfk^cjr1VVY^FVo=S^fwU+-&Fp`{@BYP%Eb&@$3vl?yKe)R^ z-cO+4X2jovJ?a8w7Y2Uh#@oQqhwB}|Q~-7Vt2&$uk z8s?w)J=kN30U<0$lWMpb0{HafLJFx*lN{K6l$c?+AiRbrD14Jo8%I#m5^}=l@T(Al zQd%DbEsXj>M=}K-vf$nM#0yxfCeIjhv9Eo(OJxbyk}H7a{x6C^j;2)t=_AiS%E2|< z0?8aXcD&QV_~SwNj`*p&hCUNWZFCT~H2nz#^kwVt04}!=X8D70X72X5WClSy5GBCxqu_-;qRc*q${^7+2X%qB%Mz|7O#=`8iwkDDJ9qqn{pd6O zj`zTiyS)TB&WeZUhKnJqkT9Y800PDpDD+4W&e=IE&QeE%Yv=vwh$2W?_6Py~h6>Gy#)?X1 zTUderDND4LQ~@krI+)h7xGxv__h9!B+GVO2QGMk6Wagwf25=uYU|$l)3A9g~_)+Sd z=_gm9`sBVKh`>$rdG9Jn;uB@)<(W=uPZ|wg4=jPQ@}IqSNH(A_9w2R$kP&;%iT9CJ zKny_$8?g5fp(HAk-fTj~FlJi-q7r~a96^_PF^Z;XR<8nB`gaK1Z(#DP#%}HzJdKxe zo!{@~90JN-|My^#7NGhN*c5CVV)~?W*M3rv@~Tk&fWVIrbYuut{hwTXz?ej+$EiOW zeS{hl=Skp>LzG-aMtTkqH)M`6pyR|b%n)E#GFZhZ^V;jU^q4CrCs?&qhq&N?1mD=w zKs2EwnkFQgda(Eu`zlMYmMjK1;KP6RxT?di`9w_zeJI2}3n2X7Ru9jc*uAmiSv7N} zP_4Zy2zNlzU-I89yYStjr)UwAFyHvK!`k;58Z-b`{~);NRzukv;4wi8C16+y-;XU$ zkRTDEKm{;Vm-&e^6SA`oSWwdH*C|8BPt_%yocg%I%UdjNCx?=0EiC0c=+w>6T9V7s zEtv%{famc3(heHw+(dxf?Htt5#zG9KzI&fn6Yah^aHUd~FnTQlHB8 z0ipCTZ!i2B-N0n|aQs*D>%B;!ufS zktJG7;sW-#=%V5F_iq0K^O8fFehL*;zog!073O^65*Ql<{hJ(mcLU<7T+qAvSpdQP zWA}yI`07C)H+x*&!-u@~QG$C|XL0Mq<2q+JpnQKAR>VI^`xaF7T#}FD`UKvXMl8`& zL!P=s2wO=&LomUr2MgdeE@kNrC~e$&heMHdS)a0b^S`-M7I`g+1=w!(kq4S^hV=6Q zdb+^x)gfHs`njFM_2C(dmw$WVi_Qg#y{q6VJ}=hGy}e>kgnpGELb84=!CO!L5WU}N z`uBGx@oViFG--OZxVIh=I)rzK_kmEznFM{&$tTqM#0sBgX81z3zcH;vBYyp9O=H$6 z+!~Cb4zLss3)7->q>gb|HR=v}ZzBiG609YO0j%VGhzEDA3zVf=j|FP(Q@GRzdfW?r z>gI{N_G8{%5qhdu@c%tnjIAA0LrLDK-v4cnuQQo`-k!SLe)_Ks1~cEf@;~bRTC$$= z?)oG<)e{Y``z3qtf#&1)x8!1bhY@RBBuaup0_k(^2Zz@H(QDMxz7&{5_B@t>L=!zn z#hrzB9Wy9NVIxWpE7Q6d&RIeNP`f z$3F8!6XnY_qDIdVys}I}7|O#)D4M7zh=&zyI*Q4o$Wq4`A17>DWGI|{{I!xVQ2NW2 zWuI+OWl2~2a}^000mGNklSyn^q2vU$sf;klwS3l5@O zzDfVD-TeCVP8(m?H0dKXmz|Rr2-rl<4kV{4Gg=5Q#VZvyk#V9CkyXm%iJ*8LQ#&@Z zJGTX!gy|vAoy>L0qAI|m2Z81{597s~y>2hgw^V2Kp!lQ1G68#vWA@GlN)GWBhgkf& z0c|jAIsGGQ2~dRQYxbiaxMjmO=k(WkT~rok0j?s|4=_0u%$C1Otoh#tq3unRt) zDrtXBsf<1abp>ZfQc96CB$SImGQp5jMZY5nCEC7N7I`hI3)u6&UA2>iNni0D-10g+ zsZIhtVC>bg0inSC+MI3d?mz z4wwMSo->F&J(QrP1Z73~c?3E~XrCa0P>;Ykc~Rn~gkwycYqIC>Z`k2GvPf%DEr9*w z4<|^9hK~}Sv3kjt?bk&oHG!TFFjw%I%ke9HyF>seQAJ}g_E&eUn>lhhgpc)wQwG0h zu-#jIA`MG`-fKu8$x}y8G@+>dVxtn5o@JEC-Uqh;5gw901k2FTdnTDxUDQj_E~)}p zq<>U|NY1n&S~g@`eGSMYkUp8DfpO(6LhaJb1%ZA^9<4okXTnd98XozTH|ne`wq)HXW5tg54=tc8`NSvr8+F-q>_Ht8ROrQ`o=c(BpBDBZ+s+HT)5g1#db8i z+xt)5X;B7CE}8|XIyh3i9ReJG=iXE=<2omRxqhuWX8|U8e@dR>Www9E88s-D`opFf z9yLwE$J+U+?MC~`*VNJzOPvNTenDw>4dS;oCKR%jP1MajdxwYu@&cGnMs-yf&8&Z! zEYeyu1+a21%zq8@?a_RhSMm*tH-#RtKp#!l9v=u;HqgT`{>>||eG37bsahX9R&#q_ zbEJVn->pF_;oLz+hrGh1mS7`uB$4_&VTt~PTpbGol9}9ceYu7%Jkd-X7r-uiw6N%Z9&Xj^e8iHFaRQGoE#tTNv<3v;A5*TJm-Wn?|Hl|!de^)pxu#?x7R8? zsl!b@2Xn=J=kSGZuT78JOJB{4>j6pBbg#eRzHQEwDPLD^)Gyc#MsR4*zYt0cLplD^bYszu5S&OWI^~3wp+wI@-_mfUS=Mn^nRka^0lf(S;qU6D8 zWF{anDJ`c^2wnnlQQW|SE5NQ7-tYu-e81>0Eljv{Yg}&}X`&O$B$T9&6a0C)1w{*+ zKIfQo?|p(S@LF&M(5`)@vv=BBdJMn|>rw3S{#>WcIXo;np>R1ngWb7(W+2yoapQ0* zCTl;Kb;U;gk_qw*=}E~V94;Zm0BGg|GIr#;G(?qY;%byPJP?#0P#qzd>7893U#( z&wWtqLPoh5<7Wyp1#$O~yqc8Ri0bta%PdZC=yc)Mf-1o7JACOyR?T{>3p48a9@erE zwCDKG)$V<%kKB>oO^8MpzslKPJY}1&$O>JbHmssa@*3`Jb?Uf8iWqW?hDG=JejyBw zK~avFPM#rEpHPfAVdtaH=@yhPxUd#L+gEu|htjC?;eEjJ{ODetKNUgWe8Ich*FGTp z#tj>0-YF}5U3Rj5-hPaW90pX5rYED&S?p0WDC$Jmo{;XIftJB*VFXIOqFRoZ1zZcJ z08hK{D?2JipC%vL?88=4q`cnZc@rl zJ*8wazar8br=k<}#1M%ZolY4X5;m8kJO0ok&aXSk0<8s8fOcGeQ`l+i*WB(e-|CH< zG1xIXqUZm>2>3$eUMlnZrX<(gu(ADwtde!*+w>m|eMdBzFM88U()d8fc#o664SYB8A#~@OL=GCmR3C5 z^@|-=_m6}I0tV&;{0JP`pGYpG`USOAc0OW(g24q*0IO{Lj{l3i!pB|S2Yr(`z!g>} zyI&`*U5ptLd;aeorsh;xrR&MNr1#m*_-d~Np6^RfVfSfe5f89Hq)rN{5daomDMO=u zOiq*qSPxqPcKiLW>}O_v?@7llOT0~hKrsVgUf%Cx+%#ILbV8_$sPXJ;Pu69>lGb7^ zR1EEBInm?1*zpv8BtC8lNQo%DLImNNlkSoe$h-+f4t(huqi4y(To2y@bm>i#s-1d6 zM{oAMt9D*RFCyI3bN=U$0Nne|J?q=^WR0_=MJSD#}${<-w;f$vM0_QXWD&+q#fU{2uv|2gF_zxS4D zBY7;=8drIX1a~>;Z`yY)5+vf!<|&|G^2jj)4-DtIic$|bR)2)`Py{ zqt_hmYw9Gcm;Ux&C_&s>81UCOZEQa!Tgkfa6#bg*^52{6aB)-6U$(N8ez1u*kqc*w zX#hhSFnM5R$uNe8u^zGlJpG(+>}}s*-{ezuz2^^$L?kkP-V zh{e%C`cYbe>H(tD%X#5*DSk zkh;Sf+<5(l;RUjlt}CrhU{e1u?)N6WOxg)O2GAZ*t$nf-#Z#Y3#nDo zIU+suX@njj_6;Ys%MXbz_n;MEw{yRFtbLDX&PVv%vW3_2qn|17CPn_n^Co9+U#?_JP~C zGeMsueYuBjyy>+zoY|zackYdWZp_02ZoJ_~?b))nxUN1$|I&8yf1emkbd31r(B?8_ z@!MScVp3oTJkBkGp-gA)b8cNH4`Mwi1xR=O=u}gH?ffE18sibdr<3uJoYRy%CeYgN z_8tcA$E~!#u~G=0OSJ|nZ?^RA+r6}rES125O8>j{SpW}^-aTmcA_9VkooBl8punLA ztN^>8{jEdH)E};K3HP9PiGShV1%yKMwNCX~K>Um1*!rfMEf1Ep^<(Rb4SH8f`YLCS z&*g;uc)Q+u)^MSdW>Ze3b!L%c1oDOh-!neofqw1-Qh;60{`<#hYBoF47qj~C;l(-r zA>2A2+3Wq4_OrKKcRzLGDZ@XKwe=I%RVV8e_Pf2!`vk%v1Yvj+1Garxwq_pjOOz4m3>-rOxPXv3DFjXX~fZ-S>eSQ3LUtTve_-$ER zKXYAkidr;ZKi!Y7t35anMzsd0R--;pLI^Aqr+|v9?$p%$2M^@){#6Bd#(DpEvI%wX zfOmt_uTJaGKL2m}+&(VRsu9Ho<)`R9waLf*q{&=s>;CJWSv}5vxo`HDE2)7Ifi)sM z4D3+A#I?Ew=q&+fCm(viS@})>+GqVt1$f5Uw;gGpaxBt3_xS0@%{+sWW^$^2@Ooqb z>f|uHP5Rp_sj*|Z*R7lNZY%a3Z9m_AeMrcg6b-aGmm2!zt6d&BI+3z#1X}MmKL7Ob z&zv{-Qx)JBF8Kbl45vNC=hk^}=d<|Yk}V#A@Yu;v=00@kxlHc&-+ukZ_S3Sq9@M(} zB)!Rg##dHn)AG5>>E%utkFrWUFJWyZ(n6LA6mtqoYA!tBw7SPn`<$Ps0DGPFy&db= zkfrL^*ih*s{=SFbnfr66=m8-gsw*-X(f%hE^gSS7w6^ZQuHC3sA=NiX2-roe^EPA9 zgJgmj$F&KAg6*@9*W06yz2MyX^q=$@_fvqUU+@nz<5bSFoxi;gcSw_fF9r02JfCCH zCg$G1>>E5|&fEMnPa^Us%}cJod;Q?m zYhcg9tgAQbN%pJ$`MfGx6pbcStO-W|=0EqT0v=+Il2}?Flk?2S_B-R=C(l3kk14?J z=YH?CcAgjX{rYny-0<^#dy4IR4oP%WB2qq|h-KgGmPYS7c57kxEY&JMXYoL-mrNja z4MMSm|L^?+uXXEFYMgHvWf->*k1e{*AHU$d`l$ZA69st2dEb3Ws`c2E-G3_^y5@mC zwFhknb&@Lgx|4a%X^K}RpL*FI-f zQpqUMJ=e^zOC;W*BkTEA9WYW%V{xcqVQ;r^Uvn;H=Rytf;3uUU%v#EqMVtFD`8 zn%BQOz5XYYF@)$-@r4(!h`7&(@>I{ua`mfT`Xx)*ZUD%jPy`u@0005cNklJKi~34{}Dy?On5ywaY-d{>l|UwYFiGar?;HI?hSQ}veXHt7pZ z;;%Q)eH(MS;`3R01kpQ={n|#V(|!8uR|}WGq>L>$D6dDZTZ2I?5U?3u)jT;j?DyIZMofhuGv6qF}I$1dVO?F zqn%65_uRU1{kLRot@@?1wzlf^@Y}=J){0(h3b2*0H3itp*FOUQ0RR8b*Joh>000I_ cL_t&o0E1-Tg`Fb5H~;_u07*qoM6N<$f|an!jQ{`u literal 0 HcmV?d00001 diff --git a/apps/desktop-gpui/src/provider-icons/claudeAgent.svg b/apps/desktop-gpui/src/provider-icons/claudeAgent.svg new file mode 100644 index 0000000000000..9f5d73fe692da --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/claudeAgent.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/codex-dark.svg b/apps/desktop-gpui/src/provider-icons/codex-dark.svg new file mode 100644 index 0000000000000..15af54d3d91f4 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/codex-dark.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/codex-light.svg b/apps/desktop-gpui/src/provider-icons/codex-light.svg new file mode 100644 index 0000000000000..55367e40c87e0 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/codex-light.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/cursor-dark.svg b/apps/desktop-gpui/src/provider-icons/cursor-dark.svg new file mode 100644 index 0000000000000..642d079966d47 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/cursor-dark.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/cursor-light.svg b/apps/desktop-gpui/src/provider-icons/cursor-light.svg new file mode 100644 index 0000000000000..e6d2301337fa2 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/cursor-light.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/fx-dark.svg b/apps/desktop-gpui/src/provider-icons/fx-dark.svg new file mode 100644 index 0000000000000..55715d23b3f69 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/fx-dark.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/fx-light.svg b/apps/desktop-gpui/src/provider-icons/fx-light.svg new file mode 100644 index 0000000000000..d30d76b2866ed --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/fx-light.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/grok-dark.svg b/apps/desktop-gpui/src/provider-icons/grok-dark.svg new file mode 100644 index 0000000000000..5fa42ea2ba6c7 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/grok-dark.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/grok-light.svg b/apps/desktop-gpui/src/provider-icons/grok-light.svg new file mode 100644 index 0000000000000..3dda90aedebe5 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/grok-light.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/kiro.svg b/apps/desktop-gpui/src/provider-icons/kiro.svg new file mode 100644 index 0000000000000..227e8bb1df28a --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/kiro.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/muse.svg b/apps/desktop-gpui/src/provider-icons/muse.svg new file mode 100644 index 0000000000000..35b60189260a1 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/muse.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/opencode-dark.svg b/apps/desktop-gpui/src/provider-icons/opencode-dark.svg new file mode 100644 index 0000000000000..6eb221fcd7b97 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/opencode-dark.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/opencode-light.svg b/apps/desktop-gpui/src/provider-icons/opencode-light.svg new file mode 100644 index 0000000000000..7a5be4fb09ec2 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/opencode-light.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/pi-dark.svg b/apps/desktop-gpui/src/provider-icons/pi-dark.svg new file mode 100644 index 0000000000000..eaed20e719239 --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/pi-dark.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/provider-icons/pi-light.svg b/apps/desktop-gpui/src/provider-icons/pi-light.svg new file mode 100644 index 0000000000000..2d0c22f72488b --- /dev/null +++ b/apps/desktop-gpui/src/provider-icons/pi-light.svg @@ -0,0 +1 @@ + diff --git a/apps/desktop-gpui/src/providers.rs b/apps/desktop-gpui/src/providers.rs new file mode 100644 index 0000000000000..ab0c329ae4ed4 --- /dev/null +++ b/apps/desktop-gpui/src/providers.rs @@ -0,0 +1,1251 @@ +//! What the composer's model picker trigger shows: the provider instances and the default model +//! selection of a fresh draft, ported from the web (`providerInstances.ts`, `ChatView.tsx`, +//! `ChatView.logic.ts`, `ComposerModelEffort.tsx`). Decoded by hand from `server.getConfig`, +//! which the generated wire types do not cover. + +use crate::strip::Defaults; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Deserializer}; +use serde_json::Value; +use std::collections::HashMap; + +const DEFAULT_ENABLED_OFF_DRIVERS: &[&str] = &[ + "cursor", + "grok", + "fx", + "kiro", + "muse", + "pi", + "opencode", + "antigravity", +]; + +const REASONING_IDS: &[&str] = &[ + "reasoningEffort", + "effort", + "reasoning", + "variant", + "reasoning_effort", + "thinking_level", + "thinking", +]; + +/// Variant words that name a tier of a family, not a model (`modelShortName.ts`). +const VARIANT_WORDS: &[&str] = &[ + "pro", + "flash", + "mini", + "max", + "lite", + "nano", + "ultra", + "plus", + "fast", + "turbo", + "preview", + "latest", + "thinking", + "reasoning", + "build", + "code", + "image", + "vision", + "audio", + "live", + "custom", + "tools", + "exp", + "experimental", + "chat", + "tts", + "stt", + "non", + "transcribe", +]; + +const FAMILY_PREFIXES: &[&str] = &["Claude ", "GPT-", "GPT ", "Gemini ", "Muse ", "Grok "]; + +fn lenient<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + T: DeserializeOwned, +{ + let items = Vec::::deserialize(deserializer)?; + Ok(items + .into_iter() + .filter_map(|item| serde_json::from_value(item).ok()) + .collect()) +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct Choice { + id: String, + label: String, + #[serde(default)] + is_default: bool, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +#[serde( + tag = "type", + rename_all = "lowercase", + rename_all_fields = "camelCase" +)] +enum Descriptor { + Select { + id: String, + #[serde(default)] + options: Vec, + #[serde(default)] + current_value: Option, + #[serde(default)] + role: Option, + }, + Boolean { + id: String, + label: String, + #[serde(default)] + current_value: Option, + }, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct Capabilities { + #[serde(default, deserialize_with = "lenient")] + option_descriptors: Vec, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct Model { + slug: String, + name: String, + #[serde(default)] + short_name: Option, + #[serde(default)] + sub_provider: Option, + #[serde(default)] + aliases: Vec, + #[serde(default)] + is_custom: bool, + #[serde(default)] + is_default: bool, + #[serde(default)] + capabilities: Option, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct Provider { + instance_id: String, + driver: String, + #[serde(default)] + display_name: Option, + #[serde(default)] + accent_color: Option, + status: String, + #[serde(default)] + availability: Option, + #[serde(default, deserialize_with = "lenient")] + models: Vec, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)] +struct InstanceConfig { + #[serde(default)] + driver: Option, + #[serde(default)] + enabled: Option, + #[serde(default)] + config: Value, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)] +struct InstanceOverride { + #[serde(default)] + enabled: Option, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct ProjectOverride { + #[serde(default)] + provider_instances: HashMap, + #[serde(default)] + default_model_selection: Option, + #[serde(default)] + default_runtime_mode: Option, + #[serde(default)] + default_thread_env_mode: Option, + #[serde(default)] + new_worktrees_start_from_origin: Option, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct Selection { + #[serde(default)] + instance_id: Option, + #[serde(default)] + provider: Option, + model: String, + #[serde(default)] + options: Value, +} + +impl Selection { + fn instance(&self) -> Option<&str> { + self.instance_id.as_deref().or(self.provider.as_deref()) + } + + /// Option values by id, from the array form or the legacy object form. + fn option(&self, id: &str) -> Option { + let text = |value: &Value| match value { + Value::String(text) => Some(text.clone()), + Value::Bool(flag) => Some(flag.to_string()), + _ => None, + }; + match &self.options { + Value::Array(items) => items + .iter() + .find(|item| item["id"] == id) + .and_then(|item| text(&item["value"])), + Value::Object(map) => map.get(id).and_then(text), + _ => None, + } + } +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct Settings { + #[serde(default)] + provider_instances: HashMap, + #[serde(default)] + project_settings_overrides: HashMap, + #[serde(default)] + default_model_selection: Option, + #[serde(default)] + default_runtime_mode: Option, + #[serde(default)] + default_thread_env_mode: Option, + #[serde(default)] + new_worktrees_start_from_origin: Option, +} + +#[derive(Deserialize)] +struct Config { + #[serde(default, deserialize_with = "lenient")] + providers: Vec, + #[serde(default)] + settings: Settings, +} + +/// The provider snapshots and settings of one environment. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Catalog { + providers: Vec, + settings: Settings, +} + +/// The two-letter instance chip at the icon's bottom-right. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Badge { + pub initials: String, + /// Background and foreground as `0xRRGGBB`; `None` uses the card style. + pub accent: Option<(u32, u32)>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Trigger { + pub driver: String, + pub display_name: String, + pub badge: Option, + pub model: String, + pub effort: Option, + /// The model's full name, for the tooltip. + pub title: String, +} + +struct Entry<'a> { + provider: &'a Provider, + enabled: bool, +} + +impl Entry<'_> { + fn available(&self) -> bool { + self.provider.availability.as_deref() != Some("unavailable") + } +} + +impl Catalog { + pub fn decode(value: Value) -> Result { + let Config { + providers, + settings, + } = serde_json::from_value(value).map_err(|error| error.to_string())?; + Ok(Self { + providers, + settings, + }) + } + + /// The workspace and permission defaults of a fresh draft in `project`: its override, then the server settings. + pub fn defaults(&self, project: Option<&str>) -> Defaults { + let overrides = project.and_then(|id| self.settings.project_settings_overrides.get(id)); + let settings = &self.settings; + Defaults::resolve( + overrides + .and_then(|o| o.default_thread_env_mode.as_deref()) + .or(settings.default_thread_env_mode.as_deref()), + overrides + .and_then(|o| o.default_runtime_mode.as_deref()) + .or(settings.default_runtime_mode.as_deref()), + overrides + .and_then(|o| o.new_worktrees_start_from_origin) + .or(settings.new_worktrees_start_from_origin), + ) + } + + /// The driver and display name of a provider instance, for the sidebar's provider icon. + pub fn instance(&self, instance_id: &str) -> Option<(&str, String)> { + let provider = self + .providers + .iter() + .find(|provider| provider.instance_id == instance_id)?; + Some((&provider.driver, display_name(provider))) + } + + /// The trigger a fresh draft in `project` shows; `None` when no provider is selectable. + pub fn trigger(&self, project: Option<&str>) -> Option { + let entries = self.entries(project); + let (entry, selection) = self.default_entry(&entries, project)?; + let provider = entry.provider; + let requested = selection + .filter(|selection| selection.instance() == Some(provider.instance_id.as_str())); + let slug = match requested { + Some(selection) => resolve_model(&provider.models, &selection.model) + .map(|model| model.slug.clone()) + .unwrap_or_else(|| default_model(provider)), + None => default_model(provider), + }; + let shown = provider + .models + .iter() + .find(|model| model.slug == slug) + .or_else(|| match provider.driver.as_str() { + "opencode" | "antigravity" => None, + _ => provider.models.first(), + }); + let display_name = display_name(provider); + let (model, title, effort) = match shown { + Some(model) => ( + trigger_model_name(model, &provider.driver), + display_model_name(&model.name, model.sub_provider.as_deref()), + effort_word(model, &provider.driver, requested), + ), + None => { + let label = if slug.is_empty() { + "Choose model".to_owned() + } else { + slug + }; + (label.clone(), label, None) + } + }; + Some(Trigger { + driver: provider.driver.clone(), + badge: show_badge(&entries, entry).then(|| badge(provider, &display_name)), + display_name, + model, + effort, + title, + }) + } + + /// Provider instances in the order the web lists them: grouped by driver in first-seen + /// order, each group's default instance first. + fn entries(&self, project: Option<&str>) -> Vec> { + let mut drivers: Vec<&str> = Vec::new(); + for provider in &self.providers { + if !drivers.contains(&provider.driver.as_str()) { + drivers.push(&provider.driver); + } + } + drivers + .into_iter() + .flat_map(|driver| { + let group = || self.providers.iter().filter(move |p| p.driver == driver); + group() + .filter(move |p| p.instance_id == driver) + .chain(group().filter(move |p| p.instance_id != driver)) + }) + .map(|provider| Entry { + provider, + enabled: self.instance_enabled(project, provider), + }) + .collect() + } + + fn instance_enabled(&self, project: Option<&str>, provider: &Provider) -> bool { + let id = &provider.instance_id; + let overridden = project + .and_then(|project| self.settings.project_settings_overrides.get(project)) + .and_then(|overrides| overrides.provider_instances.get(id)) + .and_then(|instance| instance.enabled); + if let Some(enabled) = overridden { + return enabled; + } + match self.settings.provider_instances.get(id) { + Some(config) => { + let driver = config.driver.as_deref().unwrap_or(&provider.driver); + config_enabled(config, driver) + } + None => id == "codex" || id == "claudeAgent", + } + } + + /// The entry a fresh draft starts on, with the stored selection that chose it. + fn default_entry<'a>( + &'a self, + entries: &'a [Entry<'a>], + project: Option<&str>, + ) -> Option<(&'a Entry<'a>, Option<&'a Selection>)> { + let project_selection = project + .and_then(|project| self.settings.project_settings_overrides.get(project)) + .and_then(|overrides| overrides.default_model_selection.as_ref()) + .filter(|selection| { + entries.iter().any(|entry| { + Some(entry.provider.instance_id.as_str()) == selection.instance() + && entry.enabled + }) + }); + let stored = project_selection.or(self.settings.default_model_selection.as_ref()); + if let Some(selection) = stored { + return selectable(entries, selection.instance()).map(|entry| (entry, Some(selection))); + } + let first_driver = entries.first().map(|entry| entry.provider.driver.as_str()); + let group: Vec<&Entry> = entries + .iter() + .filter(|entry| Some(entry.provider.driver.as_str()) == first_driver) + .collect(); + first_selectable(group.iter().copied()) + .or_else(|| first_selectable(entries.iter())) + .map(|entry| (entry, None)) + } +} + +fn config_enabled(config: &InstanceConfig, driver: &str) -> bool { + let flag = config.config["enabled"].as_bool(); + if config.enabled == Some(false) || flag == Some(false) { + return false; + } + config + .enabled + .or(flag) + .unwrap_or(!DEFAULT_ENABLED_OFF_DRIVERS.contains(&driver)) +} + +fn first_selectable<'a>( + entries: impl Iterator> + Clone, +) -> Option<&'a Entry<'a>> { + let usable = |entry: &&Entry| entry.enabled && entry.available(); + entries + .clone() + .find(|entry| usable(entry) && entry.provider.status == "ready") + .or_else(|| { + entries + .clone() + .find(|entry| usable(entry) && entry.provider.status != "error") + }) +} + +/// The requested instance when it can take a turn, else the first one that can. +fn selectable<'a>(entries: &'a [Entry<'a>], requested: Option<&str>) -> Option<&'a Entry<'a>> { + entries + .iter() + .find(|entry| { + Some(entry.provider.instance_id.as_str()) == requested + && entry.enabled + && entry.available() + }) + .or_else(|| first_selectable(entries.iter())) +} + +/// Slug, then name, then alias, each ignoring case; else the default model, else the first. +fn resolve_model<'a>(models: &'a [Model], requested: &str) -> Option<&'a Model> { + let requested = requested.trim(); + models + .iter() + .find(|model| model.slug == requested) + .or_else(|| { + models + .iter() + .find(|model| model.name.eq_ignore_ascii_case(requested)) + }) + .or_else(|| { + models.iter().find(|model| { + model + .aliases + .iter() + .any(|alias| alias.eq_ignore_ascii_case(requested)) + }) + }) + .or_else(|| models.iter().find(|model| model.is_default)) + .or_else(|| models.first()) +} + +fn default_model(provider: &Provider) -> String { + let models = &provider.models; + models + .iter() + .find(|model| model.is_default && !model.is_custom) + .or_else(|| models.iter().find(|model| !model.is_custom)) + .or_else(|| models.first()) + .map(|model| model.slug.clone()) + .unwrap_or_else(|| { + match provider.driver.as_str() { + "claudeAgent" => "claude-opus-5-5", + "cursor" => "auto", + "grok" => "grok-build", + "fx" | "kiro" | "acpRegistry" | "pi" => "default", + "opencode" => "openai/gpt-5", + _ => "", + } + .to_owned() + }) +} + +fn kind_label(driver: &str) -> String { + match driver { + "antigravity" => "Antigravity", + "codex" => "Codex", + "claudeAgent" => "Claude", + "cursor" => "Cursor", + "grok" => "Grok", + "fx" => "fx", + "kiro" => "Kiro", + "muse" => "Muse Code", + "acpRegistry" => "ACP Registry", + "pi" => "Pi", + "opencode" => "OpenCode", + other => return humanize(other), + } + .to_owned() +} + +fn humanize(slug: &str) -> String { + let mut spaced = String::new(); + let mut previous = ' '; + for ch in slug.chars() { + if previous.is_ascii_lowercase() && ch.is_ascii_uppercase() { + spaced.push(' '); + } + spaced.push(if matches!(ch, '_' | '-') { ' ' } else { ch }); + previous = ch; + } + let mut out = String::new(); + let mut word_start = true; + for ch in spaced + .split_whitespace() + .collect::>() + .join(" ") + .chars() + { + out.extend(if word_start { + ch.to_uppercase().collect::>() + } else { + vec![ch] + }); + word_start = !ch.is_alphanumeric(); + } + out +} + +fn display_name(provider: &Provider) -> String { + let named = provider.display_name.as_deref().map(str::trim); + let kind = kind_label(&provider.driver); + if let Some(name) = named.filter(|name| !name.is_empty() && *name != kind) { + return name.to_owned(); + } + if provider.instance_id != provider.driver { + let humanized = humanize(&provider.instance_id); + if !humanized.is_empty() { + return humanized; + } + } + named + .filter(|name| !name.is_empty()) + .map_or(kind, str::to_owned) +} + +fn accent(provider: &Provider) -> Option { + let color = provider.accent_color.as_deref()?.trim(); + let hex = color.strip_prefix('#')?; + if hex.len() != 6 || !hex.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return None; + } + u32::from_str_radix(hex, 16).ok() +} + +/// Black or white, whichever contrasts more with `color` (WCAG relative luminance). +fn accent_foreground(color: u32) -> u32 { + let channel = |shift: u32| { + let value = f64::from((color >> shift) & 0xff) / 255.0; + if value <= 0.04045 { + value / 12.92 + } else { + ((value + 0.055) / 1.055).powf(2.4) + } + }; + let luminance = 0.2126 * channel(16) + 0.7152 * channel(8) + 0.0722 * channel(0); + if (luminance + 0.05) / 0.05 >= 1.05 / (luminance + 0.05) { + 0x000000 + } else { + 0xffffff + } +} + +fn show_badge(entries: &[Entry], entry: &Entry) -> bool { + if accent(entry.provider).is_some() { + return true; + } + entries + .iter() + .filter(|candidate| candidate.provider.driver == entry.provider.driver) + .filter(|candidate| !entry.enabled || candidate.enabled) + .count() + > 1 +} + +fn badge(provider: &Provider, display_name: &str) -> Badge { + Badge { + initials: initials(display_name), + accent: accent(provider).map(|color| (color, accent_foreground(color))), + } +} + +fn is_separator(ch: char) -> bool { + let ascii_punctuation = ch.is_ascii_punctuation() && !"$+<=>^`|~".contains(ch); + let general_punctuation = matches!(ch, '\u{2010}'..='\u{2027}' | '\u{2030}'..='\u{205e}'); + (ascii_punctuation || general_punctuation) && ch != '\'' && ch != '\u{2019}' +} + +/// First two letters of a one-word name, else the first letter of the first two words. +pub fn initials(label: &str) -> String { + let spaced: String = label + .chars() + .map(|ch| if is_separator(ch) { ' ' } else { ch }) + .collect(); + let words: Vec<&str> = spaced + .split_whitespace() + .map(|word| word.trim_matches(|ch| ch == '\'' || ch == '\u{2019}')) + .filter(|word| !word.is_empty()) + .collect(); + match words.as_slice() { + [] => String::new(), + [word] => word.chars().take(2).collect::().to_uppercase(), + [first, second, ..] => [first, second] + .iter() + .filter_map(|word| word.chars().next()) + .collect::() + .to_uppercase(), + } +} + +fn non_version_words(text: &str) -> Vec { + let mut stripped = String::new(); + let mut depth = 0; + for ch in text.chars() { + match ch { + '(' => depth += 1, + ')' if depth > 0 => { + depth -= 1; + stripped.push(' '); + } + _ if depth == 0 => stripped.push(ch), + _ => {} + } + } + stripped + .split(|ch: char| ch.is_whitespace() || ch == '-') + .filter(|word| !word.is_empty() && !is_version(word)) + .map(str::to_owned) + .collect() +} + +fn is_version(word: &str) -> bool { + let word = word.strip_prefix(['v', 'V']).unwrap_or(word); + let word = match word.strip_suffix(|ch: char| ch.is_ascii_alphabetic()) { + Some(rest) if rest.ends_with(|ch: char| ch.is_ascii_digit()) => rest, + _ => word, + }; + !word.is_empty() + && word + .split('.') + .all(|part| !part.is_empty() && part.bytes().all(|b| b.is_ascii_digit())) +} + +fn names_model(text: &str) -> bool { + non_version_words(text) + .iter() + .any(|word| !VARIANT_WORDS.contains(&word.to_lowercase().as_str())) +} + +fn native_families(driver: &str) -> &'static [&'static str] { + match driver { + "codex" => &["GPT-", "GPT "], + "claudeAgent" => &["Claude "], + "cursor" | "grok" => &["Grok "], + "antigravity" => &["Gemini "], + "muse" => &["Muse "], + _ => &[], + } +} + +/// `^[A-Z][\w.&\- ]{0,24}: (?=\S)` +fn strip_vendor_prefix(name: &str) -> &str { + let Some(colon) = name.find(": ") else { + return name; + }; + let head = &name[..colon]; + let valid = head.chars().count() <= 25 + && head.starts_with(|ch: char| ch.is_ascii_uppercase()) + && head + .chars() + .all(|ch| ch.is_alphanumeric() || matches!(ch, '_' | '.' | '&' | '-' | ' ')); + let rest = &name[colon + 2..]; + if valid && rest.starts_with(|ch: char| !ch.is_whitespace()) { + rest + } else { + name + } +} + +fn model_short_name(name: &str, native: &[&str]) -> Option { + let unprefixed = strip_vendor_prefix(name); + let without_family = FAMILY_PREFIXES + .iter() + .find(|prefix| unprefixed.starts_with(**prefix)) + .and_then(|prefix| { + let rest = unprefixed[prefix.len()..].trim(); + let keep = if native.contains(prefix) { + !non_version_words(rest).is_empty() + } else { + names_model(rest) + }; + keep.then(|| { + if rest.contains(char::is_whitespace) { + rest.to_owned() + } else { + rest.replace('-', " ") + } + }) + }); + let short = without_family.unwrap_or_else(|| { + if names_model(unprefixed) { + unprefixed.to_owned() + } else { + name.to_owned() + } + }); + (short != name).then_some(short) +} + +/// Drops a leading `qualifier` and its separator (`stripLeadingQualifier`). +fn display_model_name(name: &str, qualifier: Option<&str>) -> String { + let Some(qualifier) = qualifier + .map(str::trim) + .filter(|qualifier| !qualifier.is_empty()) + else { + return name.to_owned(); + }; + let lower_name = name.to_lowercase(); + let lower_qualifier = qualifier.to_lowercase(); + let Some(rest) = lower_name + .strip_prefix(&lower_qualifier) + .filter(|_| name.is_char_boundary(lower_qualifier.len())) + .map(|_| &name[lower_qualifier.len()..]) + else { + return name.to_owned(); + }; + let after = rest.trim_start(); + let separated = after.len() < rest.len() || after.starts_with(['.', ':', '/', '-']); + if !separated { + return name.to_owned(); + } + let stripped = after + .strip_prefix(['.', ':', '/', '-']) + .unwrap_or(after) + .trim(); + if stripped.is_empty() { + name.to_owned() + } else { + stripped.to_owned() + } +} + +fn trigger_model_name(model: &Model, driver: &str) -> String { + let short = model + .short_name + .clone() + .or_else(|| model_short_name(&model.name, native_families(driver))); + match short { + Some(short) => display_model_name(&short, model.sub_provider.as_deref()), + None => display_model_name(&model.name, model.sub_provider.as_deref()), + } +} + +fn is_reasoning(descriptor: &Descriptor) -> bool { + match descriptor { + Descriptor::Select { id, role, .. } => { + role.as_deref() == Some("reasoning") || REASONING_IDS.contains(&id.as_str()) + } + Descriptor::Boolean { id, .. } => REASONING_IDS.contains(&id.as_str()), + } +} + +fn is_speed_control(descriptor: &Descriptor, driver: &str) -> bool { + match descriptor { + Descriptor::Select { id, .. } => driver == "codex" && id == "serviceTier", + Descriptor::Boolean { id, .. } => id == "fastMode", + } +} + +/// The reasoning control's current setting, such as `High`. +fn effort_word(model: &Model, driver: &str, selection: Option<&Selection>) -> Option { + let descriptors = &model.capabilities.as_ref()?.option_descriptors; + let candidates = || { + descriptors + .iter() + .filter(|descriptor| !is_speed_control(descriptor, driver)) + }; + let reasoning = candidates() + .filter(|descriptor| matches!(descriptor, Descriptor::Select { .. })) + .chain(candidates().filter(|descriptor| matches!(descriptor, Descriptor::Boolean { .. }))) + .find(|descriptor| is_reasoning(descriptor))?; + match reasoning { + Descriptor::Select { + id, + options, + current_value, + .. + } => { + let current = selection + .and_then(|selection| selection.option(id)) + .or_else(|| current_value.clone()) + .or_else(|| { + options + .iter() + .find(|option| option.is_default) + .map(|option| option.id.clone()) + })?; + options + .iter() + .find(|option| option.id == current) + .map(|option| option.label.clone()) + } + Descriptor::Boolean { + id, + label, + current_value, + } => { + let on = selection + .and_then(|selection| selection.option(id)) + .map(|value| value == "true") + .or(*current_value) + == Some(true); + Some(format!("{label} {}", if on { "On" } else { "Off" })) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::strip::{EnvMode, RuntimeMode}; + use serde_json::json; + + fn model(slug: &str, name: &str, is_default: bool) -> Value { + json!({"slug": slug, "name": name, "isCustom": false, "isDefault": is_default, "capabilities": null}) + } + + fn provider(instance: &str, driver: &str, status: &str, models: Vec) -> Value { + json!({"instanceId": instance, "driver": driver, "status": status, "models": models}) + } + + fn catalog(providers: Vec, settings: Value) -> Catalog { + Catalog::decode(json!({"providers": providers, "settings": settings})).unwrap() + } + + #[test] + fn defaults_prefer_the_project_override() { + let catalog = catalog( + vec![], + json!({ + "defaultRuntimeMode": "auto", + "defaultThreadEnvMode": "worktree", + "projectSettingsOverrides": {"p": {"defaultThreadEnvMode": "local"}}, + }), + ); + let overridden = catalog.defaults(Some("p")); + assert_eq!(overridden.env_mode, EnvMode::Local); + assert_eq!(overridden.runtime_mode, RuntimeMode::Auto); + assert_eq!(catalog.defaults(Some("q")).env_mode, EnvMode::Worktree); + assert_eq!(catalog.defaults(None).env_mode, EnvMode::Worktree); + } + + #[test] + fn initials_follow_the_web() { + assert_eq!(initials("Codex"), "CO"); + assert_eq!(initials("Codex (hub)"), "CH"); + assert_eq!(initials("Theo's Codex"), "TC"); + assert_eq!(initials("Codex 'hub'"), "CH"); + assert_eq!(initials("work-claude personal"), "WC"); + assert_eq!(initials(" "), ""); + } + + #[test] + fn accent_foreground_contrasts() { + assert_eq!(accent_foreground(0xffffff), 0x000000); + assert_eq!(accent_foreground(0x000000), 0xffffff); + assert_eq!(accent_foreground(0x1d4ed8), 0xffffff); + assert_eq!(accent_foreground(0xfacc15), 0x000000); + } + + #[test] + fn short_names_drop_the_native_family() { + let claude = native_families("claudeAgent"); + assert_eq!( + model_short_name("Claude Sonnet 4.5", claude).as_deref(), + Some("Sonnet 4.5") + ); + assert_eq!(model_short_name("Claude 4.5", claude), None); + assert_eq!(model_short_name("GPT-5.5", native_families("codex")), None); + assert_eq!( + model_short_name("GPT-5.5-Codex", native_families("codex")).as_deref(), + Some("5.5 Codex") + ); + assert_eq!(model_short_name("GPT-5 Pro", &[]), None); + assert_eq!( + model_short_name("Anthropic: Claude Opus 5", &[]).as_deref(), + Some("Opus 5") + ); + } + + #[test] + fn qualifiers_are_stripped_from_names() { + assert_eq!( + display_model_name("OpenAI / GPT-5", Some("OpenAI")), + "GPT-5" + ); + assert_eq!(display_model_name("OpenAI GPT-5", Some("openai")), "GPT-5"); + assert_eq!(display_model_name("OpenAIs", Some("OpenAI")), "OpenAIs"); + assert_eq!(display_model_name("OpenAI", Some("OpenAI")), "OpenAI"); + } + + #[test] + fn display_names_prefer_the_snapshot_then_the_instance() { + let named = |instance: &str, display: Option<&str>| Provider { + instance_id: instance.into(), + driver: "codex".into(), + display_name: display.map(str::to_owned), + accent_color: None, + status: "ready".into(), + availability: None, + models: Vec::new(), + }; + assert_eq!(display_name(&named("codex", Some("Codex"))), "Codex"); + assert_eq!( + display_name(&named("codex_work", Some("Codex"))), + "Codex Work" + ); + assert_eq!(display_name(&named("codex_work", Some("Hub"))), "Hub"); + assert_eq!(humanize("claudeAgent"), "Claude Agent"); + } + + #[test] + fn unconfigured_instances_enable_only_codex_and_claude() { + let catalog = catalog( + vec![ + provider("codex", "codex", "ready", vec![]), + provider("cursor", "cursor", "ready", vec![]), + ], + json!({}), + ); + let enabled: Vec = catalog + .entries(None) + .iter() + .map(|entry| entry.enabled) + .collect(); + assert_eq!(enabled, [true, false]); + } + + #[test] + fn configured_instances_follow_driver_defaults_and_flags() { + let config = |value: Value| serde_json::from_value::(value).unwrap(); + assert!(config_enabled(&config(json!({"driver": "codex"})), "codex")); + assert!(!config_enabled( + &config(json!({"driver": "cursor"})), + "cursor" + )); + assert!(config_enabled(&config(json!({"enabled": true})), "cursor")); + assert!(!config_enabled( + &config(json!({"enabled": true, "config": {"enabled": false}})), + "codex" + )); + assert!(config_enabled( + &config(json!({"config": {"enabled": true}})), + "cursor" + )); + } + + #[test] + fn project_override_beats_instance_config() { + let catalog = catalog( + vec![provider("codex", "codex", "ready", vec![])], + json!({"projectSettingsOverrides": {"p1": {"providerInstances": {"codex": {"enabled": false}}}}}), + ); + assert!(catalog.entries(Some("p2"))[0].enabled); + assert!(!catalog.entries(Some("p1"))[0].enabled); + } + + #[test] + fn entries_group_by_driver_with_defaults_first() { + let catalog = catalog( + vec![ + provider("codex_b", "codex", "ready", vec![]), + provider("claudeAgent", "claudeAgent", "ready", vec![]), + provider("codex", "codex", "ready", vec![]), + ], + json!({}), + ); + let ids: Vec = catalog + .entries(None) + .iter() + .map(|entry| entry.provider.instance_id.clone()) + .collect(); + assert_eq!(ids, ["codex", "codex_b", "claudeAgent"]); + } + + #[test] + fn selection_falls_back_to_a_ready_instance() { + let catalog = catalog( + vec![ + provider("codex", "codex", "error", vec![model("a", "A", true)]), + provider( + "claudeAgent", + "claudeAgent", + "ready", + vec![model("b", "B", true)], + ), + ], + json!({}), + ); + assert_eq!(catalog.trigger(None).unwrap().driver, "claudeAgent"); + } + + #[test] + fn unavailable_instances_are_skipped() { + let mut unavailable = provider("codex", "codex", "ready", vec![model("a", "A", true)]); + unavailable["availability"] = json!("unavailable"); + let catalog = catalog( + vec![ + unavailable, + provider( + "claudeAgent", + "claudeAgent", + "warning", + vec![model("b", "B", true)], + ), + ], + json!({}), + ); + assert_eq!(catalog.trigger(None).unwrap().driver, "claudeAgent"); + } + + #[test] + fn stored_default_selection_wins_and_resolves_the_model() { + let models = vec![ + model("gpt-a", "GPT A", true), + model("gpt-b", "GPT B", false), + ]; + let catalog = catalog( + vec![ + provider( + "claudeAgent", + "claudeAgent", + "ready", + vec![model("c", "C", true)], + ), + provider("codex", "codex", "ready", models), + ], + json!({"defaultModelSelection": {"instanceId": "codex", "model": "gpt b"}}), + ); + let trigger = catalog.trigger(None).unwrap(); + assert_eq!(trigger.driver, "codex"); + assert_eq!(trigger.title, "GPT B"); + } + + #[test] + fn project_default_applies_only_when_its_instance_is_enabled() { + let providers = vec![ + provider("codex", "codex", "ready", vec![model("a", "A", true)]), + provider( + "claudeAgent", + "claudeAgent", + "ready", + vec![model("b", "B", true)], + ), + ]; + let enabled = catalog( + providers.clone(), + json!({"projectSettingsOverrides": {"p": {"defaultModelSelection": {"instanceId": "claudeAgent", "model": "b"}}}}), + ); + assert_eq!(enabled.trigger(Some("p")).unwrap().driver, "claudeAgent"); + assert_eq!(enabled.trigger(None).unwrap().driver, "codex"); + let disabled = catalog( + providers, + json!({"projectSettingsOverrides": {"p": { + "defaultModelSelection": {"instanceId": "claudeAgent", "model": "b"}, + "providerInstances": {"claudeAgent": {"enabled": false}} + }}}), + ); + assert_eq!(disabled.trigger(Some("p")).unwrap().driver, "codex"); + } + + #[test] + fn default_model_prefers_flagged_then_first_non_custom() { + let mut custom = model("custom", "Custom", true); + custom["isCustom"] = json!(true); + let provider_of = |models: Vec| { + catalog(vec![provider("codex", "codex", "ready", models)], json!({})) + .providers + .remove(0) + }; + assert_eq!( + default_model(&provider_of(vec![ + custom.clone(), + model("a", "A", false), + model("b", "B", true) + ])), + "b" + ); + assert_eq!( + default_model(&provider_of(vec![custom, model("a", "A", false)])), + "a" + ); + let cursor = catalog( + vec![provider("cursor", "cursor", "ready", vec![])], + json!({}), + ) + .providers + .remove(0); + assert_eq!(default_model(&cursor), "auto"); + } + + #[test] + fn badge_shows_for_accents_and_sibling_instances() { + let mut accented = provider("codex", "codex", "ready", vec![]); + accented["accentColor"] = json!("#1d4ed8"); + let alone = catalog(vec![accented], json!({})); + let entries = alone.entries(None); + assert!(show_badge(&entries, &entries[0])); + let plain = catalog(vec![provider("codex", "codex", "ready", vec![])], json!({})); + let entries = plain.entries(None); + assert!(!show_badge(&entries, &entries[0])); + let siblings = catalog( + vec![ + provider("codex", "codex", "ready", vec![]), + provider("codex_work", "codex", "ready", vec![]), + ], + json!({"providerInstances": {"codex_work": {"driver": "codex", "enabled": true}}}), + ); + let entries = siblings.entries(None); + assert!(show_badge(&entries, &entries[0])); + assert_eq!(badge(entries[1].provider, "Codex Work").initials, "CW"); + } + + #[test] + fn effort_reads_the_reasoning_select() { + let mut with_effort = model("m", "M", true); + with_effort["capabilities"] = json!({"optionDescriptors": [ + {"id": "serviceTier", "label": "Speed", "type": "select", "options": [{"id": "fast", "label": "Fast"}], "currentValue": "fast"}, + {"id": "reasoningEffort", "label": "Reasoning", "type": "select", + "options": [{"id": "low", "label": "Low"}, {"id": "high", "label": "High", "isDefault": true}]}, + {"id": "fastMode", "label": "Fast", "type": "boolean", "currentValue": true} + ]}); + let catalog = catalog( + vec![provider("codex", "codex", "ready", vec![with_effort])], + json!({}), + ); + assert_eq!( + catalog.trigger(None).unwrap().effort.as_deref(), + Some("High") + ); + let chosen = + catalog_with_selection(&catalog, json!([{"id": "reasoningEffort", "value": "low"}])); + assert_eq!(chosen.as_deref(), Some("Low")); + } + + fn catalog_with_selection(catalog: &Catalog, options: Value) -> Option { + let selection: Selection = serde_json::from_value( + json!({"instanceId": "codex", "model": "m", "options": options}), + ) + .unwrap(); + effort_word(&catalog.providers[0].models[0], "codex", Some(&selection)) + } + + #[test] + fn boolean_reasoning_reads_on_or_off() { + let mut thinking = model("m", "M", true); + thinking["capabilities"] = json!({"optionDescriptors": [ + {"id": "thinking", "label": "Thinking", "type": "boolean", "currentValue": true} + ]}); + let catalog = catalog( + vec![provider( + "claudeAgent", + "claudeAgent", + "ready", + vec![thinking], + )], + json!({}), + ); + assert_eq!( + catalog.trigger(None).unwrap().effort.as_deref(), + Some("Thinking On") + ); + } + + #[test] + fn malformed_entries_are_dropped_not_fatal() { + let catalog = catalog( + vec![ + json!({"instanceId": 3}), + provider( + "codex", + "codex", + "ready", + vec![json!("junk"), model("a", "A", true)], + ), + ], + json!({}), + ); + assert_eq!(catalog.providers.len(), 1); + assert_eq!(catalog.providers[0].models.len(), 1); + } + + #[test] + fn no_selectable_provider_means_no_trigger() { + assert_eq!( + catalog( + vec![provider("cursor", "cursor", "ready", vec![])], + json!({}) + ) + .trigger(None), + None + ); + } +} diff --git a/apps/desktop-gpui/src/rpc.rs b/apps/desktop-gpui/src/rpc.rs index f7bcc189ed916..ee6313eb4c183 100644 --- a/apps/desktop-gpui/src/rpc.rs +++ b/apps/desktop-gpui/src/rpc.rs @@ -35,6 +35,24 @@ impl Method for ProjectsMutate { type Success = wire::ProjectsMutateSuccess; } +/// `server.getConfig`, a unary call. The wire types are not generated; `providers::Catalog` decodes it. +pub struct ServerGetConfig; + +impl Method for ServerGetConfig { + const TAG: &'static str = "server.getConfig"; + type Payload = Value; + type Success = Value; +} + +/// `vcs.listRefs`, a unary call. The wire types are not generated; `strip::Branch` decodes it. +pub struct VcsListRefs; + +impl Method for VcsListRefs { + const TAG: &'static str = "vcs.listRefs"; + type Payload = Value; + type Success = Value; +} + /// Request ids are bigints on the wire, so they travel as strings. fn id_text<'de, D: Deserializer<'de>>(deserializer: D) -> Result { match Value::deserialize(deserializer)? { diff --git a/apps/desktop-gpui/src/shell.rs b/apps/desktop-gpui/src/shell.rs index 15aa77c499bbb..ef9e9942c5fdf 100644 --- a/apps/desktop-gpui/src/shell.rs +++ b/apps/desktop-gpui/src/shell.rs @@ -2,13 +2,14 @@ //! `packages/client-runtime/src/state/shellReducer.ts` for what this client shows. use crate::projects; -use crate::status::{Section, ShellView, ThreadRow}; +use crate::status::{PrBadge, PrState, Section, ShellView, ThreadRow, TopStatus}; use crate::wire::generated::{ OrchestrationSubscribeShellSuccess as Item, OrchestrationSubscribeShellSuccessVariant4Location as Location, OrchestrationSubscribeShellSuccessVariant5Location as RemovedLocation, }; use crate::wire::generated::{ + PullRequestsItem, PullRequestsItemSource as LinkSource, SnapshotState as PrSnapshotState, ThreadsEntryActivityRunStatus as Activity, ThreadsEntryInteractionMode as InteractionMode, ThreadsEntryPendingRuntimeRequestKind as RequestKind, ThreadsEntrySettledOverride as Override, ThreadsEntryStatus as RunStatus, @@ -278,6 +279,68 @@ fn sort_settled(threads: &mut [&Thread]) { threads.sort_by(|a, b| newest_first((anchor(a), a), (anchor(b), b))); } +/// `resolveThreadPullRequestBadge` with the presentation of `ThreadStatusIndicators`. A stack is +/// the links of one native stack; stacks the web derives from head and base branches show as `+N`. +fn pr_badge(thread: &Thread) -> Option { + let visible: Vec<_> = thread + .pull_requests + .iter() + .flat_map(|links| &links.0) + .filter(|link| link.source != LinkSource::StackDismissed) + .collect(); + if visible.is_empty() { + let linked = thread.linked_pull_request.as_ref()?; + return Some(PrBadge { + state: PrState::Pending, + stack: false, + text: linked.number.to_string(), + }); + } + let state_of = |link: &&PullRequestsItem| match link.snapshot.as_ref() { + None => PrState::Open, + Some(snapshot) => match snapshot.state { + PrSnapshotState::Open if snapshot.is_draft => PrState::Draft, + PrSnapshotState::Open => PrState::Open, + PrSnapshotState::Closed => PrState::Closed, + PrSnapshotState::Merged => PrState::Merged, + }, + }; + if let [only] = visible.as_slice() { + return Some(PrBadge { + state: only + .snapshot + .as_ref() + .map_or(PrState::Pending, |_| state_of(&only)), + stack: false, + text: only.number.to_string(), + }); + } + let states: Vec = visible.iter().map(state_of).collect(); + let state = if states.iter().all(|state| *state == PrState::Draft) { + PrState::Draft + } else if states.contains(&PrState::Open) { + PrState::Open + } else if states.iter().all(|state| *state == PrState::Merged) { + PrState::Merged + } else { + PrState::Closed + }; + let stack_id = visible[0].stack.as_ref().map(|stack| &stack.id); + let stack = stack_id.is_some() + && visible + .iter() + .all(|link| link.stack.as_ref().map(|stack| &stack.id) == stack_id); + Some(PrBadge { + state, + stack, + text: if stack { + visible.len().to_string() + } else { + format!("+{}", visible.len()) + }, + }) +} + fn now_ms() -> i64 { SystemTime::now() .duration_since(UNIX_EPOCH) @@ -336,7 +399,17 @@ impl ShellState { .unwrap_or_default(), title: thread.title.clone(), branch: thread.branch.clone(), - status: thread.status, + worktree: thread + .worktree_path + .as_deref() + .is_some_and(|path| !path.trim().is_empty()), + provider_instance: thread.provider_instance_id.clone(), + top: TopStatus::of( + thread.status, + thread.blocked_on_thread_wait == Some(true), + thread.last_error_class, + ), + pr: pr_badge(thread), section, }; let rows = SECTION_ORDER @@ -523,6 +596,46 @@ mod tests { } } + #[test] + fn the_pull_request_badge_follows_the_web() { + let link = |number: u64, source: &str, state: Option<&str>| { + let mut link = serde_json::json!({ + "host": "github.com", "linkedAt": "2026-01-01T00:00:00.000Z", + "number": number, "repository": "o/r", "source": source, + "url": format!("https://github.com/o/r/pull/{number}"), + "snapshot": null, "stack": null, + }); + if let Some(state) = state { + link["snapshot"] = serde_json::json!({ + "baseBranch": "main", "headBranch": "h", "isDraft": false, "state": state, + "syncedAt": "2026-01-01T00:00:00.000Z", "title": "t", "updatedAt": null, + }); + } + link + }; + let badge = |links: Vec| { + pr_badge(&thread("t", |t| set(t, &[("pullRequests", links.into())]))) + .map(|badge| (badge.state, badge.text)) + }; + assert_eq!(badge(vec![]), None); + assert_eq!( + badge(vec![link(7, "manual", None)]), + Some((PrState::Pending, "7".to_owned())) + ); + assert_eq!( + badge(vec![link(7, "manual", Some("merged"))]), + Some((PrState::Merged, "7".to_owned())) + ); + assert_eq!( + badge(vec![ + link(1, "manual", Some("merged")), + link(2, "agent", Some("open")), + link(3, "stack-dismissed", Some("closed")), + ]), + Some((PrState::Open, "+2".to_owned())) + ); + } + #[test] fn timestamps_parse_like_date_parse() { for (text, expected) in [ diff --git a/apps/desktop-gpui/src/stage-dev-button.svg b/apps/desktop-gpui/src/stage-dev-button.svg new file mode 100644 index 0000000000000..1210577b677f5 --- /dev/null +++ b/apps/desktop-gpui/src/stage-dev-button.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/desktop-gpui/src/stage-dev.svg b/apps/desktop-gpui/src/stage-dev.svg new file mode 100644 index 0000000000000..3b36247ceba78 --- /dev/null +++ b/apps/desktop-gpui/src/stage-dev.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/desktop-gpui/src/status.rs b/apps/desktop-gpui/src/status.rs index 4199193a84f6d..7c98c2bf8c0f6 100644 --- a/apps/desktop-gpui/src/status.rs +++ b/apps/desktop-gpui/src/status.rs @@ -1,5 +1,7 @@ -use crate::projects::{EnvironmentProjects, ProjectEntry}; -use crate::wire::generated::ThreadsEntryStatus; +use crate::projects::{EnvironmentProjects, MachineKind, ProjectEntry}; +use crate::providers::Catalog; +use crate::strip::Branch; +use crate::wire::generated::{ThreadsEntryLastErrorClass, ThreadsEntryStatus}; use std::fmt; /// What the window shows about the managed server and its socket. @@ -11,6 +13,7 @@ pub enum Status { /// Socket is up. `shell` stays `None` until the first shell snapshot lands. Connected { label: String, + machine: MachineKind, /// The server keeps project pins, so the window offers them. can_pin: bool, shell: Option, @@ -18,6 +21,13 @@ pub enum Status { Failed(String), /// Another saved environment's projects; says nothing about the primary connection. Environment(EnvironmentProjects), + /// The primary connection's provider instances, for the composer's model picker. + Providers(Catalog), + /// The branch checked out in a workspace root; `None` when it is not a repository. + Branch { + root: String, + branch: Option, + }, } /// Where the web sidebar lists a thread. @@ -48,10 +58,72 @@ pub struct ThreadRow { pub project: String, pub title: String, pub branch: Option, - pub status: ThreadsEntryStatus, + pub worktree: bool, + pub provider_instance: String, + pub top: Option, + pub pr: Option, pub section: Section, } +/// A pull request's state as the badge tones it; `Pending` has no snapshot yet. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PrState { + Pending, + Open, + Draft, + Closed, + Merged, +} + +/// The web's `ThreadPullRequestBadgePresentation`: the number, `+N` for several, or the layer +/// count of a stack. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PrBadge { + pub state: PrState, + pub stack: bool, + pub text: String, +} + +/// The status the web's thread card shows at the top right (`topStatus` in `SidebarThreadRow`). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TopStatus { + Working, + Waiting, + Limited, + Failed, +} + +impl TopStatus { + pub fn label(self) -> &'static str { + match self { + Self::Working => "Working", + Self::Waiting => "Waiting", + Self::Limited => "Limited", + Self::Failed => "Failed", + } + } + + /// `resolveSidebarThreadStatus` without approvals, input and guest requests, which the row + /// does not carry yet. + pub fn of( + status: ThreadsEntryStatus, + blocked_on_thread_wait: bool, + last_error_class: Option, + ) -> Option { + use ThreadsEntryStatus as S; + match status { + S::Running if blocked_on_thread_wait => Some(Self::Waiting), + S::Preparing | S::Queued | S::Starting | S::Running | S::Waiting => Some(Self::Working), + S::Idle => Some(Self::Waiting), + S::Failed if last_error_class == Some(ThreadsEntryLastErrorClass::UsageLimit) => { + Some(Self::Limited) + } + S::Failed => Some(Self::Failed), + _ => None, + } + } +} + fn plural(count: usize, noun: &str) -> String { if count == 1 { noun.to_owned() @@ -83,6 +155,8 @@ impl fmt::Display for Status { ), Self::Failed(message) => f.write_str(message), Self::Environment(other) => write!(f, "Environment {}", other.label), + Self::Providers(_) => f.write_str("Providers"), + Self::Branch { root, .. } => write!(f, "Branch of {root}"), } } } @@ -98,17 +172,49 @@ pub fn publish(sender: &StatusSender, status: Status) { mod tests { use super::*; + #[test] + fn the_top_status_follows_the_web() { + let of = |status, blocked, class| TopStatus::of(status, blocked, class); + assert_eq!( + of(ThreadsEntryStatus::Running, false, None), + Some(TopStatus::Working) + ); + assert_eq!( + of(ThreadsEntryStatus::Running, true, None), + Some(TopStatus::Waiting) + ); + assert_eq!( + of(ThreadsEntryStatus::Idle, false, None), + Some(TopStatus::Waiting) + ); + assert_eq!( + of( + ThreadsEntryStatus::Failed, + false, + Some(ThreadsEntryLastErrorClass::UsageLimit) + ), + Some(TopStatus::Limited) + ); + assert_eq!( + of(ThreadsEntryStatus::Failed, false, None), + Some(TopStatus::Failed) + ); + assert_eq!(of(ThreadsEntryStatus::Completed, false, None), None); + } + #[test] fn connected_text_includes_counts_once_known() { let label = "Local".to_owned(); let waiting = Status::Connected { label: label.clone(), + machine: MachineKind::Server, can_pin: false, shell: None, }; assert_eq!(waiting.to_string(), "Connected to Local"); let known = Status::Connected { label: label.clone(), + machine: MachineKind::Server, can_pin: false, shell: Some(ShellView { projects: 2, @@ -122,6 +228,7 @@ mod tests { ); let single = Status::Connected { label, + machine: MachineKind::Server, can_pin: false, shell: Some(ShellView { projects: 1, diff --git a/apps/desktop-gpui/src/strip.rs b/apps/desktop-gpui/src/strip.rs new file mode 100644 index 0000000000000..878803617df64 --- /dev/null +++ b/apps/desktop-gpui/src/strip.rs @@ -0,0 +1,279 @@ +//! What the strip under the composer shows: the environment, workspace, branch and permissions +//! controls (`BranchToolbar.logic.ts`, `BranchToolbarBranchSelector.tsx`, `runtimeModeConfig.ts`). +//! The branch comes from `vcs.listRefs`, decoded by hand like `server.getConfig`. + +use serde_json::Value; + +/// The shortcut half of a tooltip, the web's `shortcutLabelForCommand` for the default bindings on macOS. +pub const HOST_SHORTCUT: &str = "\u{21e7}\u{2318}H"; +pub const WORKSPACE_SHORTCUT: &str = "\u{21e7}\u{2318}X"; +pub const PERMISSIONS_SHORTCUT: &str = "\u{21e7}\u{2318}A"; + +/// The refs listed to find the checked-out branch. +pub const REFS_LIMIT: u32 = 100; + +const GENERIC_LOCAL_LABELS: [&str; 2] = ["local", "local environment"]; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum EnvMode { + #[default] + Local, + Worktree, +} + +impl EnvMode { + fn parse(text: &str) -> Option { + match text { + "local" => Some(Self::Local), + "worktree" => Some(Self::Worktree), + _ => None, + } + } + + /// `resolveEnvModeLabel`. + pub fn label(self) -> &'static str { + match self { + Self::Local => "Current checkout", + Self::Worktree => "New worktree", + } + } +} + +/// `RuntimeMode` (packages/contracts/src/providerPolicy.ts). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RuntimeMode { + ApprovalRequired, + AutoAcceptEdits, + Auto, + FullAccess, +} + +impl RuntimeMode { + fn parse(text: &str) -> Option { + match text { + "approval-required" => Some(Self::ApprovalRequired), + "auto-accept-edits" => Some(Self::AutoAcceptEdits), + "auto" => Some(Self::Auto), + "full-access" => Some(Self::FullAccess), + _ => None, + } + } + + /// `runtimeModeOptions`, the order of the permissions toggle. + pub const ALL: [Self; 4] = [ + Self::ApprovalRequired, + Self::AutoAcceptEdits, + Self::Auto, + Self::FullAccess, + ]; + + /// `runtimeModeConfig`. + pub fn description(self) -> &'static str { + match self { + Self::ApprovalRequired => "Ask before commands and file changes.", + Self::AutoAcceptEdits => "Auto-approve edits, ask before other actions.", + Self::Auto => "Supported providers approve routine actions; others still ask.", + Self::FullAccess => "Allow commands and edits without prompts.", + } + } + + /// `runtimeModeConfig`. + pub fn label(self) -> &'static str { + match self { + Self::ApprovalRequired => "Supervised", + Self::AutoAcceptEdits => "Auto-accept edits", + Self::Auto => "Auto", + Self::FullAccess => "Full access", + } + } +} + +/// The mode the permissions popover names, and whether it is the selected one, as hover previews. +pub fn previewed(selected: RuntimeMode, preview: Option) -> (RuntimeMode, bool) { + let shown = preview.unwrap_or(selected); + (shown, shown == selected) +} + +/// What a fresh draft starts with, from the settings of the environment. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Defaults { + pub env_mode: EnvMode, + pub runtime_mode: RuntimeMode, + pub start_from_origin: bool, +} + +impl Defaults { + /// Unset or unknown values take the schema defaults (settings.ts): a local checkout, full + /// access, and new worktrees starting from origin. The project's `t3.json` is not read. + pub fn resolve( + env_mode: Option<&str>, + runtime_mode: Option<&str>, + start_from_origin: Option, + ) -> Self { + Self { + env_mode: env_mode.and_then(EnvMode::parse).unwrap_or_default(), + runtime_mode: runtime_mode + .and_then(RuntimeMode::parse) + .unwrap_or(RuntimeMode::FullAccess), + start_from_origin: start_from_origin.unwrap_or(true), + } + } +} + +/// The branch a project's checkout is on. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Branch { + pub name: Option, + /// Whether the listed ref is a remote one; `None` when it was not listed. + pub is_remote: Option, +} + +impl Branch { + /// The `vcs.listRefs` result; `None` when no ref is listed, as in a folder that is not a repository. + pub fn decode(result: &Value) -> Option { + let refs = result["refs"].as_array().filter(|refs| !refs.is_empty())?; + let current = refs.iter().find(|r| r["current"] == true); + Some(Self { + name: current.and_then(|r| r["name"].as_str()).map(str::to_owned), + is_remote: current.map(|r| r["isRemote"] == true), + }) + } + + /// `resolveBranchTriggerLabel` for a draft that has no worktree yet. + pub fn label(&self, env_mode: EnvMode, start_from_origin: bool) -> String { + let Some(name) = &self.name else { + return "Select ref".to_owned(); + }; + if env_mode != EnvMode::Worktree { + return name.clone(); + } + // Only the server can determine a local branch's configured remote. + if start_from_origin && self.is_remote == Some(false) { + format!("From {name} (remote)") + } else { + format!("From {name}") + } + } +} + +/// `resolveEnvironmentOptionLabel` for the primary environment: a generic name reads as "This device". +pub fn environment_label(label: &str) -> String { + let label = label.trim(); + if label.is_empty() || GENERIC_LOCAL_LABELS.contains(&label.to_lowercase().as_str()) { + "This device".to_owned() + } else { + label.to_owned() + } +} + +/// A tooltip as the web writes it: the label, then the shortcut after a middle dot. +pub fn tooltip_text(label: &str, shortcut: Option<&str>) -> String { + match shortcut { + Some(shortcut) => format!("{label} \u{b7} {shortcut}"), + None => label.to_owned(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn branch(name: Option<&str>, is_remote: Option) -> Branch { + Branch { + name: name.map(str::to_owned), + is_remote, + } + } + + #[test] + fn branch_label_names_the_base_of_a_new_worktree() { + let main = branch(Some("main"), Some(false)); + assert_eq!(main.label(EnvMode::Worktree, true), "From main (remote)"); + assert_eq!(main.label(EnvMode::Worktree, false), "From main"); + assert_eq!(main.label(EnvMode::Local, true), "main"); + let remote = branch(Some("origin/main"), Some(true)); + assert_eq!(remote.label(EnvMode::Worktree, true), "From origin/main"); + let unlisted = branch(Some("main"), None); + assert_eq!(unlisted.label(EnvMode::Worktree, true), "From main"); + assert_eq!( + branch(None, None).label(EnvMode::Worktree, true), + "Select ref" + ); + } + + #[test] + fn decode_reads_the_checked_out_ref() { + let result = json!({"refs": [ + {"name": "dev", "current": false, "isDefault": false}, + {"name": "main", "current": true, "isDefault": true, "isRemote": false}, + ]}); + assert_eq!( + Branch::decode(&result), + Some(branch(Some("main"), Some(false))) + ); + let detached = json!({"refs": [{"name": "dev", "current": false}]}); + assert_eq!(Branch::decode(&detached), Some(branch(None, None))); + assert_eq!(Branch::decode(&json!({"refs": []})), None); + assert_eq!(Branch::decode(&json!({})), None); + } + + #[test] + fn defaults_fall_back_to_the_schema_values() { + let unset = Defaults::resolve(None, None, None); + assert_eq!(unset.env_mode, EnvMode::Local); + assert_eq!(unset.runtime_mode, RuntimeMode::FullAccess); + assert!(unset.start_from_origin); + let set = Defaults::resolve(Some("worktree"), Some("approval-required"), Some(false)); + assert_eq!(set.env_mode, EnvMode::Worktree); + assert_eq!(set.runtime_mode, RuntimeMode::ApprovalRequired); + assert!(!set.start_from_origin); + let unknown = Defaults::resolve(Some("cloud"), Some("yolo"), None); + assert_eq!(unknown.env_mode, EnvMode::Local); + assert_eq!(unknown.runtime_mode, RuntimeMode::FullAccess); + } + + #[test] + fn previews_name_the_hovered_mode_until_the_pointer_leaves() { + let selected = RuntimeMode::FullAccess; + assert_eq!(previewed(selected, None), (selected, true)); + assert_eq!(previewed(selected, Some(selected)), (selected, true)); + assert_eq!( + previewed(selected, Some(RuntimeMode::Auto)), + (RuntimeMode::Auto, false) + ); + assert_eq!(RuntimeMode::ALL[0], RuntimeMode::ApprovalRequired); + assert_eq!(RuntimeMode::ALL[3], selected); + assert_eq!( + RuntimeMode::Auto.description(), + "Supported providers approve routine actions; others still ask." + ); + } + + #[test] + fn labels_follow_the_web() { + assert_eq!(EnvMode::Worktree.label(), "New worktree"); + assert_eq!(EnvMode::Local.label(), "Current checkout"); + assert_eq!(RuntimeMode::FullAccess.label(), "Full access"); + assert_eq!(RuntimeMode::ApprovalRequired.label(), "Supervised"); + assert_eq!(RuntimeMode::AutoAcceptEdits.label(), "Auto-accept edits"); + assert_eq!(RuntimeMode::Auto.label(), "Auto"); + } + + #[test] + fn generic_environment_names_read_as_this_device() { + assert_eq!(environment_label("Local"), "This device"); + assert_eq!(environment_label(" local environment "), "This device"); + assert_eq!(environment_label(""), "This device"); + assert_eq!(environment_label("Ada's Mac Studio"), "Ada's Mac Studio"); + } + + #[test] + fn tooltips_put_the_shortcut_after_a_middle_dot() { + assert_eq!( + tooltip_text("New worktree", Some(WORKSPACE_SHORTCUT)), + "New worktree \u{b7} \u{21e7}\u{2318}X" + ); + assert_eq!(tooltip_text("From main", None), "From main"); + } +} diff --git a/apps/desktop-gpui/src/ui.rs b/apps/desktop-gpui/src/ui.rs index 1822c899b5015..73cf90846a408 100644 --- a/apps/desktop-gpui/src/ui.rs +++ b/apps/desktop-gpui/src/ui.rs @@ -1,12 +1,18 @@ -use crate::assets::MESSAGE_SQUARE_DASHED; +use crate::assets::{MESSAGE_SQUARE_DASHED, STAGE_DEV, STAGE_DEV_BUTTON, provider_icon}; use crate::connection::{Action, ActionSender}; +use crate::history::History; use crate::projects::{self, EnvironmentProjects, MachineKind, ProjectEntry}; -use crate::status::{Section, ShellView, Status, ThreadRow}; -use crate::wire::generated::ThreadsEntryStatus as ThreadStatus; +use crate::providers::{Catalog, Trigger}; +use crate::status::{PrBadge, PrState, Section, ShellView, Status, ThreadRow, TopStatus}; +use crate::strip::{ + self, Branch, Defaults, EnvMode, HOST_SHORTCUT, PERMISSIONS_SHORTCUT, RuntimeMode, + WORKSPACE_SHORTCUT, +}; use gpui::{ - Anchor, AnyElement, App, Bounds, Context, Entity, Focusable as _, FontWeight, Hsla, - IntoElement, Keystroke, ParentElement, Pixels, Render, RenderOnce, Styled, - UniformListScrollHandle, Window, canvas, div, prelude::*, px, rgb, uniform_list, + Anchor, Animation, AnimationExt as _, AnyElement, App, Bounds, Context, Entity, Focusable as _, + FontWeight, Hsla, IntoElement, Keystroke, ObjectFit, ParentElement, Pixels, Render, RenderOnce, + Styled, UniformListScrollHandle, Window, actions, canvas, div, ease_in_out, img, prelude::*, + px, rgb, uniform_list, }; use gpui_component::{ ActiveTheme, Icon, IconName, Selectable, Sizable, TitleBar, @@ -15,14 +21,72 @@ use gpui_component::{ input::{Input, InputEvent, InputState, Textarea, TextareaState}, kbd::Kbd, popover::Popover, - tag::Tag, tooltip::Tooltip, v_flex, }; -use std::{cell::Cell, rc::Rc}; +use gpui_kit_assets::IconName as Icons; +use std::{ + cell::Cell, + collections::{HashMap, HashSet}, + rc::Rc, + time::Duration, +}; const SIDEBAR_WIDTH: f32 = 300.; -const ROW_HEIGHT: f32 = 68.; +/// The web's `--workspace-topbar-height` on macOS desktop. +const TOPBAR_HEIGHT: f32 = 52.; +/// Clears the traffic lights, like `--workspace-controls-left` from `titlebar-area-x`. +const TITLEBAR_CONTENT_LEFT: f32 = 84.; +/// The web shows the dev stage art in dev builds (`APP_STAGE_LABEL` is "Dev"). +const STAGE_BACKDROP: bool = cfg!(debug_assertions); +/// `SidebarStageBackdrop`'s `h-20`. +const STAGE_BACKDROP_HEIGHT: f32 = 80.; + +actions!( + t3, + [ + ToggleSidebar, + DismissSidebar, + OpenPermissions, + GoBack, + GoForward + ] +); + +/// The web's `SIDEBAR_MOBILE_QUERY`, `(max-width: 767px)`. +const SIDEBAR_BREAKPOINT: f32 = 767.; +/// The left sheet's `w-[calc(100%-(--spacing(12)))] max-w-md`. +const SIDEBAR_OVERLAY_GAP: f32 = 48.; +const SIDEBAR_OVERLAY_MAX: f32 = 448.; +/// The sheet's `duration-200` and its `-translate-x-8` entrance. +const SHEET_MS: u64 = 200; +const SHEET_SLIDE: f32 = 32.; +/// Key context while the overlay is open, so Escape only dismisses it then. +pub const OVERLAY_CONTEXT: &str = "SidebarOverlay"; + +fn is_narrow(width: f32) -> bool { + width <= SIDEBAR_BREAKPOINT +} + +fn narrow_window(window: &Window) -> bool { + is_narrow(f32::from(window.viewport_size().width)) +} + +fn overlay_width(window_width: f32) -> f32 { + (window_width - SIDEBAR_OVERLAY_GAP).min(SIDEBAR_OVERLAY_MAX) +} + +/// The web's `toggleSidebar`: narrow viewports flip the overlay, wide ones the persistent state. +fn toggled(narrow: bool, open: bool, overlay_open: bool) -> (bool, bool) { + if narrow { + (open, !overlay_open) + } else { + (!open, overlay_open) + } +} +/// A thread card (`h-[4.875rem]`) between the `py-0.5` of its list item. +const CARD_HEIGHT: f32 = 78.; +const ROW_HEIGHT: f32 = CARD_HEIGHT + 4.; const NO_PROJECT: &str = "No project"; const HEADLINE_SCRATCH: &str = "What should we work on?"; /// The web's `ComboboxPopup` cap, `max-h-[min(var(--available-height),23rem)]`. @@ -40,13 +104,31 @@ pub struct MainView { list: UniformListScrollHandle, /// Working, Snoozed and Settled shelves; the web starts all of them collapsed. shelves: [bool; 3], + sidebar_open: bool, + sidebar_overlay_open: bool, + /// The sidebar's width at the last render, for the cards' branch truncation. + sidebar_width: Cell, + /// Hover styles cannot change layout, so the send button's hover growth is view state. + send_hovered: bool, + permissions_open: bool, + /// The open thread; selecting is all a row does yet. + selected_thread: Option, + /// Local view state until the draft's mode is sent to the server. + runtime_mode: Option, + permissions: Entity, selected_project: Option, + history: History, picker_open: bool, /// Last painted trigger bounds, so the popup can shrink to fit below it as on the web. picker_trigger: Rc>>, picker_order: Option>, /// The other saved environments, by id. environments: Vec, + providers: Option, + /// The branch per workspace root; `None` for a root that is not a repository. + branches: HashMap>, + /// Roots whose branch was asked for on this connection. + branches_requested: HashSet, actions: ActionSender, } @@ -65,22 +147,49 @@ impl MainView { } }) .detach(); + let composer = cx.new(|cx| { + TextareaState::new(window, cx) + .placeholder("Ask anything. @ files, $ skills, / commands") + // The web's `min-h-[2lh]` and `max-h-52`. + .auto_grow(2, 9) + }); + // The send button enables with content. + cx.subscribe(&composer, |_, _, event: &InputEvent, cx| { + if matches!(event, InputEvent::Change) { + cx.notify(); + } + }) + .detach(); + let view = cx.weak_entity(); + let permissions = cx.new(|_| PermissionsPanel { + view, + selected: RuntimeMode::FullAccess, + preview: None, + }); Self { status, - search: cx.new(|cx| InputState::new(window, cx).placeholder("Search threads")), + search: cx.new(|cx| InputState::new(window, cx).placeholder("Search")), project_search, - composer: cx.new(|cx| { - TextareaState::new(window, cx) - .placeholder("Ask anything. @ files, $ skills, / commands") - .auto_grow(3, 10) - }), + composer, list: UniformListScrollHandle::new(), shelves: [false; 3], + sidebar_open: true, + sidebar_overlay_open: false, + sidebar_width: Cell::new(SIDEBAR_WIDTH), + send_hovered: false, + permissions_open: false, + selected_thread: None, + runtime_mode: None, + permissions, selected_project: None, + history: History::default(), picker_open: false, picker_trigger: Rc::default(), picker_order: None, environments: Vec::new(), + providers: None, + branches: HashMap::new(), + branches_requested: HashSet::new(), actions, } } @@ -93,6 +202,17 @@ impl MainView { } return cx.notify(); } + if let Status::Providers(catalog) = status { + self.providers = Some(catalog); + return cx.notify(); + } + if let Status::Branch { root, branch } = status { + self.branches.insert(root, branch); + return cx.notify(); + } + if matches!(status, Status::Connecting) { + self.branches_requested.clear(); + } self.status = status; // Thread activity reorders recency on every delta, so the default must not follow it. if self.selected_project.is_none() { @@ -100,10 +220,31 @@ impl MainView { .shell() .and_then(|shell| active_project(None, shell)) .map(|entry| entry.id.clone()); + if let Some(id) = &self.selected_project { + self.history.visit(id.clone()); + } } + self.request_branch(); cx.notify(); } + /// Asks for the branch of the active project's root once per connection. + fn request_branch(&mut self) { + let Some(entry) = self + .shell() + .and_then(|shell| active_project(self.selected_project.as_deref(), shell)) + else { + return; + }; + let root = entry.root.clone(); + if entry.scratch || root.is_empty() || !self.branches_requested.insert(root.clone()) { + return; + } + if self.actions.try_send(Action::LoadBranch { root }).is_err() { + eprintln!("[ui] could not ask for the branch"); + } + } + fn shell(&self) -> Option<&ShellView> { match &self.status { Status::Connected { shell, .. } => shell.as_ref(), @@ -123,64 +264,446 @@ impl MainView { Some(listed.into_iter().map(|entry| entry.key.clone()).collect()) } - fn environment(&self) -> String { - match &self.status { - Status::Connected { label, .. } => label.clone(), - other => other.to_string(), + /// One draggable row: the sidebar's `SidebarChrome` on the left, the thread breadcrumb right. + fn top_bar(&self, narrow: bool, cx: &mut Context) -> impl IntoElement { + let border = cx.theme().border; + let active = self + .shell() + .and_then(|shell| active_project(self.selected_project.as_deref(), shell)); + let open = self.sidebar_open && !narrow; + TitleBar::new() + .h(px(TOPBAR_HEIGHT)) + .pl_0() + .border_b_0() + .bg(gpui::transparent_black()) + .child(self.sidebar_chrome(open, SIDEBAR_WIDTH, cx)) + .child( + h_flex() + .flex_1() + .min_w_0() + .h_full() + .px_4() + .border_b_1() + .border_color(border) + .when(self.shell().is_some(), |row| { + row.child(self.breadcrumb(active, cx)) + }), + ) + } + + /// The web's `SidebarChrome`: toggle, then the brand and history while the sidebar is shown. + fn sidebar_chrome(&self, open: bool, width: f32, cx: &mut Context) -> impl IntoElement { + let backdrop = open && STAGE_BACKDROP; + let (brand, brand_muted) = if backdrop { + (gpui::white(), gpui::white().opacity(0.7)) + } else { + (cx.theme().foreground, cx.theme().muted_foreground) + }; + let toggle = Kbd::format(&Keystroke::parse("cmd-b").expect("valid keystroke")); + let shortcut = |keys| Kbd::format(&Keystroke::parse(keys).expect("valid keystroke")); + let border = cx.theme().border; + h_flex() + .h_full() + .flex_shrink_0() + .when(open, |row| row.w(px(width))) + .when(!open, |row| row.border_b_1().border_color(border)) + .pl(px(TITLEBAR_CONTENT_LEFT)) + .pr_3() + .gap_1() + .child( + nav_button( + "toggle-sidebar", + if open { + IconName::PanelLeftClose + } else { + IconName::PanelLeft + }, + format!("Toggle main sidebar ({toggle})").into(), + true, + backdrop, + cx, + ) + .on_click( + cx.listener(|this, _, window, cx| { + this.toggle_sidebar(narrow_window(window), cx) + }), + ), + ) + // Like the web, the brand and history live in the sidebar and hide with it. + .when(open, |bar| { + bar.child( + h_flex() + .ml_1() + .gap_1() + .text_sm() + .text_color(brand) + .child(div().font_weight(FontWeight::BOLD).child("T3")) + .child( + div() + .font_weight(FontWeight::MEDIUM) + .text_color(brand_muted) + .child("Code"), + ), + ) + .child( + nav_button( + "go-back", + IconName::ArrowLeft, + format!("Go back ({})", shortcut("cmd-[")).into(), + self.history.can_back(), + backdrop, + cx, + ) + .on_click(cx.listener(|this, _, _, cx| this.go_back(cx))), + ) + .child( + nav_button( + "go-forward", + IconName::ArrowRight, + format!("Go forward ({})", shortcut("cmd-]")).into(), + self.history.can_forward(), + backdrop, + cx, + ) + .on_click(cx.listener(|this, _, _, cx| this.go_forward(cx))), + ) + }) + } + + fn select_project(&mut self, id: String, cx: &mut Context) { + self.history.visit(id.clone()); + self.selected_project = Some(id); + self.picker_open = false; + self.sidebar_overlay_open = false; + self.request_branch(); + cx.notify(); + } + + fn go_back(&mut self, cx: &mut Context) { + if let Some(id) = self.history.back() { + self.sidebar_overlay_open = false; + self.selected_project = Some(id.to_owned()); + self.request_branch(); + cx.notify(); } } - fn sidebar(&self, cx: &mut Context) -> impl IntoElement { - let count = self - .shell() - .map_or(0, |shell| sidebar_items(&shell.rows, self.shelves).len()); + fn go_forward(&mut self, cx: &mut Context) { + if let Some(id) = self.history.forward() { + self.sidebar_overlay_open = false; + self.selected_project = Some(id.to_owned()); + self.request_branch(); + cx.notify(); + } + } + + fn toggle_sidebar(&mut self, narrow: bool, cx: &mut Context) { + (self.sidebar_open, self.sidebar_overlay_open) = + toggled(narrow, self.sidebar_open, self.sidebar_overlay_open); + cx.notify(); + } + + fn close_sidebar_overlay(&mut self, cx: &mut Context) { + self.sidebar_overlay_open = false; + cx.notify(); + } + + /// The web's mobile `Sheet`: a dimmed backdrop and a left drawer sliding in over the content. + fn sidebar_drawer(&self, width: f32, window: &Window, cx: &mut Context) -> AnyElement { + let drawer = div() + .absolute() + .top_0() + .bottom_0() + .left_0() + .w(px(width)) + .occlude() + .shadow_lg() + .child(self.sidebar_surface(width, cx)) + .child( + v_flex() + .size_full() + .child( + TitleBar::new() + .h(px(TOPBAR_HEIGHT)) + .pl_0() + .border_b_0() + .bg(gpui::transparent_black()) + .child(self.sidebar_chrome(true, width, cx)), + ) + .child( + div() + .flex_1() + .min_h_0() + .child(self.sidebar(width, window, cx)), + ), + ) + .with_animation( + "sidebar-drawer", + Animation::new(Duration::from_millis(SHEET_MS)).with_easing(ease_in_out), + |drawer, delta| drawer.left(px(-SHEET_SLIDE * (1. - delta))).opacity(delta), + ); + div() + .absolute() + .inset_0() + .child( + div() + .id("sidebar-backdrop") + .absolute() + .inset_0() + .cursor_default() + .bg(cx.theme().background.opacity(0.6)) + .on_click(cx.listener(|this, _, _, cx| this.close_sidebar_overlay(cx))) + .with_animation( + "sidebar-backdrop", + Animation::new(Duration::from_millis(SHEET_MS)).with_easing(ease_in_out), + |backdrop, delta| backdrop.opacity(delta), + ), + ) + .child(drawer) + .into_any_element() + } + + /// The sidebar's background, border and stage art, painted under the top bar and sidebar + /// so the art runs behind both, as the web's `SidebarStageBackdrop` does. + fn sidebar_surface(&self, width: f32, cx: &mut Context) -> impl IntoElement { + div() + .absolute() + .top_0() + .left_0() + .bottom_0() + .w(px(width)) + .bg(cx.theme().sidebar) + .border_r_1() + .border_color(cx.theme().sidebar_border) + .when(STAGE_BACKDROP, |surface| { + surface.child( + img(STAGE_DEV) + .absolute() + .top_0() + .left_0() + .w(px(width)) + .h(px(STAGE_BACKDROP_HEIGHT)) + .object_fit(ObjectFit::Cover), + ) + }) + } + + fn sidebar(&self, width: f32, window: &Window, cx: &mut Context) -> impl IntoElement { + self.sidebar_width.set(width); + let (top, shelves) = self.shell().map_or_else(Default::default, |shell| { + ( + top_rows(&shell.rows).len(), + shelves(&shell.rows, self.shelves), + ) + }); v_flex() - .w(px(SIDEBAR_WIDTH)) + .w(px(width)) .flex_shrink_0() .h_full() .gap_2() .p_2() - .bg(cx.theme().sidebar) - .border_r_1() - .border_color(cx.theme().sidebar_border) - .child(Input::new(&self.search).prefix(Icon::new(IconName::Search))) + .child(self.sidebar_header(window, cx)) .child( uniform_list( "threads", - count, + top, cx.processor(|this, range: std::ops::Range, _, cx| { let Some(shell) = this.shell() else { return Vec::new(); }; - let (muted, hover) = (cx.theme().muted_foreground, cx.theme().list_hover); - let items = sidebar_items(&shell.rows, this.shelves); - items[range] + let rows = top_rows(&shell.rows); + rows[range] .iter() - .map(|item| match *item { - SidebarItem::Row(index) => { - thread_row(&shell.rows[index], muted, hover).into_any_element() - } - SidebarItem::Header { - section, - count, - expanded, - } => { - let shelf = shelf_index(section); - shelf_header(section, count, expanded, muted, hover) - .on_click(cx.listener(move |this, _, _, cx| { - this.shelves[shelf] = !this.shelves[shelf]; - cx.notify(); - })) - .into_any_element() - } - }) + .map(|&index| this.thread_item(&shell.rows[index], cx)) .collect() }), ) .track_scroll(&self.list) .flex_1() - .min_h_0(), + .min_h(px(ROW_HEIGHT)), ) + .child( + v_flex().gap_px().min_h_0().children( + shelves + .into_iter() + .map(|shelf| self.shelf(shelf, cx)) + .collect::>(), + ), + ) + .child(self.sidebar_footer(cx)) + } + + /// A sidebar row; picking it marks the open thread, and closes the overlay like the web. + fn thread_item(&self, row: &ThreadRow, cx: &mut Context) -> gpui::Stateful { + let selected = self.selected_thread.as_deref() == Some(row.id.as_str()); + let id = row.id.clone(); + let project = self.shell().and_then(|shell| { + shell + .project_entries + .iter() + .find(|e| e.contains(&row.project_id)) + }); + let provider = self + .providers + .as_ref() + .and_then(|catalog| catalog.instance(&row.provider_instance)); + thread_row( + row, + project, + provider, + selected, + self.sidebar_width.get(), + cx, + ) + .on_click(cx.listener(move |this, _, _, cx| { + this.selected_thread = Some(id.clone()); + this.sidebar_overlay_open = false; + cx.notify(); + })) + } + + /// One collapsible shelf: its header, then its rows while expanded. + fn shelf(&self, shelf: Shelf, cx: &mut Context) -> impl IntoElement + use<> { + let Shelf { + section, + count, + expanded, + } = shelf; + let index = shelf_index(section); + let (muted, hover) = (cx.theme().muted_foreground, cx.theme().list_hover); + let visible = self.shell().map_or(0, |shell| { + shelf_rows( + &shell.rows, + section, + expanded, + self.selected_thread.as_deref(), + ) + .len() + }); + v_flex() + .gap_px() + .min_h_0() + .child( + shelf_header(section, count, expanded, muted, hover).on_click(cx.listener( + move |this, _, _, cx| { + this.shelves[index] = !this.shelves[index]; + cx.notify(); + }, + )), + ) + .when(visible > 0, |shelf| { + shelf.child( + uniform_list( + ("shelf", index), + visible, + cx.processor(move |this, range: std::ops::Range, _, cx| { + let Some(shell) = this.shell() else { + return Vec::new(); + }; + let selected = this.selected_thread.as_deref(); + let rows = shelf_rows(&shell.rows, section, expanded, selected); + rows[range] + .iter() + .map(|&index| this.thread_item(&shell.rows[index], cx)) + .collect() + }), + ) + .h(px(visible.min(SHELF_VISIBLE_ROWS) as f32 * ROW_HEIGHT)) + .min_h_0(), + ) + }) + } + + /// The web's `SidebarChromeFooter`: utility buttons left, the update control right. + fn sidebar_footer(&self, cx: &App) -> impl IntoElement { + h_flex() + .pt_1() + .gap_px() + .child(footer_button( + "settings", + Icon::new(IconName::Settings), + "Settings", + false, + cx, + )) + .child(footer_button( + "pull-requests", + Icon::new(Icons::GitPullRequest), + "Pull requests", + false, + cx, + )) + .child(footer_button( + "usage", + Icon::new(Icons::ChartNoAxesColumn), + "Usage", + false, + cx, + )) + .child(div().flex_1()) + .child(footer_button( + "update", + Icon::new(Icons::CircleArrowUp), + "Update available", + true, + cx, + )) + } + + /// The web's `SidebarThreadHeader`: search, then the project filter, add project and new thread. + fn sidebar_header(&self, window: &Window, cx: &mut Context) -> impl IntoElement { + use gpui_kit_assets::IconName as Icons; + let has_projects = self + .shell() + .is_some_and(|shell| !shell.project_entries.is_empty()); + let new_thread = Kbd::format(&Keystroke::parse("cmd-n").expect("valid keystroke")); + let focused = self.search.focus_handle(cx).is_focused(window); + let (hover, ring) = (cx.theme().list_hover, cx.theme().ring.opacity(0.7)); + h_flex() + .gap_1() + .child( + // The web's unstyled search: only hover and focus paint the row. + h_flex() + .flex_1() + .min_w_0() + .h_8() + .px_2() + .gap_2() + .rounded(px(6.)) + .border_1() + .border_color(gpui::transparent_black()) + .text_color(cx.theme().muted_foreground) + .hover(move |style| style.bg(hover)) + .when(focused, |row| row.bg(hover).border_color(ring)) + .child(Icon::new(IconName::Search).size_4()) + .child( + div() + .flex_1() + .min_w_0() + .font_weight(FontWeight::MEDIUM) + .child(Input::new(&self.search).appearance(false).small()), + ), + ) + .when(has_projects, |row| { + row.child(header_icon_button( + "filter-projects", + Icons::ListFilter, + "Filter threads by project".into(), + cx, + )) + .child(header_icon_button( + "add-project", + Icons::FolderPlus, + "Add project".into(), + cx, + )) + }) + .child(header_icon_button( + "new-thread", + Icons::SquarePen, + format!("New thread ({new_thread})").into(), + cx, + )) } fn main_area(&self, cx: &mut Context) -> AnyElement { @@ -197,16 +720,6 @@ impl MainView { .flex_1() .min_w_0() .h_full() - .child( - h_flex() - .flex_shrink_0() - .h(px(40.)) - .px_4() - .items_center() - .border_b_1() - .border_color(cx.theme().border) - .child(self.breadcrumb(active, cx)), - ) .child( centered(cx) .gap_6() @@ -217,7 +730,13 @@ impl MainView { projects::with_remote_members(&shell.project_entries, &self.environments), cx, )) - .child(self.composer_box(cx)), + .child( + v_flex() + .w(px(720.)) + .max_w_full() + .child(self.composer_box(cx)) + .children(active.and_then(|entry| self.context_strip(entry, cx))), + ), ) .into_any_element() } @@ -262,7 +781,12 @@ impl MainView { .gap(px(6.)) .min_w_0() .text_color(muted) + .cursor_pointer() .hover(move |style| style.text_color(foreground)) + .tooltip(tooltip(format!( + "New thread in {}", + project_label(Some(entry)) + ))) .child(icon) .child( div() @@ -391,9 +915,7 @@ impl MainView { .children(environment_badge(index, entry, muted)) .on_click(move |_, _, cx| { view.update(cx, |this, cx| { - this.selected_project = Some(id.clone()); - this.picker_open = false; - cx.notify(); + this.select_project(id.clone(), cx); }); }) }); @@ -467,9 +989,7 @@ impl MainView { .on_click(move |_, _, cx| { let Some(id) = scratch_id.clone() else { return }; link_view.update(cx, |this, cx| { - this.selected_project = Some(id); - this.picker_open = false; - cx.notify(); + this.select_project(id, cx); }); }) .child("or start without a project") @@ -487,47 +1007,453 @@ impl MainView { ) } + /// The web's `ComposerSurface.ContextStrip` under the composer; none for the scratch project. + fn context_strip(&self, project: &ProjectEntry, cx: &App) -> Option { + let Status::Connected { label, machine, .. } = &self.status else { + return None; + }; + if project.scratch { + return None; + } + let defaults = self.providers.as_ref().map_or_else( + || Defaults::resolve(None, None, None), + |c| c.defaults(Some(&project.id)), + ); + let environment = strip::environment_label(label); + let branch = self + .branches + .get(&project.root) + .and_then(Option::as_ref) + .map(|branch| { + let label = branch.label(defaults.env_mode, defaults.start_from_origin); + // The branch is the one label that gives way in a narrow window. + strip_control("branch", Icons::GitBranch, label.clone(), label, cx) + .flex_shrink(1.) + .min_w_0() + }); + let is_repo = branch.is_some(); + let workspace = defaults.env_mode; + let runtime_mode = self.runtime_mode.unwrap_or(defaults.runtime_mode); + Some( + h_flex() + .w_full() + .items_center() + .gap_1() + .px_2() + .pt_2() + .child(strip_control( + "host", + machine_icon(*machine), + environment.clone(), + strip::tooltip_text(&environment, Some(HOST_SHORTCUT)), + cx, + )) + .children(is_repo.then(|| strip_separator(cx))) + .children(is_repo.then(|| { + strip_control( + "workspace", + workspace_icon(workspace), + workspace.label().to_owned(), + strip::tooltip_text(workspace.label(), Some(WORKSPACE_SHORTCUT)), + cx, + ) + })) + .child(div().flex_1()) + .children(branch) + .child(self.permissions_popover(runtime_mode, cx)), + ) + } + + /// The web's `ComposerContextPermissions` menu: opens above the chip, aligned to its end. + fn permissions_popover(&self, mode: RuntimeMode, cx: &App) -> impl IntoElement { + let view = self.permissions.read(cx).view.clone(); + let panel = self.permissions.clone(); + let content_panel = panel.clone(); + Popover::new("permissions") + .anchor(Anchor::BottomRight) + .open(self.permissions_open) + .p_0() + .on_open_change(move |open, _, cx| { + panel.update(cx, |panel, _| panel.preview = None); + view.update(cx, |this, cx| { + this.permissions_open = *open; + cx.notify(); + }) + .ok(); + }) + .trigger(PermissionsTrigger { mode, open: false }) + .content(move |_, _, cx| { + content_panel.update(cx, |panel, _| panel.selected = mode); + content_panel.clone() + }) + } + + fn open_permissions(&mut self, cx: &mut Context) { + let strip = self.shell().is_some_and(|shell| { + active_project(self.selected_project.as_deref(), shell).is_some_and(|e| !e.scratch) + }); + self.permissions_open = strip; + cx.notify(); + } + fn composer_box(&self, cx: &mut Context) -> impl IntoElement { + let trigger = self.providers.as_ref().and_then(|catalog| { + let project = self.selected_project.as_deref(); + catalog.trigger(project.filter(|id| !id.is_empty())) + }); v_flex() .w(px(720.)) .max_w_full() - .p_3() - .gap_2() - .rounded_xl() + .p_px() + .rounded(px(16.)) .border_1() - .border_color(cx.theme().border) + // The web's dark `--chat-composer-outline`. + .border_color(gpui::white().opacity(0.05)) .bg(cx.theme().secondary) .child( - Textarea::new(&self.composer) - .appearance(false) - .bordered(false), + // The web's `px-4 pt-4 pb-2` body; the negative margin cancels the textarea's own padding. + div().px_4().pt_4().pb_2().child( + Textarea::new(&self.composer) + .appearance(false) + .bordered(false) + .mx(px(-10.)) + .my(px(-8.)) + // The web's `leading-relaxed` at 14px. + .line_height(px(22.75)), + ), ) .child( h_flex() + .px_2() + .pb_2() .justify_between() + .child(header_icon_button( + "attach", + gpui_kit_assets::IconName::Paperclip, + "Attach files".into(), + cx, + )) .child( - div() - .id("attach-tooltip") - .tooltip(tooltip("Attach files")) + h_flex() + .gap_2() + .children(trigger.map(|trigger| model_trigger(&trigger, cx))) .child( - Button::new("attach") - .ghost() - .small() - .icon(gpui_kit_assets::IconName::Paperclip) - .on_click(|_, _, _| {}), + send_button( + !self.composer.read(cx).value().trim().is_empty(), + self.send_hovered, + cx, + ) + .on_hover(cx.listener( + |this, hovered: &bool, _, cx| { + this.send_hovered = *hovered; + cx.notify(); + }, + )), ), + ), + ) + } +} + +/// The web's `ComposerControl` at `xs`: a ghost button with a 12px icon and a 12px label. +fn strip_control( + id: &'static str, + icon: Icons, + label: String, + tip: String, + cx: &App, +) -> gpui::Stateful { + let theme = cx.theme(); + let (foreground, muted) = (theme.foreground, theme.muted_foreground); + div() + .id(id) + .flex() + .flex_shrink_0() + .items_center() + .gap(px(6.)) + .h(px(24.)) + .px(px(8.)) + .rounded(px(8.)) + .text_xs() + .text_color(muted) + .cursor_pointer() + .hover(move |style| style.bg(foreground.opacity(0.06)).text_color(foreground)) + .tooltip(tooltip(tip)) + .child(Icon::new(icon).size(px(12.))) + .child( + div() + .max_w(px(240.)) + .min_w_0() + .overflow_hidden() + .whitespace_nowrap() + .text_ellipsis() + .child(label), + ) +} + +/// The permissions chip as a popover trigger. +#[derive(IntoElement)] +struct PermissionsTrigger { + mode: RuntimeMode, + open: bool, +} + +impl Selectable for PermissionsTrigger { + fn selected(self, _: bool) -> Self { + self + } + + fn is_selected(&self) -> bool { + false + } + + fn open(mut self, open: bool) -> Self { + self.open = open; + self + } + + fn is_open(&self) -> bool { + self.open + } +} + +impl RenderOnce for PermissionsTrigger { + fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { + let tip = if self.open { + String::new() + } else { + strip::tooltip_text("Permissions", Some(PERMISSIONS_SHORTCUT)) + }; + let control = strip_control( + "permissions", + runtime_icon(self.mode), + self.mode.label().to_owned(), + tip, + cx, + ); + let foreground = cx.theme().foreground; + control.when(self.open, move |control| { + control.bg(foreground.opacity(0.06)).text_color(foreground) + }) + } +} + +/// The web's `ComposerPermissionsSection`: four icon segments, and the shown mode named below. +struct PermissionsPanel { + view: gpui::WeakEntity, + selected: RuntimeMode, + preview: Option, +} + +impl Render for PermissionsPanel { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let theme = cx.theme(); + let (foreground, muted) = (theme.foreground, theme.muted_foreground); + let (track, selected_bg, hover_bg) = ( + theme.input.opacity(0.4), + theme.input.opacity(0.72), + theme.accent, + ); + let (shown, is_selected) = strip::previewed(self.selected, self.preview); + let last = RuntimeMode::ALL.len() - 1; + let segments = RuntimeMode::ALL + .into_iter() + .enumerate() + .map(|(index, mode)| { + let chosen = mode == self.selected; + div() + .id(("runtime-mode", index)) + .flex() + .flex_1() + .h_6() + .items_center() + .justify_center() + .when(index == 0, |segment| segment.rounded_l_md()) + .when(index == last, |segment| segment.rounded_r_md()) + .cursor_pointer() + .when(chosen, |segment| { + segment.bg(selected_bg).text_color(foreground) + }) + .when(!chosen, |segment| { + segment + .text_color(muted) + .hover(move |style| style.bg(hover_bg).text_color(foreground)) + }) + .on_hover(cx.listener(move |this, hovered: &bool, _, cx| { + if *hovered { + this.preview = Some(mode); + } else if this.preview == Some(mode) { + this.preview = None; + } + cx.notify(); + })) + .on_click(cx.listener(move |this, _, _, cx| { + this.selected = mode; + this.preview = None; + this.view + .update(cx, |view, cx| { + view.runtime_mode = Some(mode); + view.permissions_open = false; + cx.notify(); + }) + .ok(); + cx.notify(); + })) + .child(Icon::new(runtime_icon(mode)).size(px(14.))) + }); + v_flex() + .w(px(288.)) + .gap(px(6.)) + .px_2() + .py(px(6.)) + .child( + div() + .text_xs() + .font_weight(FontWeight::MEDIUM) + .text_color(muted) + .child("Permissions"), + ) + .child(h_flex().rounded_lg().bg(track).p(px(2.)).children(segments)) + .child( + v_flex() + .gap(px(2.)) + .child( + div() + .text_sm() + .text_color(if is_selected { foreground } else { muted }) + .child(shown.label()), ) .child( - Button::new("send") - .primary() - .rounded_full() - .icon(IconName::ArrowUp) - .on_click(|_, _, _| {}), + div() + .min_h_8() + .text_xs() + .line_height(px(16.)) + .text_color(muted) + .child(shown.description()), ), ) } } +/// The web's `Separator` between the environment and the workspace: `mx-0.5 h-3.5`. +fn strip_separator(cx: &App) -> gpui::Div { + div() + .flex_shrink_0() + .mx(px(2.)) + .w(px(1.)) + .h(px(14.)) + .bg(cx.theme().border) +} + +fn workspace_icon(mode: EnvMode) -> Icons { + match mode { + EnvMode::Local => Icons::Folder, + EnvMode::Worktree => Icons::FolderGit2, + } +} + +/// `runtimeModeConfig`'s icons. +fn runtime_icon(mode: RuntimeMode) -> Icons { + match mode { + RuntimeMode::ApprovalRequired => Icons::Lock, + RuntimeMode::AutoAcceptEdits => Icons::PenLine, + RuntimeMode::Auto => Icons::Sparkles, + RuntimeMode::FullAccess => Icons::ShieldOff, + } +} + +/// The web's `ProviderModelPicker` trigger, a `ComposerControl` at `sm`. Clicking does nothing yet. +fn model_trigger(trigger: &Trigger, cx: &App) -> gpui::Stateful { + let theme = cx.theme(); + let (foreground, muted) = (theme.foreground, theme.muted_foreground); + div() + .id("model-trigger") + .group("model-trigger") + .flex() + .flex_shrink_0() + .items_center() + .gap(px(6.)) + .h(px(28.)) + .px_2() + .rounded(px(8.)) + .text_sm() + .font_weight(FontWeight::MEDIUM) + .text_color(foreground.opacity(0.8)) + .cursor_pointer() + .hover(move |style| style.bg(foreground.opacity(0.06)).text_color(foreground)) + .tooltip(tooltip(trigger.title.clone())) + .on_click(|_, _, _| {}) + .child(provider_mark(trigger, cx)) + .child( + div() + .max_w(px(224.)) + .overflow_hidden() + .whitespace_nowrap() + .text_ellipsis() + .child(trigger.model.clone()), + ) + .children(trigger.effort.clone().map(|effort| { + div() + .ml(px(-2.)) + .max_w(px(96.)) + .overflow_hidden() + .whitespace_nowrap() + .text_ellipsis() + .text_color(muted) + .group_hover("model-trigger", move |style| style.text_color(foreground)) + .child(effort) + })) +} + +/// The web's `ProviderInstanceIcon` at `size-4`: the driver's art, or its initials, with the +/// instance chip at the bottom-right. +fn provider_mark(trigger: &Trigger, cx: &App) -> gpui::Div { + let theme = cx.theme(); + let art = match provider_icon(&trigger.driver, theme.is_dark()) { + Some(path) => img(path).size_full().into_any_element(), + None => div() + .size_full() + .flex() + .items_center() + .justify_center() + .text_size(px(10.)) + .font_weight(FontWeight::SEMIBOLD) + .child(crate::providers::initials(&trigger.display_name)) + .into_any_element(), + }; + div() + .relative() + .flex_shrink_0() + .size_4() + .child(art) + .children(trigger.badge.as_ref().map(|badge| { + let (bg, fg) = match badge.accent { + Some((bg, fg)) => (rgb(bg).into(), rgb(fg).into()), + None => (theme.background, theme.muted_foreground), + }; + div() + .absolute() + .right(px(-2.)) + .bottom(px(-2.)) + .h(px(12.)) + .min_w(px(12.)) + .px(px(2.)) + .flex() + .items_center() + .justify_center() + .rounded_full() + .border_1() + .border_color(theme.secondary) + .shadow_sm() + .bg(bg) + .text_color(fg) + .text_size(px(7.)) + .line_height(px(7.)) + .font_weight(FontWeight::SEMIBOLD) + .child(badge.initials.clone()) + })) +} + /// The project name as plain heading text that underlines on hover and while its picker is open. /// Under a scratch draft it is the `InlineButton` `picker` tone at `text-sm`, `font-medium`. #[derive(IntoElement)] @@ -624,6 +1550,181 @@ fn tooltip( } } +/// The web's `SidebarHeaderIconButton`: a 28px ghost button whose icon turns to the foreground on hover. +fn header_icon_button( + id: &'static str, + icon: gpui_kit_assets::IconName, + label: gpui::SharedString, + cx: &App, +) -> impl IntoElement + use<> { + ghost_icon_button(id, Icon::new(icon).size_4(), label, true, cx) +} + +/// The top bar's `icon-sm` buttons: `ghost`, or `media-navigation` on the stage art. +/// Disabled ones stay dim and ignore the pointer. +fn nav_button( + id: &'static str, + icon: IconName, + label: gpui::SharedString, + enabled: bool, + on_backdrop: bool, + cx: &App, +) -> gpui::Stateful { + let icon = Icon::new(icon).size(px(14.)); + if !on_backdrop { + return ghost_icon_button(id, icon, label, enabled, cx); + } + let white = gpui::white(); + icon_button( + id, + icon, + label, + enabled, + white.opacity(0.9), + white.opacity(0.1), + white, + ) +} + +fn ghost_icon_button( + id: &'static str, + icon: Icon, + label: gpui::SharedString, + enabled: bool, + cx: &App, +) -> gpui::Stateful { + let theme = cx.theme(); + // Translucent like the web's `--accent`, so it shows on the composer as well as the sidebar. + let (idle, hover) = (theme.muted_foreground, theme.foreground.opacity(0.06)); + icon_button(id, icon, label, enabled, idle, hover, theme.foreground) +} + +fn icon_button( + id: &'static str, + icon: Icon, + label: gpui::SharedString, + enabled: bool, + idle: Hsla, + hover_bg: Hsla, + hover_fg: Hsla, +) -> gpui::Stateful { + div() + .id(id) + .flex_shrink_0() + .size(px(28.)) + .flex() + .items_center() + .justify_center() + .rounded(px(6.)) + .text_color(idle) + .when(enabled, |button| { + button + .cursor_pointer() + .hover(move |style| style.bg(hover_bg).text_color(hover_fg)) + .tooltip(tooltip(label)) + }) + // The web's `disabled:opacity-64`. + .when(!enabled, |button| button.opacity(0.64)) + .child(icon) +} + +/// The sidebar footer's `size-8` menu button, `rounded-full` for the update control. +fn footer_button( + id: &'static str, + icon: Icon, + label: &'static str, + round: bool, + cx: &App, +) -> gpui::Stateful { + let theme = cx.theme(); + let (idle, hover_bg, hover_fg) = (theme.muted_foreground, theme.list_hover, theme.foreground); + div() + .id(id) + .flex_shrink_0() + .size_8() + .flex() + .items_center() + .justify_center() + .when(round, |button| button.rounded_full()) + .when(!round, |button| button.rounded(px(6.))) + .text_color(idle) + .cursor_pointer() + .hover(move |style| style.bg(hover_bg).text_color(hover_fg)) + .tooltip(tooltip(label)) + .child(icon.size_4()) +} + +/// The web's `ComposerPrimaryActions` send button: stage art in dev builds, else `message-action`. +fn send_button(enabled: bool, hovered: bool, cx: &App) -> gpui::Stateful { + let theme = cx.theme(); + let (bg, hover_bg, fg) = if STAGE_BACKDROP { + ( + gpui::transparent_black(), + gpui::white().opacity(0.1), + gpui::white(), + ) + } else if enabled { + (theme.primary, theme.primary_hover, theme.primary_foreground) + } else { + ( + theme.foreground.opacity(0.12), + theme.foreground.opacity(0.12), + theme.muted_foreground, + ) + }; + // The web's `hover:scale-105` over 150ms: the circle grows 5% inside a fixed slot. + let grown = enabled && hovered; + div() + .id("send") + .group("send") + .relative() + .flex_shrink_0() + .size(px(32.)) + .when(enabled, |slot| { + slot.cursor_pointer().tooltip(tooltip("Submit message")) + }) + .child( + div() + .absolute() + .inset_0() + .rounded_full() + .bg(bg) + .when(STAGE_BACKDROP && !enabled, |circle| circle.opacity(0.64)) + .when(STAGE_BACKDROP, |circle| { + circle.child( + img(STAGE_DEV_BUTTON) + .absolute() + .size_full() + // `overflow_hidden` does not round-clip images. + .rounded_full() + .object_fit(ObjectFit::Cover), + ) + }) + .child( + div() + .absolute() + .size_full() + .rounded_full() + .flex() + .items_center() + .justify_center() + .text_color(fg) + .when(enabled, |face| { + face.group_hover("send", move |style| style.bg(hover_bg)) + }) + .child(Icon::new(IconName::ArrowUp).size_4()), + ) + .with_animation( + ("send-grow", usize::from(grown)), + Animation::new(Duration::from_millis(150)).with_easing(ease_in_out), + move |circle, delta| { + let inset = px(-0.8) * if grown { delta } else { 1. - delta }; + circle.top(inset).left(inset).right(inset).bottom(inset) + }, + ), + ) +} + fn machine_icon(kind: MachineKind) -> gpui_kit_assets::IconName { use gpui_kit_assets::IconName as Icons; match kind { @@ -747,27 +1848,18 @@ fn centered(cx: &Context) -> gpui::Div { .text_color(cx.theme().foreground) } -fn status_tag(status: ThreadStatus) -> Option { - let (tag, label) = match status { - ThreadStatus::Running | ThreadStatus::Starting => (Tag::info(), "Running"), - ThreadStatus::Waiting => (Tag::warning(), "Waiting"), - ThreadStatus::Queued | ThreadStatus::Preparing => (Tag::secondary(), "Queued"), - ThreadStatus::Failed => (Tag::danger(), "Failed"), - _ => return None, - }; - Some(tag.small().child(label)) -} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum SidebarItem { - Row(usize), - Header { - section: Section, - count: usize, - expanded: bool, - }, +struct Shelf { + section: Section, + count: usize, + expanded: bool, } +/// Rows a shelf shows before it scrolls. +const SHELF_VISIBLE_ROWS: usize = 6; +/// The web's shelf header and footer button size (`h-8`, `size-8`). +const SHELF_HEADER_HEIGHT: f32 = 32.; + fn shelf_index(section: Section) -> usize { match section { Section::Working => 0, @@ -776,40 +1868,48 @@ fn shelf_index(section: Section) -> usize { } } -/// The web's default list: pinned and active rows, then a header per shelf (Working and -/// Snoozed only when non-empty, Settled always) with its rows only while expanded. -fn sidebar_items(rows: &[ThreadRow], expanded: [bool; 3]) -> Vec { +/// The pinned and active rows, listed above the shelves. +fn top_rows(rows: &[ThreadRow]) -> Vec { + let listed = |row: &ThreadRow| matches!(row.section, Section::Pinned | Section::Active); + (0..rows.len()) + .filter(|&index| listed(&rows[index])) + .collect() +} + +/// A shelf's rows: all while expanded, else only the open thread (`routeThreadOnly`). +fn shelf_rows( + rows: &[ThreadRow], + section: Section, + expanded: bool, + selected: Option<&str>, +) -> Vec { + (0..rows.len()) + .filter(|&index| { + let row = &rows[index]; + row.section == section && (expanded || selected == Some(row.id.as_str())) + }) + .collect() +} + +/// The web's shelves, bottom-anchored: Working and Snoozed only when non-empty, Settled always. +fn shelves(rows: &[ThreadRow], expanded: [bool; 3]) -> Vec { if rows.is_empty() { return Vec::new(); } - let mut items = Vec::with_capacity(rows.len() + 3); - for (index, row) in rows.iter().enumerate() { - if matches!(row.section, Section::Pinned | Section::Active) { - items.push(SidebarItem::Row(index)); - } - } - for section in [Section::Working, Section::Snoozed, Section::Settled] { - let count = rows.iter().filter(|row| row.section == section).count(); - if count == 0 && section != Section::Settled { - continue; - } - let expanded = expanded[shelf_index(section)]; - items.push(SidebarItem::Header { - section, - count, - expanded, - }); - if expanded { - let shelf = rows - .iter() - .enumerate() - .filter(|(_, row)| row.section == section); - items.extend(shelf.map(|(index, _)| SidebarItem::Row(index))); - } - } - items + [Section::Working, Section::Snoozed, Section::Settled] + .into_iter() + .filter_map(|section| { + let count = rows.iter().filter(|row| row.section == section).count(); + (count > 0 || section == Section::Settled).then(|| Shelf { + section, + count, + expanded: expanded[shelf_index(section)], + }) + }) + .collect() } +/// The web's `CollapsibleSectionHeader`: label left, then the count and a chevron right. fn shelf_header( section: Section, count: usize, @@ -829,83 +1929,352 @@ fn shelf_header( }; h_flex() .id(label) - .h(px(ROW_HEIGHT)) - .w_full() + .h(px(SHELF_HEADER_HEIGHT)) + .mx(px(2.)) .px_2() .gap_2() - .items_end() - .pb_2() .rounded_md() .text_xs() + .font_weight(FontWeight::MEDIUM) .text_color(muted) + .cursor_pointer() .hover(|style| style.bg(hover)) - .child(Icon::new(chevron).small()) .child(div().flex_1().child(label)) - .child(count.to_string()) + .child( + div() + .font_weight(FontWeight::NORMAL) + .child(count.to_string()), + ) + .child(Icon::new(chevron).size(px(12.))) } -fn thread_row(row: &ThreadRow, muted: Hsla, hover: Hsla) -> impl IntoElement + use<> { - v_flex() - .id(row.id.clone()) - .h(px(ROW_HEIGHT)) - .w_full() - .overflow_hidden() - .px_2() - .py_1() +/// The web's `text-info-foreground`, `text-error-foreground` and `text-warning-foreground`. +fn top_status_color(status: TopStatus, dark: bool) -> Option { + match (status, dark) { + (TopStatus::Waiting, _) => None, + (TopStatus::Working, true) => Some(0x60a5fa), + (TopStatus::Working, false) => Some(0x1d4ed8), + (TopStatus::Limited, true) => Some(0xfbbf24), + (TopStatus::Limited, false) => Some(0xb45309), + (TopStatus::Failed, true) => Some(0xf87171), + (TopStatus::Failed, false) => Some(0xb91c1c), + } +} + +fn top_status_icon(status: TopStatus) -> Icons { + match status { + TopStatus::Working => Icons::CircleDashed, + TopStatus::Waiting => Icons::CircleDotDashed, + TopStatus::Limited | TopStatus::Failed => Icons::CircleAlert, + } +} + +/// The project avatar at the favicon's `size-4`: the monogram on its tinted square. +fn project_badge(entry: Option<&ProjectEntry>, dark: bool, mono: gpui::SharedString) -> gpui::Div { + let badge = div().flex_shrink_0().size_4(); + let Some(entry) = entry else { + return badge; + }; + let color: Hsla = rgb(entry.color_hex(dark)).into(); + if entry.scratch { + return badge.child( + Icon::empty() + .path(MESSAGE_SQUARE_DASHED) + .size_4() + .text_color(color), + ); + } + badge + .flex() + .items_center() .justify_center() + .rounded(px(4.)) + .bg(color.opacity(0.14)) + .text_color(color) + .text_size(px(8.25)) + .font_weight(FontWeight::BOLD) + .font_family(mono) + .child(entry.initials.clone()) +} + +/// The web's `PULL_REQUEST_STATE_PRESENTATION` tones. +fn pr_color(state: PrState, dark: bool) -> Option { + let (hex, alpha) = match (state, dark) { + (PrState::Pending, _) => return None, + (PrState::Open, false) => (0x059669, 1.), + (PrState::Open, true) => (0x6ee7b7, 0.9), + (PrState::Draft, false) => (0x71717a, 1.), + (PrState::Draft, true) => (0xa1a1aa, 0.8), + (PrState::Closed, false) => (0xdc2626, 1.), + (PrState::Closed, true) => (0xfca5a5, 0.9), + (PrState::Merged, false) => (0x7c3aed, 1.), + (PrState::Merged, true) => (0xc4b5fd, 0.9), + }; + Some(Hsla::from(rgb(hex)).opacity(alpha)) +} + +fn pr_icon(badge: &PrBadge) -> Icons { + match (badge.stack, badge.state) { + (true, _) => Icons::Layers, + (false, PrState::Draft) => Icons::GitPullRequestDraft, + (false, PrState::Closed) => Icons::GitPullRequestClosed, + (false, PrState::Merged) => Icons::GitMerge, + (false, _) => Icons::GitPullRequestArrow, + } +} + +/// The web's `MiddleTruncate` with a character budget in place of measured text. +fn middle_truncate(text: &str, budget: usize) -> String { + let chars: Vec = text.chars().collect(); + if chars.len() <= budget || budget < 3 { + return text.to_owned(); + } + let keep = budget - 1; + let (head, tail) = (keep.div_ceil(2), keep / 2); + let start: String = chars[..head].iter().collect(); + let end: String = chars[chars.len() - tail..].iter().collect(); + format!("{start}\u{2026}{end}") +} + +/// How many `text-xs` characters fit the branch: the card's width less its padding, icons and +/// the pull request badge. +fn branch_budget( + sidebar_width: f32, + worktree: bool, + pr: Option<&PrBadge>, + provider: bool, +) -> usize { + const CHAR_WIDTH: f32 = 6.4; + const GAP: f32 = 6.; + // Sidebar padding and the card's horizontal padding. + let mut room = sidebar_width - 16. - 20.; + if worktree { + room -= 14. + GAP; + } + if provider { + room -= 16. + GAP; + } + if let Some(pr) = pr { + room -= 12. + 4. + pr.text.chars().count() as f32 * CHAR_WIDTH + GAP; + } + (room / CHAR_WIDTH).max(0.) as usize +} + +fn card_action( + id: &'static str, + label: &'static str, + muted: Hsla, + foreground: Hsla, +) -> gpui::Stateful { + h_flex() + .id(id) + .h_full() + .gap_1() + .px(px(6.)) .rounded_md() - .hover(|style| style.bg(hover)) + .text_xs() + .text_color(muted) + .cursor_pointer() + .hover(move |style| style.text_color(foreground)) + .tooltip(tooltip(label)) +} + +/// The group's label, else the project's own title. +fn project_name<'a>(row: &'a ThreadRow, entry: Option<&'a ProjectEntry>) -> &'a str { + entry.map_or(row.project.as_str(), |entry| entry.title.as_str()) +} + +/// The web's card `SidebarThreadRow`. Snooze and Settle appear on hover, as in the web, but do +/// nothing yet. +fn thread_row( + row: &ThreadRow, + project: Option<&ProjectEntry>, + provider: Option<(&str, String)>, + selected: bool, + sidebar_width: f32, + cx: &App, +) -> gpui::Stateful { + let theme = cx.theme(); + let dark = theme.is_dark(); + let (muted, foreground) = (theme.muted_foreground, theme.foreground); + let group = gpui::SharedString::from(format!("thread-{}", row.id)); + let name = project_name(row, project); + let status = row.top.map(|top| { + let color = top_status_color(top, dark).map_or(muted, |hex| rgb(hex).into()); + h_flex() + .gap_1() + .font_weight(FontWeight::MEDIUM) + .text_color(color) + .group_hover(group.clone(), |style| style.invisible()) + .child(Icon::new(top_status_icon(top)).size_4()) + .child(top.label()) + }); + let actions = h_flex() + .absolute() + .top_0() + .right_0() + .h_full() + .invisible() + .group_hover(group.clone(), |style| style.visible()) .child( - div() - .text_xs() - .text_color(muted) - .truncate() - .child(row.project.clone()), + card_action("snooze", "Snooze thread", muted, foreground) + .child(Icon::new(Icons::Clock).size(px(12.))), ) .child( - h_flex() - .gap_2() + card_action("settle", "Settle thread", muted, foreground) + .child(Icon::new(Icons::Check).size(px(14.))) + .child("Settle"), + ); + let provider = provider.map(|(driver, display_name)| { + let art = match provider_icon(driver, dark) { + Some(path) => img(path).size_full().into_any_element(), + None => div() + .size_full() + .flex() + .items_center() + .justify_center() + .text_size(px(10.)) + .font_weight(FontWeight::SEMIBOLD) + .child(crate::providers::initials(&display_name)) + .into_any_element(), + }; + div().flex_shrink_0().size_4().opacity(0.6).child(art) + }); + div() + .id(row.id.clone()) + .group(group) + .h(px(ROW_HEIGHT)) + .w_full() + .py(px(2.)) + .cursor_pointer() + .child( + v_flex() + .h(px(CARD_HEIGHT)) + .w_full() + .overflow_hidden() + .px(px(10.)) + .py_2() + .rounded_md() + .when(selected, |card| card.bg(theme.list_active)) + .when(!selected, |card| { + card.hover(|style| style.bg(theme.list_hover)) + }) + .child( + h_flex() + .h(px(20.)) + .flex_shrink_0() + .items_center() + .gap(px(6.)) + .child(project_badge(project, dark, theme.mono_font_family.clone())) + .child( + div() + .flex_1() + .min_w_0() + .truncate() + .text_xs() + .text_color(muted) + .child(name.to_owned()), + ) + .child( + div() + .relative() + .flex() + .flex_shrink_0() + .items_center() + .justify_end() + .h_full() + .min_w_8() + .text_xs() + .children(status) + .child(actions), + ), + ) .child( div() - .flex_1() - .min_w_0() - .text_sm() + .mt_1() + .h(px(20.)) + .flex_shrink_0() .truncate() + .text_sm() + .font_weight(FontWeight::MEDIUM) + .text_color(foreground) .child(row.title.clone()), ) - .children(status_tag(row.status).map(|tag| div().flex_shrink_0().child(tag))), + .child( + h_flex() + .mt(px(2.)) + .h(px(16.)) + .flex_shrink_0() + .items_center() + .gap(px(6.)) + .text_xs() + .text_color(muted) + .when(row.worktree, |line| { + line.child(Icon::new(Icons::FolderGit2).size(px(14.))) + }) + .child(div().flex_1().min_w_0().truncate().children( + row.branch.as_deref().map(|branch| { + middle_truncate( + branch, + branch_budget( + sidebar_width, + row.worktree, + row.pr.as_ref(), + provider.is_some(), + ), + ) + }), + )) + .children(row.pr.as_ref().map(|badge| { + let tone = pr_color(badge.state, dark).unwrap_or(muted); + h_flex() + .flex_shrink_0() + .gap_1() + .child(Icon::new(pr_icon(badge)).size(px(12.)).text_color(tone)) + .child(div().text_color(muted).child(badge.text.clone())) + })) + .children(provider), + ), ) - .children(row.branch.as_ref().map(|branch| { - div() - .text_xs() - .text_color(muted) - .truncate() - .child(branch.clone()) - })) } impl Render for MainView { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + let width = f32::from(window.viewport_size().width); + let narrow = is_narrow(width); + let docked = self.sidebar_open && !narrow; + let overlay = narrow && self.sidebar_overlay_open; v_flex() + .relative() .size_full() .bg(cx.theme().background) .text_color(cx.theme().foreground) - .child( - TitleBar::new().child("T3 Code").child( - div() - .ml_2() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(self.environment()), - ), + .when(overlay, |root| root.key_context(OVERLAY_CONTEXT)) + .on_action(cx.listener(|this, _: &ToggleSidebar, window, cx| { + this.toggle_sidebar(narrow_window(window), cx) + })) + .on_action( + cx.listener(|this, _: &DismissSidebar, _, cx| this.close_sidebar_overlay(cx)), ) + .on_action(cx.listener(|this, _: &OpenPermissions, _, cx| this.open_permissions(cx))) + .on_action(cx.listener(|this, _: &GoBack, _, cx| this.go_back(cx))) + .on_action(cx.listener(|this, _: &GoForward, _, cx| this.go_forward(cx))) + .when(docked, |root| { + root.child(self.sidebar_surface(SIDEBAR_WIDTH, cx)) + }) + .child(self.top_bar(narrow, cx)) .child( h_flex() .flex_1() .min_h_0() - .child(self.sidebar(cx)) + .when(docked, |row| { + row.child(self.sidebar(SIDEBAR_WIDTH, window, cx)) + }) .child(self.main_area(cx)), ) + .when(overlay, |root| { + root.child(self.sidebar_drawer(overlay_width(width), window, cx)) + }) } } @@ -913,6 +2282,22 @@ impl Render for MainView { mod tests { use super::*; + #[test] + fn the_sidebar_overlays_at_the_webs_breakpoint() { + assert!(is_narrow(767.)); + assert!(!is_narrow(768.)); + assert_eq!(overlay_width(583.), 448.); + assert_eq!(overlay_width(400.), 352.); + } + + #[test] + fn toggling_flips_only_the_state_for_the_viewport() { + assert_eq!(toggled(true, true, false), (true, true)); + assert_eq!(toggled(true, false, true), (false, false)); + assert_eq!(toggled(false, true, false), (false, false)); + assert_eq!(toggled(false, true, true), (false, true)); + } + fn entry(id: &str, scratch: bool) -> ProjectEntry { ProjectEntry { key: id.to_owned(), @@ -923,6 +2308,7 @@ mod tests { color: 0, pinned: false, scratch, + root: String::new(), logical_key: None, remote: Vec::new(), } @@ -935,13 +2321,42 @@ mod tests { project: project_id.to_owned(), title: String::new(), branch: None, - status: ThreadStatus::Idle, + worktree: false, + provider_instance: String::new(), + top: None, + pr: None, section: Section::Active, } } #[test] - fn sidebar_items_keep_shelves_collapsed_until_expanded() { + fn the_card_names_its_project_from_the_group_or_the_row() { + let mut thread = row("t"); + thread.project = "own".to_owned(); + assert_eq!(project_name(&thread, Some(&entry("group", false))), "group"); + assert_eq!(project_name(&thread, None), "own"); + } + + #[test] + fn long_branches_lose_their_middle() { + assert_eq!(middle_truncate("main", 10), "main"); + assert_eq!( + middle_truncate("yordis/t3/token-security-comprison", 12), + "yordis\u{2026}rison" + ); + assert_eq!(middle_truncate("abcdefgh", 2), "abcdefgh"); + let badge = PrBadge { + state: PrState::Open, + stack: false, + text: "19".to_owned(), + }; + let plain = branch_budget(300., true, None, true); + assert!(branch_budget(300., true, Some(&badge), true) < plain); + assert!(branch_budget(448., true, None, true) > plain); + } + + #[test] + fn shelves_list_only_what_the_web_does() { let in_section = |id: &str, section| ThreadRow { section, ..row(id) }; let rows = vec![ in_section("p", Section::Pinned), @@ -949,32 +2364,28 @@ mod tests { in_section("w", Section::Working), in_section("s", Section::Settled), ]; - let header = |section, count, expanded| SidebarItem::Header { + let shelf = |section, expanded| Shelf { section, - count, + count: 1, expanded, }; + assert_eq!(top_rows(&rows), [0, 1]); assert_eq!( - sidebar_items(&rows, [false; 3]), + shelves(&rows, [false; 3]), [ - SidebarItem::Row(0), - SidebarItem::Row(1), - header(Section::Working, 1, false), - header(Section::Settled, 1, false), + shelf(Section::Working, false), + shelf(Section::Settled, false) ] ); assert_eq!( - sidebar_items(&rows, [true, false, true]), - [ - SidebarItem::Row(0), - SidebarItem::Row(1), - header(Section::Working, 1, true), - SidebarItem::Row(2), - header(Section::Settled, 1, true), - SidebarItem::Row(3), - ] + shelves(&rows, [true, false, true]), + [shelf(Section::Working, true), shelf(Section::Settled, true)] ); - assert!(sidebar_items(&[], [true; 3]).is_empty()); + assert_eq!(shelf_rows(&rows, Section::Settled, true, None), [3]); + assert!(shelf_rows(&rows, Section::Settled, false, None).is_empty()); + assert!(shelf_rows(&rows, Section::Settled, false, Some("w")).is_empty()); + assert_eq!(shelf_rows(&rows, Section::Settled, false, Some("s")), [3]); + assert!(shelves(&[], [true; 3]).is_empty()); } #[test]