diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad3fbd75ff28..1ea17ac027d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,7 +95,7 @@ jobs: # play nicely with hermit-managed rust hermit uninstall rustup export CARGO_INCREMENTAL=0 - cargo clippy --all-targets -- -D warnings + cargo clippy --workspace --all-targets --exclude v8 -- -D warnings - name: Check for banned TLS crates run: ./scripts/check-no-native-tls.sh diff --git a/.gitignore b/.gitignore index 3704de6ad1eb..15fb5c318367 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ run_cli.sh tokenizer_files/ .DS_Store .idea +.vscode *.log tmp/ @@ -22,17 +23,24 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +# UI +node_modules +./ui/desktop/out + +# Generated goose DLLs (built at build time, not checked in) +ui/desktop/src/bin/goose_ffi.dll +ui/desktop/src/bin/goose_llm.dll + # Hermit .hermit/ # Claude -.claude/ +.claude debug_*.txt # Docs # Dependencies -/node_modules # Production /build @@ -46,9 +54,12 @@ benchmark-* benchconf.json scripts/fake.sh do_not_version/ +/ui/desktop/src/bin/temporal /temporal-service/temporal.db +/ui/desktop/src/bin/temporal.db /temporal.db - +/ui/desktop/src/bin/goose-scheduler-executor +/ui/desktop/src/bin/goose /.env /working_dir @@ -66,6 +77,3 @@ result # Goose self-test artifacts gooseselftest/ .tasks/ - -**/tests/**/*.txt -**/*tests*.new diff --git a/Cargo.lock b/Cargo.lock index 89ab7be37850..ffb1b5291878 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + [[package]] name = "aes" version = "0.8.4" @@ -27,12 +37,23 @@ checksum = "44bc1fef9c32f03bce2ab44af35b6f483bfd169bf55cc59beeb2e3b1a00ae4d1" dependencies = [ "anyhow", "derive_more", - "schemars", + "schemars 1.2.1", "serde", "serde_json", "strum", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -187,6 +208,45 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -209,6 +269,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -305,12 +376,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aws-lc-fips-sys" +version = "0.13.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bce4948d2520386c6d92a6ea2d472300257702242e5a1d01d6add52bd2e7c1" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "regex", +] + [[package]] name = "aws-lc-rs" version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" dependencies = [ + "aws-lc-fips-sys", "aws-lc-sys", "untrusted 0.7.1", "zeroize", @@ -322,6 +408,7 @@ version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" dependencies = [ + "bindgen", "cc", "cmake", "dunce", @@ -827,6 +914,18 @@ dependencies = [ "tower-service", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -871,8 +970,8 @@ dependencies = [ "encoding_rs", "flate2", "globset", - "indexmap", - "itertools", + "indexmap 2.13.0", + "itertools 0.14.0", "nu-ansi-term", "once_cell", "path_abs", @@ -905,6 +1004,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn 2.0.117", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -1140,6 +1259,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "cfb" version = "0.10.0" @@ -1196,6 +1324,18 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", ] [[package]] @@ -1433,6 +1573,26 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1650,6 +1810,18 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -1660,6 +1832,21 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto_secretbox" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1" +dependencies = [ + "aead", + "cipher", + "generic-array", + "poly1305", + "salsa20", + "subtle", + "zeroize", +] + [[package]] name = "ctor" version = "0.2.9" @@ -1670,6 +1857,43 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + [[package]] name = "darling" version = "0.21.3" @@ -1690,6 +1914,20 @@ dependencies = [ "darling_macro 0.23.0", ] +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + [[package]] name = "darling_core" version = "0.21.3" @@ -1717,6 +1955,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.117", +] + [[package]] name = "darling_macro" version = "0.21.3" @@ -1799,6 +2048,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" +[[package]] +name = "decoded-char" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5440d1dc8ea7cae44cda3c64568db29bfa2434aba51ae66a50c00488841a65a3" + [[package]] name = "der" version = "0.7.10" @@ -1806,10 +2061,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", + "der_derive", + "flagset", "pem-rfc7468", "zeroize", ] +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "deranged" version = "0.5.8" @@ -1817,6 +2099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", + "serde_core", ] [[package]] @@ -1830,6 +2113,37 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -1991,40 +2305,100 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.15.0" +name = "ecdsa" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "serde", + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] -name = "email_address" -version = "0.2.9" +name = "ed25519" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "serde", + "pkcs8", + "signature", ] [[package]] -name = "encode_unicode" -version = "1.0.0" +name = "ed25519-dalek" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" dependencies = [ - "cfg-if", + "serde", ] [[package]] -name = "endian-type" +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" @@ -2035,6 +2409,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcb71a32ab9582e2756554e84b24aee90d7187034049c35921ec3296b70c13ad" +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "equivalent" version = "1.0.2" @@ -2183,6 +2563,22 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filetime" version = "0.2.27" @@ -2206,6 +2602,12 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + [[package]] name = "flate2" version = "1.1.9" @@ -2465,6 +2867,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2517,6 +2920,18 @@ dependencies = [ "wasip3", ] +[[package]] +name = "getset" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "gif" version = "0.13.3" @@ -2537,6 +2952,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "globset" version = "0.4.18" @@ -2550,6 +2971,17 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.27" @@ -2562,7 +2994,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -2581,7 +3013,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -2599,6 +3031,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -2717,6 +3158,15 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-auth" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" +dependencies = [ + "memchr", +] + [[package]] name = "http-body" version = "0.4.6" @@ -3083,6 +3533,17 @@ dependencies = [ "quote", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -3180,6 +3641,24 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +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" @@ -3336,6 +3815,37 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-number" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479dfd2ad8e4b4ae076b031f72ef2f3791f65e2a0f51e5f3408dbf716c4c2f82" +dependencies = [ + "lexical", + "ryu-js", + "serde", + "smallvec", +] + +[[package]] +name = "json-syntax" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "044a68aba3f96d712f492b72be25e10f96201eaaca3207a7d6e68d6d5105fda9" +dependencies = [ + "decoded-char", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "json-number", + "locspan", + "locspan-derive", + "ryu-js", + "serde", + "smallstr", + "smallvec", + "utf8-decode", +] + [[package]] name = "json5" version = "0.4.1" @@ -3363,7 +3873,7 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1b46a0365a611fbf1d2143104dcf910aada96fafd295bab16c60b802bf6fa1d" dependencies = [ - "ahash", + "ahash 0.8.12", "base64 0.22.1", "bytecount", "email_address", @@ -3401,6 +3911,21 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "jwt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +dependencies = [ + "base64 0.13.1", + "crypto-common", + "digest", + "hmac", + "serde", + "serde_json", + "sha2", +] + [[package]] name = "keyring" version = "3.6.3" @@ -3431,7 +3956,7 @@ name = "leaf" version = "1.28.0" dependencies = [ "agent-client-protocol-schema", - "ahash", + "ahash 0.8.12", "anyhow", "async-stream", "async-trait", @@ -3455,7 +3980,7 @@ dependencies = [ "futures", "ignore", "include_dir", - "indexmap", + "indexmap 2.13.0", "indoc", "insta", "jsonschema", @@ -3482,7 +4007,7 @@ dependencies = [ "reqwest 0.13.2", "rmcp 1.2.0", "sacp", - "schemars", + "schemars 1.2.1", "serde", "serde_json", "serde_urlencoded", @@ -3523,7 +4048,7 @@ dependencies = [ "uuid", "v_htmlescape", "webbrowser", - "which", + "which 8.0.2", "winapi", "wiremock", "zip 0.6.6", @@ -3549,7 +4074,7 @@ dependencies = [ "regex", "rmcp 1.2.0", "sacp", - "schemars", + "schemars 1.2.1", "serde", "serde_json", "strum", @@ -3606,7 +4131,9 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "sha2", "shlex", + "sigstore-verification", "strum", "tar", "tempfile", @@ -3639,7 +4166,7 @@ dependencies = [ "once_cell", "reqwest 0.13.2", "rmcp 1.2.0", - "schemars", + "schemars 1.2.1", "serde", "serde_json", "shell-words", @@ -3730,6 +4257,72 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" +[[package]] +name = "lexical" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc8a009b2ff1f419ccc62706f04fe0ca6e67b37460513964a3dfdb919bb37d6" +dependencies = [ + "lexical-core", +] + +[[package]] +name = "lexical-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" +dependencies = [ + "lexical-parse-integer", + "lexical-util", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "lexical-util" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" + +[[package]] +name = "lexical-write-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361" +dependencies = [ + "lexical-util", + "lexical-write-integer", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df" +dependencies = [ + "lexical-util", +] + [[package]] name = "libc" version = "0.2.183" @@ -3746,6 +4339,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" version = "0.2.16" @@ -3802,6 +4405,24 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "locspan" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33890449fcfac88e94352092944bf321f55e5deb4e289a6f51c87c55731200a0" + +[[package]] +name = "locspan-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88991223b049a3d29ca1f60c05639581336a0f3ee4bf8a659dddecc11c4961a" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "log" version = "0.4.29" @@ -3821,12 +4442,12 @@ dependencies = [ "ecb", "encoding_rs", "flate2", - "indexmap", + "indexmap 2.13.0", "itoa", "jiff", "log", "md-5", - "nom", + "nom 8.0.0", "nom_locate", "rand 0.9.2", "rangemap", @@ -3923,6 +4544,12 @@ dependencies = [ "serde", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3980,6 +4607,12 @@ dependencies = [ "pxfm", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + [[package]] name = "nanoid" version = "0.4.0" @@ -4028,6 +4661,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nom" version = "8.0.0" @@ -4045,7 +4688,7 @@ checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d" dependencies = [ "bytecount", "memchr", - "nom", + "nom 8.0.0", ] [[package]] @@ -4225,6 +4868,69 @@ dependencies = [ "objc2", ] +[[package]] +name = "oci-client" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b74df13319e08bc386d333d3dc289c774c88cc543cae31f5347db07b5ec2172" +dependencies = [ + "bytes", + "chrono", + "futures-util", + "http 1.4.0", + "http-auth", + "jwt", + "lazy_static", + "oci-spec", + "olpc-cjson", + "regex", + "reqwest 0.12.28", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tokio", + "tracing", + "unicase", +] + +[[package]] +name = "oci-spec" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc3da52b83ce3258fbf29f66ac784b279453c2ac3c22c5805371b921ede0d308" +dependencies = [ + "const_format", + "derive_builder", + "getset", + "regex", + "serde", + "serde_json", + "strum", + "strum_macros", + "thiserror 2.0.18", +] + +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "olpc-cjson" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696183c9b5fe81a7715d074fd632e8bd46f4ccc0231a3ed7fc580a80de5f7083" +dependencies = [ + "serde", + "serde_json", + "unicode-normalization", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -4259,6 +4965,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "open" version = "5.3.3" @@ -4270,6 +4982,37 @@ dependencies = [ "pathdiff", ] +[[package]] +name = "openidconnect" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c6709ba2ea764bbed26bce1adf3c10517113ddea6f2d4196e4851757ef2b2" +dependencies = [ + "base64 0.21.7", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http 1.4.0", + "itertools 0.10.5", + "log", + "oauth2", + "p256", + "p384", + "rand 0.8.5", + "rsa", + "serde", + "serde-value", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror 1.0.69", + "url", +] + [[package]] name = "openssl" version = "0.10.76" @@ -4370,7 +5113,7 @@ dependencies = [ "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", - "prost", + "prost 0.14.3", "reqwest 0.13.2", "thiserror 2.0.18", ] @@ -4382,7 +5125,7 @@ source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a# dependencies = [ "opentelemetry", "opentelemetry_sdk", - "prost", + "prost 0.14.3", "tonic", "tonic-prost", ] @@ -4419,6 +5162,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-multimap" version = "0.7.3" @@ -4435,6 +5187,30 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking" version = "2.2.1" @@ -4475,6 +5251,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "pastey" version = "0.2.1" @@ -4504,10 +5291,20 @@ checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest", "hmac", - "password-hash", + "password-hash 0.4.2", "sha2", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "pem" version = "3.0.6" @@ -4576,6 +5373,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap 2.13.0", +] + [[package]] name = "pin-project" version = "1.1.11" @@ -4619,6 +5426,21 @@ dependencies = [ "spki", ] +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes", + "cbc", + "der", + "pbkdf2 0.12.2", + "scrypt", + "sha2", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -4626,6 +5448,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", + "pkcs5", + "rand_core 0.6.4", "spki", ] @@ -4648,7 +5472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap", + "indexmap 2.13.0", "quick-xml 0.38.4", "serde", "time", @@ -4680,6 +5504,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -4755,6 +5590,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -4779,6 +5623,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -4795,13 +5661,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55" dependencies = [ "futures", - "indexmap", + "indexmap 2.13.0", "nix 0.31.2", "tokio", "tracing", "windows 0.62.2", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive 0.13.5", +] + [[package]] name = "prost" version = "0.14.3" @@ -4809,7 +5685,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.14.3", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.13.5", + "prost-types", + "regex", + "syn 2.0.117", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -4819,12 +5728,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-reflect" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5edd582b62f5cde844716e66d92565d7faf7ab1445c8cebce6e00fba83ddb2" +dependencies = [ + "base64 0.22.1", + "once_cell", + "prost 0.13.5", + "prost-reflect-derive 0.14.0", + "prost-types", + "serde", + "serde-value", +] + +[[package]] +name = "prost-reflect" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37587d5a8a1b3dc9863403d084fc2254b91ab75a702207098837950767e2260b" +dependencies = [ + "prost 0.13.5", + "prost-reflect-derive 0.15.1", + "prost-types", +] + +[[package]] +name = "prost-reflect-build" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8db7191445b1dbee19df4f6b6294e5123aef52620b344a630bb845d302622a" +dependencies = [ + "prost-build", + "prost-reflect 0.15.3", +] + +[[package]] +name = "prost-reflect-derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fce6b22f15cc8d8d400a2b98ad29202b33bd56c7d9ddd815bc803a807ecb65" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-reflect-derive" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab076798900edeaf1499ed1c30097db86e6697c5d02660a63d72fe4ebdcfefd2" +dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost 0.13.5", +] + [[package]] name = "psl-types" version = "2.0.11" @@ -5168,7 +6144,7 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8eff4fa778b5c2a57e85c5f2fe3a709c52f0e60d23146e2151cbef5893f420e" dependencies = [ - "ahash", + "ahash 0.8.12", "fluent-uri", "once_cell", "parking_lot", @@ -5230,6 +6206,7 @@ dependencies = [ "hyper-util", "js-sys", "log", + "mime_guess", "percent-encoding", "pin-project-lite", "quinn", @@ -5241,12 +6218,14 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls 0.26.4", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams 0.4.2", "web-sys", "webpki-roots 1.0.6", ] @@ -5295,10 +6274,20 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.5.0", "web-sys", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rgb" version = "0.8.53" @@ -5335,7 +6324,7 @@ dependencies = [ "pastey", "pin-project-lite", "rmcp-macros 0.12.0", - "schemars", + "schemars 1.2.1", "serde", "serde_json", "thiserror 2.0.18", @@ -5365,7 +6354,7 @@ dependencies = [ "rand 0.10.0", "reqwest 0.13.2", "rmcp-macros 1.2.0", - "schemars", + "schemars 1.2.1", "serde", "serde_json", "sse-stream", @@ -5476,6 +6465,15 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "rustix" version = "1.1.4" @@ -5622,6 +6620,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "ryu-js" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f" + [[package]] name = "sacp" version = "10.1.0" @@ -5637,7 +6641,7 @@ dependencies = [ "jsonrpcmsg", "rmcp 0.12.0", "sacp-derive", - "schemars", + "schemars 1.2.1", "serde", "serde_json", "thiserror 2.0.18", @@ -5658,6 +6662,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -5680,9 +6693,21 @@ dependencies = [ name = "schannel" version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" dependencies = [ - "windows-sys 0.61.2", + "dyn-clone", + "ref-cast", + "serde", + "serde_json", ] [[package]] @@ -5717,6 +6742,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash 0.5.0", + "pbkdf2 0.12.2", + "salsa20", + "sha2", +] + [[package]] name = "sct" version = "0.7.1" @@ -5733,6 +6770,20 @@ version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -5803,6 +6854,16 @@ dependencies = [ "typeid", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -5840,7 +6901,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap", + "indexmap 2.13.0", "itoa", "memchr", "serde", @@ -5859,6 +6920,26 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "serde_spanned" version = "1.0.4" @@ -5886,8 +6967,17 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.1", "serde_core", + "serde_json", "serde_with_macros", + "time", ] [[package]] @@ -5908,7 +6998,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap", + "indexmap 2.13.0", "itoa", "ryu", "serde", @@ -6013,6 +7103,116 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "sigstore" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43427f0d642cfed11bd596608148ee4476dd75f938888aa13a9c4e176fe14225" +dependencies = [ + "async-trait", + "aws-lc-rs", + "base64 0.22.1", + "cfg-if", + "chrono", + "const-oid", + "crypto_secretbox", + "digest", + "ecdsa", + "ed25519", + "ed25519-dalek", + "elliptic-curve", + "futures", + "futures-util", + "hex", + "json-syntax", + "oci-client", + "olpc-cjson", + "openidconnect", + "p256", + "p384", + "pem", + "pkcs1", + "pkcs8", + "rand 0.8.5", + "regex", + "reqwest 0.12.28", + "ring", + "rsa", + "rustls-pki-types", + "rustls-webpki 0.103.9", + "scrypt", + "serde", + "serde_json", + "serde_repr", + "serde_with", + "sha2", + "signature", + "sigstore_protobuf_specs", + "thiserror 2.0.18", + "tls_codec", + "tokio", + "tokio-util", + "tough", + "tracing", + "url", + "webbrowser", + "x509-cert", + "zeroize", +] + +[[package]] +name = "sigstore-protobuf-specs-derive" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80baa401f274093f7bb27d7a69d6139cbc11f1b97624e9a61a9b3ea32c776a35" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sigstore-verification" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db10507b7d2ff109e56bfd885ce7b489a16040996516020ecfb43ced017a7a47" +dependencies = [ + "async-trait", + "base64 0.22.1", + "ed25519-dalek", + "hex", + "log", + "p256", + "p384", + "reqwest 0.12.28", + "serde", + "serde_json", + "sha2", + "signature", + "sigstore", + "thiserror 2.0.18", + "tokio", + "x509-parser", +] + +[[package]] +name = "sigstore_protobuf_specs" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e5ed827a6d8d2be7fc598515d061b59d85f496d7066152822a80f3250af74" +dependencies = [ + "anyhow", + "glob", + "prost 0.13.5", + "prost-build", + "prost-reflect 0.14.7", + "prost-reflect-build", + "prost-types", + "serde", + "serde_json", + "sigstore-protobuf-specs-derive", + "which 7.0.3", +] + [[package]] name = "simd-adler32" version = "0.3.8" @@ -6059,6 +7259,16 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "smallstr" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862077b1e764f04c251fe82a2ef562fd78d7cadaeb072ca7c2bcaf7217b1ff3b" +dependencies = [ + "serde", + "smallvec", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -6074,6 +7284,29 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "snafu" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" +dependencies = [ + "futures-core", + "pin-project", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "socket2" version = "0.5.10" @@ -6145,7 +7378,7 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink", - "indexmap", + "indexmap 2.13.0", "log", "memchr", "once_cell", @@ -6389,6 +7622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", + "quote", "unicode-ident", ] @@ -6739,6 +7973,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tokio" version = "1.50.0" @@ -6850,7 +8105,7 @@ version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap", + "indexmap 2.13.0", "serde_core", "serde_spanned", "toml_datetime", @@ -6911,10 +8166,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" dependencies = [ "bytes", - "prost", + "prost 0.14.3", "tonic", ] +[[package]] +name = "tough" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88d0ee9525696569cc2af5d46f8a739028c0268895071e0386957195b0c9161" +dependencies = [ + "async-recursion", + "async-trait", + "aws-lc-rs", + "bytes", + "chrono", + "dyn-clone", + "futures", + "futures-core", + "globset", + "hex", + "log", + "olpc-cjson", + "pem", + "percent-encoding", + "reqwest 0.12.28", + "rustls 0.23.37", + "serde", + "serde_json", + "serde_plain", + "snafu", + "tempfile", + "tokio", + "tokio-util", + "typed-path 0.9.3", + "untrusted 0.7.1", + "url", + "walkdir", +] + [[package]] name = "tower" version = "0.5.3" @@ -7217,6 +8507,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typed-path" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82205ffd44a9697e34fc145491aa47310f9871540bb7909eaa9365e0a9a46607" + [[package]] name = "typed-path" version = "0.12.3" @@ -7248,7 +8544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "408c7e039c96ec1d517a1111ade7fadab889f32c096dac691a1e3b8018c3e39a" dependencies = [ "aes", - "ahash", + "ahash 0.8.12", "base64 0.22.1", "byteorder", "cbc", @@ -7333,6 +8629,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -7376,6 +8682,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8-decode" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca61eb27fa339aa08826a29f03e87b99b4d8f0fc2255306fd266bb1b6a9de498" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -7394,7 +8706,7 @@ version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" dependencies = [ - "indexmap", + "indexmap 2.13.0", "serde", "serde_json", "utoipa-gen", @@ -7590,11 +8902,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.13.0", "wasm-encoder", "wasmparser", ] +[[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-streams" version = "0.5.0" @@ -7616,7 +8941,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "semver", ] @@ -7689,6 +9014,18 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + [[package]] name = "which" version = "8.0.2" @@ -8229,6 +9566,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wiremock" version = "0.6.5" @@ -8280,7 +9623,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.13.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -8311,7 +9654,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.0", - "indexmap", + "indexmap 2.13.0", "log", "serde", "serde_derive", @@ -8330,7 +9673,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.13.0", "log", "semver", "serde", @@ -8346,6 +9689,37 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "sha1", + "signature", + "spki", + "tls_codec", +] + +[[package]] +name = "x509-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + [[package]] name = "xattr" version = "1.6.1" @@ -8525,7 +9899,7 @@ dependencies = [ "crossbeam-utils", "flate2", "hmac", - "pbkdf2", + "pbkdf2 0.11.0", "sha1", "time", "zstd 0.11.2+zstd.1.5.2", @@ -8542,7 +9916,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap", + "indexmap 2.13.0", "memchr", "thiserror 2.0.18", "zopfli", @@ -8556,9 +9930,9 @@ checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004" dependencies = [ "crc32fast", "flate2", - "indexmap", + "indexmap 2.13.0", "memchr", - "typed-path", + "typed-path 0.12.3", "zopfli", ] diff --git a/README.md b/README.md index cd2dc4e5c43b..f388136cd3b3 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,138 @@
-# goose +# Leaf -_a local, extensible, open source AI agent that automates engineering tasks_ +_a pure CLI AI agent forked from Goose, focused on automation through the command line_

- Discord - CI + CI

-goose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_. +Leaf is a **pure CLI AI agent** forked from the [Goose](https://github.com/block/goose) project by Block. While Goose provides both desktop and CLI interfaces, Leaf focuses exclusively on command-line automation for developers who prefer terminal-based workflows. -Whether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision. +## Key Differences from Goose -Designed for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation. +- **Pure CLI**: No desktop UI or Electron components - just a fast, terminal-based experience +- **Lightweight**: Removed V8 dependencies and UI components for a smaller footprint +- **Agent Protocol (ACP)**: Full support for Agent Client Protocol for multi-agent workflows +- **MCP Integration**: Seamless integration with Model Context Protocol (MCP) servers +- **Multi-Model**: Works with any LLM (OpenAI, Anthropic, local models, etc.) -[![Watch the video](https://github.com/user-attachments/assets/ddc71240-3928-41b5-8210-626dfb28af7a)](https://youtu.be/D-DpDunrbpo) +## What Leaf Can Do -# Quick Links -- [Quickstart](https://block.github.io/goose/docs/quickstart) -- [Installation](https://block.github.io/goose/docs/getting-started/installation) -- [Tutorials](https://block.github.io/goose/docs/category/tutorials) -- [Documentation](https://block.github.io/goose/docs/category/getting-started) -- [Governance](https://github.com/block/goose/blob/main/GOVERNANCE.md) -- [Custom Distributions](https://github.com/block/goose/blob/main/CUSTOM_DISTROS.md) - build your own goose distro with preconfigured providers, extensions, and branding +Leaf is your on-machine AI agent, capable of automating complex development tasks: -## Need Help? -- [Diagnostics & Reporting](https://block.github.io/goose/docs/troubleshooting/diagnostics-and-reporting) -- [Known Issues](https://block.github.io/goose/docs/troubleshooting/known-issues) +- **Code Generation**: Build projects from scratch, write and refactor code +- **Debugging**: Analyze errors and fix issues autonomously +- **Workflow Automation**: Execute complex multi-step engineering pipelines +- **External Integrations**: Interact with APIs and external services via MCP +- **Orchestration**: Delegate tasks to subagents with independent contexts -# a little goose humor 🪿 +## Quick Start -> Why did the developer choose goose as their AI agent? -> -> Because it always helps them "migrate" their code to production! 🚀 +### Installation -# goose around with us -- [Discord](https://discord.gg/goose-oss) -- [YouTube](https://www.youtube.com/@goose-oss) -- [LinkedIn](https://www.linkedin.com/company/goose-oss) -- [Twitter/X](https://x.com/goose_oss) -- [Bluesky](https://bsky.app/profile/opensource.block.xyz) -- [Nostr](https://njump.me/opensource@block.xyz) +```bash +# Build from source +cargo build --release --package leaf-cli + +# The binary will be at: +./target/release/leaf +``` + +### Setup + +```bash +# Configure your provider +leaf configure + +# Start an interactive session +leaf session + +# Or run a recipe +leaf run --recipe my-recipe.yaml +``` + +### Example Usage + +```bash +# Ask Leaf to help with a task +leaf session + +# Run with a specific provider +leaf session --provider openai + +# Use recipes for repeatable workflows +leaf run --recipe deploy.yaml +``` + +## Architecture + +``` +crates/ +├── leaf # Core agent logic +├── leaf-acp # Agent Client Protocol implementation +├── leaf-cli # Command-line interface +├── leaf-mcp # Model Context Protocol extensions +└── leaf-server # ACP server (leafd) +``` + +## Project Status + +Leaf is a community fork focused on CLI-only workflows. It maintains compatibility with: +- **MCP Servers**: All standard Model Context Protocol servers +- **Recipes**: YAML-based automation workflows +- **Extensions**: Dynamic tool loading via MCP + +## Documentation + +- [Getting Started](#getting-started) - Installation and first steps +- [Configuration](#configuration) - Setting up providers and extensions +- [Recipes](#recipes) - Automation workflows +- [MCP Extensions](#mcp-extensions) - Available tools and integrations + +## Development + +```bash +# Setup +source bin/activate-hermit + +# Build +cargo build --release + +# Test +cargo test --package leaf-cli + +# Lint +cargo clippy --all-targets -- -D warnings +cargo fmt +``` + +## Contributing + +We welcome contributions! Please: +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Run tests and linting +5. Submit a PR + +## License + +Apache License 2.0 - see [LICENSE](LICENSE) file. + +## Acknowledgments + +Leaf is a fork of [Goose](https://github.com/block/goose) by Block. We're grateful for the solid foundation they built. + +--- + +
+ +**Pure CLI. Maximum Control. No UI Overhead.** + +
diff --git a/crates/leaf-acp/src/transport.rs b/crates/leaf-acp/src/transport.rs index 632f540bb8e4..130b08e92216 100644 --- a/crates/leaf-acp/src/transport.rs +++ b/crates/leaf-acp/src/transport.rs @@ -114,6 +114,7 @@ pub fn create_router(server: Arc) -> Router { Router::new() .route("/health", get(health)) + .route("/status", get(health)) .route( "/acp", post(http::handle_post).with_state(http_state.clone()), diff --git a/crates/leaf-acp/tests/test_data/openai_basic.txt b/crates/leaf-acp/tests/test_data/openai_basic.txt new file mode 100644 index 000000000000..4c3d0c69a071 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_basic.txt @@ -0,0 +1,9 @@ +data: {"id":"chatcmpl-test","object":"chat.completion.chunk","created":1766229303,"model":"gpt-5-nano","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]} + +data: {"id":"chatcmpl-test","object":"chat.completion.chunk","created":1766229303,"model":"gpt-5-nano","choices":[{"index":0,"delta":{"content":"2"},"finish_reason":null}]} + +data: {"id":"chatcmpl-test","object":"chat.completion.chunk","created":1766229303,"model":"gpt-5-nano","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-test","object":"chat.completion.chunk","created":1766229303,"model":"gpt-5-nano","choices":[],"usage":{"prompt_tokens":100,"completion_tokens":10,"total_tokens":110}} + +data: [DONE] diff --git a/crates/leaf-acp/tests/test_data/openai_builtin_execute.txt b/crates/leaf-acp/tests/test_data/openai_builtin_execute.txt new file mode 100644 index 000000000000..bf6d35bd7935 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_builtin_execute.txt @@ -0,0 +1,511 @@ +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_HCUq7OYIqj233H77wpqAtSGP","type":"function","function":{"name":"code_execution__execute","arguments":""}}],"refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"XbIx"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"0WAOew1EJA"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"PdQalDVBc"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"SUSOxe2S"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"async"}}]},"finish_reason":null}],"usage":null,"obfuscation":"lGNpX0hf"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" function"}}]},"finish_reason":null}],"usage":null,"obfuscation":"1wCB"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" run"}}]},"finish_reason":null}],"usage":null,"obfuscation":"LNRcwubzP"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"()"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Aq7vRtDvlF4"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" {\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"lb2NCrdyI"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ErsVOWq6Z3Xu"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"qNMOJVTHLUp4"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" //"}}]},"finish_reason":null}],"usage":null,"obfuscation":"6n2KlzcsTp"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Step"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Syxh8KgO"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"LVrPhgFXXORS"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"1"}}]},"finish_reason":null}],"usage":null,"obfuscation":"HJcsbkgldODt"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"meGjHjoA1xft"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Get"}}]},"finish_reason":null}],"usage":null,"obfuscation":"6ue6xAtTS"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"tFmcmr9A"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" from"}}]},"finish_reason":null}],"usage":null,"obfuscation":"YlOG2Pln"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" MCP"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Uxh9l06s6"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" fixture"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Pzqu4"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"kOYakk7vCs"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"Ew0I4Yv7lsZu"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" const"}}]},"finish_reason":null}],"usage":null,"obfuscation":"BwnGXcl"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"DYK3JLN8"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7T6NXmfl7g"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ="}}]},"finish_reason":null}],"usage":null,"obfuscation":"rYrRL7FpIpl"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" await"}}]},"finish_reason":null}],"usage":null,"obfuscation":"aKhInCt"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Mc"}}]},"finish_reason":null}],"usage":null,"obfuscation":"TiUrBUJ3S8"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"p"}}]},"finish_reason":null}],"usage":null,"obfuscation":"rUBZNYrMu5QX"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Fixture"}}]},"finish_reason":null}],"usage":null,"obfuscation":"JAVgEJ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".get"}}]},"finish_reason":null}],"usage":null,"obfuscation":"9SzQLFQkS"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"35KBxpEwO"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"({"}}]},"finish_reason":null}],"usage":null,"obfuscation":"JrhE1JHlm54"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"});"}}]},"finish_reason":null}],"usage":null,"obfuscation":"wohXvQg7Ob"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"lVazpoueAIq"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"j5Kk4o8z8KVq"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"1GWCRFnUwLOm"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" let"}}]},"finish_reason":null}],"usage":null,"obfuscation":"QfhWFYeVO"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"y2sgZpV8"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ="}}]},"finish_reason":null}],"usage":null,"obfuscation":"CSJpY7ykRgo"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ''"}}]},"finish_reason":null}],"usage":null,"obfuscation":"d8X4iNvkfT"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":";\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"SHsYhZpXT5"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"eaM94gaV3SjW"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"oBdwnsKEn6Pe"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" if"}}]},"finish_reason":null}],"usage":null,"obfuscation":"MOdNd1VIdT"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ("}}]},"finish_reason":null}],"usage":null,"obfuscation":"LaNZf51HyNY"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"zpRHAvyvj"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"RiV1yS7ugy"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" !="}}]},"finish_reason":null}],"usage":null,"obfuscation":"4UaTQI4oY6"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" null"}}]},"finish_reason":null}],"usage":null,"obfuscation":"CksUZb6e"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":")"}}]},"finish_reason":null}],"usage":null,"obfuscation":"du3S3fRcNGFJ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" {\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"peNFo8PaO"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"y3HGwbdgGGN2"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"2yRkpk4hHq"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" if"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Fqx4l55lFk"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ("}}]},"finish_reason":null}],"usage":null,"obfuscation":"R3Nn65bbShk"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"typeof"}}]},"finish_reason":null}],"usage":null,"obfuscation":"KNuxb26"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"y3VY7fiz"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"GzFgdFnlEk"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ==="}}]},"finish_reason":null}],"usage":null,"obfuscation":"QZYQNItUp"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" '"}}]},"finish_reason":null}],"usage":null,"obfuscation":"BxFluNL3LiS"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"string"}}]},"finish_reason":null}],"usage":null,"obfuscation":"EYS4Szt"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"')"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ld3QawCCj8j"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" {\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"AhXpCgGSW"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"HR4oVYvrKIp0"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"GRH0AMvt"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"b22NAPWU"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ="}}]},"finish_reason":null}],"usage":null,"obfuscation":"jXZz9g9tus1"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"EzPoaNwG"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"dNp3Z4YURy"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":";\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"SFijuwxEh0"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ta4OC28eSsDQ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"KmW5WLpYfl"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" }"}}]},"finish_reason":null}],"usage":null,"obfuscation":"67HBmLCOU18"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" else"}}]},"finish_reason":null}],"usage":null,"obfuscation":"SWcwdCbt"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" if"}}]},"finish_reason":null}],"usage":null,"obfuscation":"H6DUToAyQ4"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ("}}]},"finish_reason":null}],"usage":null,"obfuscation":"quSQs2d3Leu"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"typeof"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Yth9o7r"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"4VQXKZnb"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Z3wtnBjMxE"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ==="}}]},"finish_reason":null}],"usage":null,"obfuscation":"0TPK99588"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" '"}}]},"finish_reason":null}],"usage":null,"obfuscation":"WY5WPQzxzYm"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"object"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7uUOdgI"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"')"}}]},"finish_reason":null}],"usage":null,"obfuscation":"HvcmQAC33mi"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" {\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"gZ29HXtRa"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"2BVcAHxoKTBJ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"8ZgPj7ct"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ibY235FW"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ="}}]},"finish_reason":null}],"usage":null,"obfuscation":"ZioVR8c4ry5"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"uw5DCPUr"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"easTNJ4HTi"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"2f996DsH"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ??"}}]},"finish_reason":null}],"usage":null,"obfuscation":"antE6IcDfK"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Tb6mqsJT"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"F9SNOWaJ4s"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".content"}}]},"finish_reason":null}],"usage":null,"obfuscation":"AcXac"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ??"}}]},"finish_reason":null}],"usage":null,"obfuscation":"1AJwttqKUC"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"vKazIIc2"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"digOEQ8L8K"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".output"}}]},"finish_reason":null}],"usage":null,"obfuscation":"buFsGY"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ??"}}]},"finish_reason":null}],"usage":null,"obfuscation":"0teoOOb2Ps"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ''"}}]},"finish_reason":null}],"usage":null,"obfuscation":"d8wkQgBHJX"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":";\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7ZG1t8y03p"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"pyBBgSFqywgy"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"j9yOeuhITh"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" }\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"KPPU04Hw7"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"5LX55vkRR8kW"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"PxYDQQzmC8oa"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" }\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ZhtYKzY50"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"lTheErkcOz2I"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"1NujUoinnFdC"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" if"}}]},"finish_reason":null}],"usage":null,"obfuscation":"LnpOMGO1gA"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" (!"}}]},"finish_reason":null}],"usage":null,"obfuscation":"PG0Ukg0lIG"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"seyEOZxMx"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":")"}}]},"finish_reason":null}],"usage":null,"obfuscation":"8HgFZ5FIEiyd"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" {\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"pe3d5DOxz"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"pO4Zjn4nYjUN"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"PwrHo1elqK"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"j5wyQvGu"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ="}}]},"finish_reason":null}],"usage":null,"obfuscation":"ZbcOhXhUldx"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" '//"}}]},"finish_reason":null}],"usage":null,"obfuscation":"quGPXmBjM"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" no"}}]},"finish_reason":null}],"usage":null,"obfuscation":"IAwIEo6A6a"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"h5ERm6Sh"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" returned"}}]},"finish_reason":null}],"usage":null,"obfuscation":"WOaC"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" by"}}]},"finish_reason":null}],"usage":null,"obfuscation":"5gcyoClkTY"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Mc"}}]},"finish_reason":null}],"usage":null,"obfuscation":"0zteWyELKW"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"p"}}]},"finish_reason":null}],"usage":null,"obfuscation":"B5cHXJmChBms"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Fixture"}}]},"finish_reason":null}],"usage":null,"obfuscation":"0Xe1QR"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".get"}}]},"finish_reason":null}],"usage":null,"obfuscation":"2vHYUwQDp"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"9JwUpRtnt"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"()"}}]},"finish_reason":null}],"usage":null,"obfuscation":"1zjaqiXZl2f"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"';"}}]},"finish_reason":null}],"usage":null,"obfuscation":"bYFdw3hFiAw"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"3GhBmzN37pV"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"e5RqErjJCYzD"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"vkgozuoVy8n6"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" }\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"4JNc8Okf2"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"eEpcildyEGUi"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"kZJv2yzDAT"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"NJaDVEBJleAZ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" //"}}]},"finish_reason":null}],"usage":null,"obfuscation":"AkzTH2plV3"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Step"}}]},"finish_reason":null}],"usage":null,"obfuscation":"VGDFoeiS"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"NdzacxSwRwkc"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"2"}}]},"finish_reason":null}],"usage":null,"obfuscation":"dO0VtCqWvSue"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"3EmacbMeJWDm"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"UB5FQGQ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" the"}}]},"finish_reason":null}],"usage":null,"obfuscation":"4KMEtFkPe"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" retrieved"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7mp"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"xFHKGtd8"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" to"}}]},"finish_reason":null}],"usage":null,"obfuscation":"XWtWyabOHo"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" /"}}]},"finish_reason":null}],"usage":null,"obfuscation":"GI413xIkvDP"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tmp"}}]},"finish_reason":null}],"usage":null,"obfuscation":"8Hwwo1OP5F"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"/result"}}]},"finish_reason":null}],"usage":null,"obfuscation":"vr2pwB"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".txt"}}]},"finish_reason":null}],"usage":null,"obfuscation":"WcJiQyK6D"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"cR0x5ORkfO"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"g4ZejTLDefCP"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" const"}}]},"finish_reason":null}],"usage":null,"obfuscation":"LKWKME4"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"i4nxJ43"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"hVAkN7PbyW"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ="}}]},"finish_reason":null}],"usage":null,"obfuscation":"ilkxXcex9UU"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" await"}}]},"finish_reason":null}],"usage":null,"obfuscation":"cYjPDlD"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Developer"}}]},"finish_reason":null}],"usage":null,"obfuscation":"G6t"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"."}}]},"finish_reason":null}],"usage":null,"obfuscation":"OOxdzNJq"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"MiMZRWA"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"({"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7sQdVn1KZH3"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"L6lFRm1PNqG"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Su0Qw704RLRe"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"rkfEW2QLpO"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" path"}}]},"finish_reason":null}],"usage":null,"obfuscation":"jGD2QcOd"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ne7pCHdniACu"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" '/"}}]},"finish_reason":null}],"usage":null,"obfuscation":"A7rPtw8xwm"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tmp"}}]},"finish_reason":null}],"usage":null,"obfuscation":"scv4frvmOL"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"/result"}}]},"finish_reason":null}],"usage":null,"obfuscation":"2eELSy"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".txt"}}]},"finish_reason":null}],"usage":null,"obfuscation":"1KSuOUume"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"',"}}]},"finish_reason":null}],"usage":null,"obfuscation":"2bfPK90ZMWR"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"GSiHQg8iM2E"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"56PdyUx8eBvQ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"XurvUHlgwc"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" // command"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ZsYLy"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"PFlue8D49Rzx"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" '"}}]},"finish_reason":null}],"usage":null,"obfuscation":"JUShQEjjAxm"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"MB5hRzt5"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"',"}}]},"finish_reason":null}],"usage":null,"obfuscation":"unqKUoz76aS"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"P39riGC0HKA"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"sJxnV6swTye6"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"xVJI6wFQLA"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" content"}}]},"finish_reason":null}],"usage":null,"obfuscation":"aYkuCMJQ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":""}}]},"finish_reason":null}],"usage":null,"obfuscation":"DQ5IKXUC"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"YaxTVILdGh6I"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"tTzYfOHr"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":",\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"EKsCaMI5g9"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"kKCqlBzwhCq8"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"KLo6QPM5VKk0"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" });"}}]},"finish_reason":null}],"usage":null,"obfuscation":"DNqh4g1Na"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"u1iriNCPeXk"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"PRv5bSbXOVAK"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"zBm488IA2h"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" "}}]},"finish_reason":null}],"usage":null,"obfuscation":"5BmN6XMBvJK8"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" return"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Jzvgzr"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" {"}}]},"finish_reason":null}],"usage":null,"obfuscation":"dqpuXK9vmsI"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"0QRLI8kv"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":","}}]},"finish_reason":null}],"usage":null,"obfuscation":"1GL4TASp6ipg"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Czgd0iL"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Res"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Bv304pNXQg"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" }"}}]},"finish_reason":null}],"usage":null,"obfuscation":"fZGuBvkzNzz"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":";\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ManURNiZBB"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"hN2gPjbJkn1b"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}\\"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Hke7vUJzSM"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"n"}}]},"finish_reason":null}],"usage":null,"obfuscation":"naUuxvnxWvXs"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"HYQQNm55"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tool"}}]},"finish_reason":null}],"usage":null,"obfuscation":"lmbBNO1kI"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_graph"}}]},"finish_reason":null}],"usage":null,"obfuscation":"qdrnpGB"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":["}}]},"finish_reason":null}],"usage":null,"obfuscation":"OYQjmFkz5"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"7yVjHnHF8g"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tool"}}]},"finish_reason":null}],"usage":null,"obfuscation":"QEEhae2TJ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"kEukkQGI"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"M"}}]},"finish_reason":null}],"usage":null,"obfuscation":"LYxfiGayx20v"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"cp"}}]},"finish_reason":null}],"usage":null,"obfuscation":"zEf2SuFI3cH"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Fixture"}}]},"finish_reason":null}],"usage":null,"obfuscation":"S2GG1i"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".get"}}]},"finish_reason":null}],"usage":null,"obfuscation":"likS7Y45F"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"TwySl39OJ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"2p2DjdVv"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"description"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Zk"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"ZBeDt6sM"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Get"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7bjcsaItXQ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"LtDIcbNq"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"A1kFK8sA"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"depends"}}]},"finish_reason":null}],"usage":null,"obfuscation":"AyIA9i"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_on"}}]},"finish_reason":null}],"usage":null,"obfuscation":"9F4q7ARQQd"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":[]"}}]},"finish_reason":null}],"usage":null,"obfuscation":"jAVbSgQF"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"},{\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"XUbEQMwJ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tool"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Eehl1Y6Xt"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"T3iH8KKU"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Developer"}}]},"finish_reason":null}],"usage":null,"obfuscation":"jzRU"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"."}}]},"finish_reason":null}],"usage":null,"obfuscation":"zeeCDR1q"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"8YZ1VtI"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"R15EQTSl"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"description"}}]},"finish_reason":null}],"usage":null,"obfuscation":"v8"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"uCS5hMDz"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"b4P9og82"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" code"}}]},"finish_reason":null}],"usage":null,"obfuscation":"CkksRy3s"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" to"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7z4KadqiCw"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" /"}}]},"finish_reason":null}],"usage":null,"obfuscation":"XYeWQ4H9N5X"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tmp"}}]},"finish_reason":null}],"usage":null,"obfuscation":"qADuBhZQgw"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"/result"}}]},"finish_reason":null}],"usage":null,"obfuscation":"0tqRXZ"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".txt"}}]},"finish_reason":null}],"usage":null,"obfuscation":"59jHzUi0h"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"QRyYho94"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"depends"}}]},"finish_reason":null}],"usage":null,"obfuscation":"61NYkx"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_on"}}]},"finish_reason":null}],"usage":null,"obfuscation":"WHWBsd5Jkg"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":["}}]},"finish_reason":null}],"usage":null,"obfuscation":"xyMuvvQ1H"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"0"}}]},"finish_reason":null}],"usage":null,"obfuscation":"NkL223rTveiY"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"]}"}}]},"finish_reason":null}],"usage":null,"obfuscation":"5FvupEDfMHe"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"]}"}}]},"finish_reason":null}],"usage":null,"obfuscation":"uDYSO0oHbmz"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"9sRJO4xoviH"} + +data: {"id":"chatcmpl-D64NZp69RkEyXdUoDaCBj7fSYll8J","object":"chat.completion.chunk","created":1770339173,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":5657,"completion_tokens":1488,"total_tokens":7145,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":1216,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"6YeROQTGvStbj"} + +data: [DONE] diff --git a/crates/leaf-acp/tests/test_data/openai_builtin_final.txt b/crates/leaf-acp/tests/test_data/openai_builtin_final.txt new file mode 100644 index 000000000000..45e52a3113ee --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_builtin_final.txt @@ -0,0 +1,167 @@ +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"aQKzd"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"##"},"finish_reason":null}],"usage":null,"obfuscation":"0Wf0H"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Done"},"finish_reason":null}],"usage":null,"obfuscation":"AQ"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"0hF"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"zu5Ssj"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Retrieved"},"finish_reason":null}],"usage":null,"obfuscation":"qz8LIGnHhsu2z"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" code"},"finish_reason":null}],"usage":null,"obfuscation":"rd"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" using"},"finish_reason":null}],"usage":null,"obfuscation":"I"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Mc"},"finish_reason":null}],"usage":null,"obfuscation":"4nTh"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"p"},"finish_reason":null}],"usage":null,"obfuscation":"8GcBxs"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Fixture"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".get"},"finish_reason":null}],"usage":null,"obfuscation":"l1G"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Code"},"finish_reason":null}],"usage":null,"obfuscation":"tiq"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"gu51g"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"vovzo6"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Saved"},"finish_reason":null}],"usage":null,"obfuscation":"Y"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" the"},"finish_reason":null}],"usage":null,"obfuscation":"EhL"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" code"},"finish_reason":null}],"usage":null,"obfuscation":"7L"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" to"},"finish_reason":null}],"usage":null,"obfuscation":"MeaR"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" /"},"finish_reason":null}],"usage":null,"obfuscation":"Bzpfd"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"tmp"},"finish_reason":null}],"usage":null,"obfuscation":"hmov"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/result"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".txt"},"finish_reason":null}],"usage":null,"obfuscation":"omu"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" using"},"finish_reason":null}],"usage":null,"obfuscation":"P"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Developer"},"finish_reason":null}],"usage":null,"obfuscation":"ehHehaK2t0Ddx"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".text"},"finish_reason":null}],"usage":null,"obfuscation":"Io"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Editor"},"finish_reason":null}],"usage":null,"obfuscation":"4"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" ("},"finish_reason":null}],"usage":null,"obfuscation":"ZyNgq"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"write"},"finish_reason":null}],"usage":null,"obfuscation":"79"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":")\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"yx"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"###"},"finish_reason":null}],"usage":null,"obfuscation":"AnhY"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" File"},"finish_reason":null}],"usage":null,"obfuscation":"fR"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" content"},"finish_reason":null}],"usage":null,"obfuscation":"toRedYiWXrOWCt6"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"wNYeR"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"``"},"finish_reason":null}],"usage":null,"obfuscation":"xmJag"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"`\n"},"finish_reason":null}],"usage":null,"obfuscation":"ZqCC"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"test"},"finish_reason":null}],"usage":null,"obfuscation":"Zom"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"25fVgE"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"uuid"},"finish_reason":null}],"usage":null,"obfuscation":"sNs"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"WKf475"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"123"},"finish_reason":null}],"usage":null,"obfuscation":"pUWP"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"45"},"finish_reason":null}],"usage":null,"obfuscation":"UJWHi"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"naAmnL"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"678"},"finish_reason":null}],"usage":null,"obfuscation":"EZiD"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"90"},"finish_reason":null}],"usage":null,"obfuscation":"pmDEn"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"ExdUo"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"``"},"finish_reason":null}],"usage":null,"obfuscation":"GVkML"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"`\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"Rm"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"###"},"finish_reason":null}],"usage":null,"obfuscation":"KHkH"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Write"},"finish_reason":null}],"usage":null,"obfuscation":"N"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" result"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"N9gVJ"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"3m12KG"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Successfully"},"finish_reason":null}],"usage":null,"obfuscation":"zWAzMcKJ1h"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" wrote"},"finish_reason":null}],"usage":null,"obfuscation":"g"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" to"},"finish_reason":null}],"usage":null,"obfuscation":"npd5"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" /"},"finish_reason":null}],"usage":null,"obfuscation":"Di2lF"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"tmp"},"finish_reason":null}],"usage":null,"obfuscation":"bFN2"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/result"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".txt"},"finish_reason":null}],"usage":null,"obfuscation":"jAM"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"mBj"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"If"},"finish_reason":null}],"usage":null,"obfuscation":"EwyTz"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" you"},"finish_reason":null}],"usage":null,"obfuscation":"MdZ"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" want"},"finish_reason":null}],"usage":null,"obfuscation":"lr"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" to"},"finish_reason":null}],"usage":null,"obfuscation":"3UYH"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" view"},"finish_reason":null}],"usage":null,"obfuscation":"fT"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" or"},"finish_reason":null}],"usage":null,"obfuscation":"aA1v"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" edit"},"finish_reason":null}],"usage":null,"obfuscation":"lx"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" the"},"finish_reason":null}],"usage":null,"obfuscation":"CFk"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" file"},"finish_reason":null}],"usage":null,"obfuscation":"Pk"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" further"},"finish_reason":null}],"usage":null,"obfuscation":"xZV6orYDINsvpSm"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"finish_reason":null}],"usage":null,"obfuscation":"rIgbQL"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" I"},"finish_reason":null}],"usage":null,"obfuscation":"PushO"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" can"},"finish_reason":null}],"usage":null,"obfuscation":"NfT"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" open"},"finish_reason":null}],"usage":null,"obfuscation":"vt"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" it"},"finish_reason":null}],"usage":null,"obfuscation":"YdGd"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" or"},"finish_reason":null}],"usage":null,"obfuscation":"Nr1q"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" perform"},"finish_reason":null}],"usage":null,"obfuscation":"odfH1BqmSysXoQX"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" additional"},"finish_reason":null}],"usage":null,"obfuscation":"85PS7edSCbfj"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" edits"},"finish_reason":null}],"usage":null,"obfuscation":"u"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"finish_reason":null}],"usage":null,"obfuscation":"PyvcSe"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"7"} + +data: {"id":"chatcmpl-D64NkPzFlFdteIXaDZiWzbRazyX73","object":"chat.completion.chunk","created":1770339184,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":6014,"completion_tokens":601,"total_tokens":6615,"prompt_tokens_details":{"cached_tokens":4736,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":512,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"wCzJI4gC3nyF"} + +data: [DONE] diff --git a/crates/leaf-acp/tests/test_data/openai_builtin_search.txt b/crates/leaf-acp/tests/test_data/openai_builtin_search.txt new file mode 100644 index 000000000000..7a47e4f4d2bc --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_builtin_search.txt @@ -0,0 +1,39 @@ +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"finish_reason":null}],"obfuscation":"bj"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_mhfEtsDu6HJHTgoyc3rUDTNh","type":"function","function":{"name":"code_execution__list_functions","arguments":""}}]},"finish_reason":null}],"obfuscation":"iPDqUVdKwkKN3F"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{}"}}]},"finish_reason":null}],"obfuscation":"1sK4gwd8S5s"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_AwLicLqHJBNM33BX8pjZeB9H","type":"function","function":{"name":"code_execution__get_function_details","arguments":""}}]},"finish_reason":null}],"obfuscation":"Nb9YXNVD"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"fu"}}]},"finish_reason":null}],"obfuscation":"WfhGNuZ8"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"nctio"}}]},"finish_reason":null}],"obfuscation":"onePKs8g"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ns\": ["}}]},"finish_reason":null}],"obfuscation":"1i6Ns7"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"Mcp"}}]},"finish_reason":null}],"obfuscation":"SxKZ9zQ8"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"Fixtu"}}]},"finish_reason":null}],"obfuscation":"oRNvv26j"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"re.get"}}]},"finish_reason":null}],"obfuscation":"9jh4BM5"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"Code"}}]},"finish_reason":null}],"obfuscation":"X4583CKJj"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\", \"D"}}]},"finish_reason":null}],"obfuscation":"pLCir4"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"evelop"}}]},"finish_reason":null}],"obfuscation":"YdjzlvJ"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"er.w"}}]},"finish_reason":null}],"obfuscation":"Kv1vRc0to"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"rite"}}]},"finish_reason":null}],"obfuscation":"4sRF9L7t"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"]"}}]},"finish_reason":null}],"obfuscation":"SmXF9J"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"finish_reason":null}],"obfuscation":"kO5yFNBeMAXW"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"RNhcx0FLJsf"} + +data: {"id":"chatcmpl-D64NHpAses8hYgIt8xQfDCmg3PoHQ","object":"chat.completion.chunk","created":1770339155,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":2919,"completion_tokens":2626,"total_tokens":5545,"prompt_tokens_details":{"cached_tokens":2560,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":2560,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"KT48p4Oy6v"} + +data: [DONE] diff --git a/crates/leaf-acp/tests/test_data/openai_fs_read_tool_call.txt b/crates/leaf-acp/tests/test_data/openai_fs_read_tool_call.txt new file mode 100644 index 000000000000..b9c05e967f09 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_fs_read_tool_call.txt @@ -0,0 +1,31 @@ +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_ihWQp56Fq7txY7HHZJiNwzdy","type":"function","function":{"name":"read","arguments":""}}],"refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"arnej9G"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"M6pPn6sMuZ"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"path"}}]},"finish_reason":null}],"usage":null,"obfuscation":"7mHQMw0OR"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"UktUfC37dd"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"/"}}]},"finish_reason":null}],"usage":null,"obfuscation":"kkR18xflju"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tmp"}}]},"finish_reason":null}],"usage":null,"obfuscation":"WRyfb1apwr"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"/test"}}]},"finish_reason":null}],"usage":null,"obfuscation":"8Q5olmtW"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_ac"}}]},"finish_reason":null}],"usage":null,"obfuscation":"3eLyGhC3LQ"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"p"}}]},"finish_reason":null}],"usage":null,"obfuscation":"NXUnZPCSiGnn"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_read"}}]},"finish_reason":null}],"usage":null,"obfuscation":"mCuJG2UV"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".txt"}}]},"finish_reason":null}],"usage":null,"obfuscation":"KjZS3z8R5"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Pe4UZ56QwD"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"5hgvg2ilskM"} + +data: {"id":"chatcmpl-DFS7Z5WU3Km0oi1oFGiFSCLRPlriu","object":"chat.completion.chunk","created":1772575389,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":7010,"completion_tokens":156,"total_tokens":7166,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":128,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"bxKDPI1dGxmTdwd"} + +data: [DONE] + + diff --git a/crates/leaf-acp/tests/test_data/openai_fs_read_tool_result.txt b/crates/leaf-acp/tests/test_data/openai_fs_read_tool_result.txt new file mode 100644 index 000000000000..a56e349bf714 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_fs_read_tool_result.txt @@ -0,0 +1,21 @@ +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"5Gvpc"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"test"},"finish_reason":null}],"usage":null,"obfuscation":"8x6"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-read"},"finish_reason":null}],"usage":null,"obfuscation":"PM"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-content"},"finish_reason":null}],"usage":null,"obfuscation":"UxQl2cnPsJJsU4e"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"OIwMhu"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"123"},"finish_reason":null}],"usage":null,"obfuscation":"wsSJ"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"45"},"finish_reason":null}],"usage":null,"obfuscation":"2A6Mf"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"R"} + +data: {"id":"chatcmpl-DFS7cNHbE2luDLKMezfuTconLbkjx","object":"chat.completion.chunk","created":1772575392,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":7061,"completion_tokens":143,"total_tokens":7204,"prompt_tokens_details":{"cached_tokens":6784,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":128,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"GPZ33iiMhEfX"} + +data: [DONE] + + diff --git a/crates/leaf-acp/tests/test_data/openai_fs_write_tool_call.txt b/crates/leaf-acp/tests/test_data/openai_fs_write_tool_call.txt new file mode 100644 index 000000000000..bdeef703be6c --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_fs_write_tool_call.txt @@ -0,0 +1,57 @@ +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_AmzdRa1JlDxMgwoFQ3W9Y6bf","type":"function","function":{"name":"write","arguments":""}}],"refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"RgiRu2"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{"}}]},"finish_reason":null}],"usage":null,"obfuscation":"c0QxkX9w4RWN"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" \""}}]},"finish_reason":null}],"usage":null,"obfuscation":"guqW44RQ65"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"path"}}]},"finish_reason":null}],"usage":null,"obfuscation":"U4psai0wZ"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"VmwxuO12qp"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" \"/"}}]},"finish_reason":null}],"usage":null,"obfuscation":"u6VUDV4LA"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"tmp"}}]},"finish_reason":null}],"usage":null,"obfuscation":"oyo2Q6TTwc"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"/test"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ra2IFNjY"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_ac"}}]},"finish_reason":null}],"usage":null,"obfuscation":"67j2bDOpM3"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"p"}}]},"finish_reason":null}],"usage":null,"obfuscation":"tqT1vbnWapFk"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"35yX5yg"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".txt"}}]},"finish_reason":null}],"usage":null,"obfuscation":"2OmZyuL8W"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\","}}]},"finish_reason":null}],"usage":null,"obfuscation":"D4bVfN7m5M"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" \""}}]},"finish_reason":null}],"usage":null,"obfuscation":"TtPSCtq6VV"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"content"}}]},"finish_reason":null}],"usage":null,"obfuscation":"EqSkfr"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"ubKsA67ljm"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" \""}}]},"finish_reason":null}],"usage":null,"obfuscation":"H6lzd69n6V"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"test"}}]},"finish_reason":null}],"usage":null,"obfuscation":"v0IFOBPju"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-write"}}]},"finish_reason":null}],"usage":null,"obfuscation":"jzyuAat"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-content"}}]},"finish_reason":null}],"usage":null,"obfuscation":"6p3tY"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-"}}]},"finish_reason":null}],"usage":null,"obfuscation":"R4PFCfWGWwQO"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"678"}}]},"finish_reason":null}],"usage":null,"obfuscation":"BHRNUazkpJ"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"90"}}]},"finish_reason":null}],"usage":null,"obfuscation":"085WWflBbh1"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"zu9irR3Bf58"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" }"}}]},"finish_reason":null}],"usage":null,"obfuscation":"m4lW5kBYPNx"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"Ptpd2etx9L8"} + +data: {"id":"chatcmpl-DFS9ySulK0wZhiU0Qbcdrr7vqTth2","object":"chat.completion.chunk","created":1772575538,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":7013,"completion_tokens":233,"total_tokens":7246,"prompt_tokens_details":{"cached_tokens":6784,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":192,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"K1gClbYIVju8"} + +data: [DONE] + + diff --git a/crates/leaf-acp/tests/test_data/openai_fs_write_tool_result.txt b/crates/leaf-acp/tests/test_data/openai_fs_write_tool_result.txt new file mode 100644 index 000000000000..df360031e68c --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_fs_write_tool_result.txt @@ -0,0 +1,41 @@ +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"LlUCg"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Written"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" to"},"finish_reason":null}],"usage":null,"obfuscation":"eOkP"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" /"},"finish_reason":null}],"usage":null,"obfuscation":"Wna6a"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"tmp"},"finish_reason":null}],"usage":null,"obfuscation":"r1VT"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/test"},"finish_reason":null}],"usage":null,"obfuscation":"KI"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"_ac"},"finish_reason":null}],"usage":null,"obfuscation":"OV52"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"p"},"finish_reason":null}],"usage":null,"obfuscation":"lN8FEz"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"_write"},"finish_reason":null}],"usage":null,"obfuscation":"w"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".txt"},"finish_reason":null}],"usage":null,"obfuscation":"mhj"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":"},"finish_reason":null}],"usage":null,"obfuscation":"MLXIAD"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"finish_reason":null}],"usage":null,"obfuscation":"fu"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-write"},"finish_reason":null}],"usage":null,"obfuscation":"P"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-content"},"finish_reason":null}],"usage":null,"obfuscation":"UnYjMuzV1tsglmv"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"dXz2oN"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"678"},"finish_reason":null}],"usage":null,"obfuscation":"cBoM"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"90"},"finish_reason":null}],"usage":null,"obfuscation":"nCBwP"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"j"} + +data: {"id":"chatcmpl-DFSA1sjiFQp5MUkdJvhadSGA4nAoX","object":"chat.completion.chunk","created":1772575541,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":7080,"completion_tokens":19,"total_tokens":7099,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"e7"} + +data: [DONE] + + diff --git a/crates/leaf-acp/tests/test_data/openai_image_attachment.txt b/crates/leaf-acp/tests/test_data/openai_image_attachment.txt new file mode 100644 index 000000000000..add7a87aa3de --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_image_attachment.txt @@ -0,0 +1,238 @@ +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"7E7t8"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Here"},"finish_reason":null}],"usage":null,"obfuscation":"C7q"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"’s"},"finish_reason":null}],"usage":null,"obfuscation":"2YLin"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" what"},"finish_reason":null}],"usage":null,"obfuscation":"X0"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" I"},"finish_reason":null}],"usage":null,"obfuscation":"cWHjE"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" can"},"finish_reason":null}],"usage":null,"obfuscation":"sSB"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" derive"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" from"},"finish_reason":null}],"usage":null,"obfuscation":"wh"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" the"},"finish_reason":null}],"usage":null,"obfuscation":"pdc"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" image"},"finish_reason":null}],"usage":null,"obfuscation":"6"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"sf"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"8yQy1S"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" OCR"},"finish_reason":null}],"usage":null,"obfuscation":"8A1"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/text"},"finish_reason":null}],"usage":null,"obfuscation":"9R"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" transcription"},"finish_reason":null}],"usage":null,"obfuscation":"Ehc21BJqv"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":\n"},"finish_reason":null}],"usage":null,"obfuscation":"9ab4"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"u2EwQi"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Hello"},"finish_reason":null}],"usage":null,"obfuscation":"5"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Goose"},"finish_reason":null}],"usage":null,"obfuscation":"b"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"!\n"},"finish_reason":null}],"usage":null,"obfuscation":"EsVy"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"Va8f84"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" This"},"finish_reason":null}],"usage":null,"obfuscation":"IR"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"finish_reason":null}],"usage":null,"obfuscation":"eaRL"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"finish_reason":null}],"usage":null,"obfuscation":"cdTOW"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"finish_reason":null}],"usage":null,"obfuscation":"WN"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" image"},"finish_reason":null}],"usage":null,"obfuscation":"l"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"lx"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"ABF429"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Observ"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ations"},"finish_reason":null}],"usage":null,"obfuscation":"J"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":\n"},"finish_reason":null}],"usage":null,"obfuscation":"iWhB"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"IFJGlV"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" -"},"finish_reason":null}],"usage":null,"obfuscation":"C8bxY"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" The"},"finish_reason":null}],"usage":null,"obfuscation":"UNZ"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" text"},"finish_reason":null}],"usage":null,"obfuscation":"aO"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" appears"},"finish_reason":null}],"usage":null,"obfuscation":"fMW5Ik2fIuCxYIi"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" in"},"finish_reason":null}],"usage":null,"obfuscation":"6Csr"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" two"},"finish_reason":null}],"usage":null,"obfuscation":"1OA"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" lines"},"finish_reason":null}],"usage":null,"obfuscation":"6"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" on"},"finish_reason":null}],"usage":null,"obfuscation":"MfK8"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"finish_reason":null}],"usage":null,"obfuscation":"Xn4NP"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" white"},"finish_reason":null}],"usage":null,"obfuscation":"Z"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" background"},"finish_reason":null}],"usage":null,"obfuscation":"xJbYCha0CxG5"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" with"},"finish_reason":null}],"usage":null,"obfuscation":"pN"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" black"},"finish_reason":null}],"usage":null,"obfuscation":"7"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" sans"},"finish_reason":null}],"usage":null,"obfuscation":"ni"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-serif"},"finish_reason":null}],"usage":null,"obfuscation":"X"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" style"},"finish_reason":null}],"usage":null,"obfuscation":"W"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\n"},"finish_reason":null}],"usage":null,"obfuscation":"TPOZ"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"SpMYza"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" -"},"finish_reason":null}],"usage":null,"obfuscation":"CSuHP"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Lik"},"finish_reason":null}],"usage":null,"obfuscation":"Wgi"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ely"},"finish_reason":null}],"usage":null,"obfuscation":"dhDR"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" centered"},"finish_reason":null}],"usage":null,"obfuscation":"ai50dSkUIc0bS6"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" alignment"},"finish_reason":null}],"usage":null,"obfuscation":"8sTNdzGIIYmdW"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"finish_reason":null}],"usage":null,"obfuscation":"Fby3dv"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" two"},"finish_reason":null}],"usage":null,"obfuscation":"Lpl"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-line"},"finish_reason":null}],"usage":null,"obfuscation":"xf"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" layout"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"CM"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"What"},"finish_reason":null}],"usage":null,"obfuscation":"zy9"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" would"},"finish_reason":null}],"usage":null,"obfuscation":"d"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" you"},"finish_reason":null}],"usage":null,"obfuscation":"qyo"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" like"},"finish_reason":null}],"usage":null,"obfuscation":"tG"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" me"},"finish_reason":null}],"usage":null,"obfuscation":"KJ3J"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" to"},"finish_reason":null}],"usage":null,"obfuscation":"jTJN"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" do"},"finish_reason":null}],"usage":null,"obfuscation":"Kvuu"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" next"},"finish_reason":null}],"usage":null,"obfuscation":"Q4"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"?\n"},"finish_reason":null}],"usage":null,"obfuscation":"MKya"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"FGV2M3"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Translate"},"finish_reason":null}],"usage":null,"obfuscation":"ojudhthRN0wje"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" the"},"finish_reason":null}],"usage":null,"obfuscation":"j1d"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" text"},"finish_reason":null}],"usage":null,"obfuscation":"IN"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"5GPdt"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"GdygSH"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Extract"},"finish_reason":null}],"usage":null,"obfuscation":"CocHxzrTqsaRFbq"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" more"},"finish_reason":null}],"usage":null,"obfuscation":"LN"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" metadata"},"finish_reason":null}],"usage":null,"obfuscation":"52g08uQUy8fzv2"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" ("},"finish_reason":null}],"usage":null,"obfuscation":"AmD59"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"font"},"finish_reason":null}],"usage":null,"obfuscation":"Qvr"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"finish_reason":null}],"usage":null,"obfuscation":"DKTZR4"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" size"},"finish_reason":null}],"usage":null,"obfuscation":"sa"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"finish_reason":null}],"usage":null,"obfuscation":"kQknkh"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" contrast"},"finish_reason":null}],"usage":null,"obfuscation":"CzLtt2a3ZKZRsk"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":")\n"},"finish_reason":null}],"usage":null,"obfuscation":"10ts"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"BkGfhE"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Generate"},"finish_reason":null}],"usage":null,"obfuscation":"kRp8kkudiLXyPr"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" alt"},"finish_reason":null}],"usage":null,"obfuscation":"naE"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" text"},"finish_reason":null}],"usage":null,"obfuscation":"WO"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" or"},"finish_reason":null}],"usage":null,"obfuscation":"wjCo"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" accessibility"},"finish_reason":null}],"usage":null,"obfuscation":"HgWk0MkQP"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" description"},"finish_reason":null}],"usage":null,"obfuscation":"2RkUmSr6mK5"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"qCmgA"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"dDYudm"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Create"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"finish_reason":null}],"usage":null,"obfuscation":"Uco4R"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" caption"},"finish_reason":null}],"usage":null,"obfuscation":"cAPhTd1DoHVetWW"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" or"},"finish_reason":null}],"usage":null,"obfuscation":"qK8N"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" use"},"finish_reason":null}],"usage":null,"obfuscation":"wGO"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" it"},"finish_reason":null}],"usage":null,"obfuscation":"DKP3"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" in"},"finish_reason":null}],"usage":null,"obfuscation":"0qux"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" code"},"finish_reason":null}],"usage":null,"obfuscation":"FI"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" ("},"finish_reason":null}],"usage":null,"obfuscation":"BXnPm"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"HTML"},"finish_reason":null}],"usage":null,"obfuscation":"lal"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/C"},"finish_reason":null}],"usage":null,"obfuscation":"2bBrS"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"SS"},"finish_reason":null}],"usage":null,"obfuscation":"kM3Jx"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":")\n"},"finish_reason":null}],"usage":null,"obfuscation":"H7Et"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"EbOoyr"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Run"},"finish_reason":null}],"usage":null,"obfuscation":"h74"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" OCR"},"finish_reason":null}],"usage":null,"obfuscation":"3vs"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" on"},"finish_reason":null}],"usage":null,"obfuscation":"N3y0"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" similar"},"finish_reason":null}],"usage":null,"obfuscation":"mMnr4ffD7zygIYv"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" images"},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" for"},"finish_reason":null}],"usage":null,"obfuscation":"TJp"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" batch"},"finish_reason":null}],"usage":null,"obfuscation":"6"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" processing"},"finish_reason":null}],"usage":null,"obfuscation":"ls6HOZgAV1ez"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"j"} + +data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":2768,"completion_tokens":572,"total_tokens":3340,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":448,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"EvMBUZOQw2ps4DX"} + +data: [DONE] + diff --git a/crates/leaf-acp/tests/test_data/openai_image_tool_call.txt b/crates/leaf-acp/tests/test_data/openai_image_tool_call.txt new file mode 100644 index 000000000000..491f78285829 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_image_tool_call.txt @@ -0,0 +1,9 @@ +data: {"id":"chatcmpl-D64jlQjeSdFB1flOZAoyVx3Kr3jBM","object":"chat.completion.chunk","created":1770340549,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_WQz9ddBYGE2NUstbYIx4jLzP","type":"function","function":{"name":"mcp-fixture__get_image","arguments":""}}],"refusal":null},"finish_reason":null}],"usage":null,"obfuscation":""} + +data: {"id":"chatcmpl-D64jlQjeSdFB1flOZAoyVx3Kr3jBM","object":"chat.completion.chunk","created":1770340549,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{}"}}]},"finish_reason":null}],"usage":null,"obfuscation":"S3q07Q7rhA5"} + +data: {"id":"chatcmpl-D64jlQjeSdFB1flOZAoyVx3Kr3jBM","object":"chat.completion.chunk","created":1770340549,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"sqTkwgxzEue"} + +data: {"id":"chatcmpl-D64jlQjeSdFB1flOZAoyVx3Kr3jBM","object":"chat.completion.chunk","created":1770340549,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":5254,"completion_tokens":345,"total_tokens":5599,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":320,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"XUJZvwg1ZvdZQCh"} + +data: [DONE] diff --git a/crates/leaf-acp/tests/test_data/openai_image_tool_result.txt b/crates/leaf-acp/tests/test_data/openai_image_tool_result.txt new file mode 100644 index 000000000000..55126be16644 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_image_tool_result.txt @@ -0,0 +1,25 @@ +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"5exqq"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}],"usage":null,"obfuscation":"yI"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Goose"},"finish_reason":null}],"usage":null,"obfuscation":"6"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"!\n"},"finish_reason":null}],"usage":null,"obfuscation":"ocAA"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"This"},"finish_reason":null}],"usage":null,"obfuscation":"DJx"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"finish_reason":null}],"usage":null,"obfuscation":"CQGZ"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"finish_reason":null}],"usage":null,"obfuscation":"NJygF"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"finish_reason":null}],"usage":null,"obfuscation":"kV"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" image"},"finish_reason":null}],"usage":null,"obfuscation":"S"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"finish_reason":null}],"usage":null,"obfuscation":"Cv0h11"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"N"} + +data: {"id":"chatcmpl-D64jowotwkWXsd3RqhveZzRGUuOVu","object":"chat.completion.chunk","created":1770340552,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":5448,"completion_tokens":466,"total_tokens":5914,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":448,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"evt64Mp0NnoCT9R"} + +data: [DONE] diff --git a/crates/leaf-acp/tests/test_data/openai_shell_tool_call.txt b/crates/leaf-acp/tests/test_data/openai_shell_tool_call.txt new file mode 100644 index 000000000000..f677072b2321 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_shell_tool_call.txt @@ -0,0 +1,30 @@ +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_JOtrY1Ec2KAB2a7gI6H6CWfs","type":"function","function":{"name":"shell","arguments":""}}],"refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"QvrA4B"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"fvwZUJVGPB"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"command"}}]},"finish_reason":null}],"usage":null,"obfuscation":"bHHxtU"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"GgtVJCU4"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"echo"}}]},"finish_reason":null}],"usage":null,"obfuscation":"DRTSpxLvb"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" test"}}]},"finish_reason":null}],"usage":null,"obfuscation":"IwvMxMcs"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-shell"}}]},"finish_reason":null}],"usage":null,"obfuscation":"y6LBASX"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-content"}}]},"finish_reason":null}],"usage":null,"obfuscation":"4bIL9"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-"}}]},"finish_reason":null}],"usage":null,"obfuscation":"cOYkwBZ6YQk9"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"987"}}]},"finish_reason":null}],"usage":null,"obfuscation":"2QASfrMS6w"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"65"}}]},"finish_reason":null}],"usage":null,"obfuscation":"IKmTYztxyp2"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"finish_reason":null}],"usage":null,"obfuscation":"tVHIYFZOIs"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"91dJqR0sRAl"} + +data: {"id":"chatcmpl-DJqmdyg4XAGu7vN41PpMz4U8CMQhI","object":"chat.completion.chunk","created":1773623503,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":3392,"completion_tokens":284,"total_tokens":3676,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":256,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"aOmtxq2YavJWo1s"} + +data: [DONE] + diff --git a/crates/leaf-acp/tests/test_data/openai_shell_tool_result.txt b/crates/leaf-acp/tests/test_data/openai_shell_tool_result.txt new file mode 100644 index 000000000000..82e7f2ea8ac0 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_shell_tool_result.txt @@ -0,0 +1,21 @@ +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"k3ql1"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"test"},"finish_reason":null}],"usage":null,"obfuscation":"fi2"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-shell"},"finish_reason":null}],"usage":null,"obfuscation":"l"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-content"},"finish_reason":null}],"usage":null,"obfuscation":"lV6xovjxNlG3ZDv"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"EzE9fO"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"987"},"finish_reason":null}],"usage":null,"obfuscation":"Ji6A"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"65"},"finish_reason":null}],"usage":null,"obfuscation":"lI6rL"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"x"} + +data: {"id":"chatcmpl-DJqmhw9icV2gtFchMIfmzQZxB4sAt","object":"chat.completion.chunk","created":1773623507,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":3443,"completion_tokens":9,"total_tokens":3452,"prompt_tokens_details":{"cached_tokens":3200,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":""} + +data: [DONE] + + diff --git a/crates/leaf-acp/tests/test_data/openai_tool_call.txt b/crates/leaf-acp/tests/test_data/openai_tool_call.txt new file mode 100644 index 000000000000..c62eaf9b9238 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_tool_call.txt @@ -0,0 +1,10 @@ +data: {"id":"chatcmpl-CqqCVVtD16yj37EZocLFkGNMhHZFS","object":"chat.completion.chunk","created":1766709751,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_eLXEeL8ZQBgXACKp78eNmyNp","type":"function","function":{"name":"mcp-fixture__get_code","arguments":""}}],"refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"FobexttCIQY"} + +data: {"id":"chatcmpl-CqqCVVtD16yj37EZocLFkGNMhHZFS","object":"chat.completion.chunk","created":1766709751,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{}"}}]},"finish_reason":null}],"usage":null,"obfuscation":"01EkRUrgMxo"} + +data: {"id":"chatcmpl-CqqCVVtD16yj37EZocLFkGNMhHZFS","object":"chat.completion.chunk","created":1766709751,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"k965c2jCwUF"} + +data: {"id":"chatcmpl-CqqCVVtD16yj37EZocLFkGNMhHZFS","object":"chat.completion.chunk","created":1766709751,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":2320,"completion_tokens":149,"total_tokens":2469,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":128,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"7Wxwg9X1OBwbjfE"} + +data: [DONE] + diff --git a/crates/leaf-acp/tests/test_data/openai_tool_result.txt b/crates/leaf-acp/tests/test_data/openai_tool_result.txt new file mode 100644 index 000000000000..d940cc4d9834 --- /dev/null +++ b/crates/leaf-acp/tests/test_data/openai_tool_result.txt @@ -0,0 +1,26 @@ +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"p0yzG"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"test"},"finish_reason":null}],"usage":null,"obfuscation":"ASB"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"b2XPf4"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"uuid"},"finish_reason":null}],"usage":null,"obfuscation":"88h"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"MunLmd"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"123"},"finish_reason":null}],"usage":null,"obfuscation":"iyKy"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"45"},"finish_reason":null}],"usage":null,"obfuscation":"MGSUp"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"swF2Pu"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"678"},"finish_reason":null}],"usage":null,"obfuscation":"TPtP"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"90"},"finish_reason":null}],"usage":null,"obfuscation":"1UrvC"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"e"} + +data: {"id":"chatcmpl-CqqCXO3JYXwnwTystVUj2AuyW9xgV","object":"chat.completion.chunk","created":1766709753,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":2357,"completion_tokens":274,"total_tokens":2631,"prompt_tokens_details":{"cached_tokens":2048,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":256,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"qFAKS6Oew9eV"} + +data: [DONE] + diff --git a/crates/leaf-cli/Cargo.toml b/crates/leaf-cli/Cargo.toml index 4664bda922ad..59023270a1f9 100644 --- a/crates/leaf-cli/Cargo.toml +++ b/crates/leaf-cli/Cargo.toml @@ -61,6 +61,8 @@ url = { workspace = true } urlencoding = { workspace = true } clap_complete = "4.5.62" comfy-table = "7.2.2" +sha2 = "0.10" +sigstore-verification = { version = "0.1", default-features = false, features = ["rustls"] } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["wincred"] } diff --git a/crates/leaf-cli/src/commands/configure.rs b/crates/leaf-cli/src/commands/configure.rs index 5ace5dac62cd..d1c66d8d51f8 100644 --- a/crates/leaf-cli/src/commands/configure.rs +++ b/crates/leaf-cli/src/commands/configure.rs @@ -447,9 +447,7 @@ fn select_model_from_list( // Sort models naturally let mut sorted_models = models.to_vec(); - sorted_models.sort_by(|a, b| { - a.to_lowercase().cmp(&b.to_lowercase()) - }); + sorted_models.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase())); if sorted_models.len() > MAX_MODELS { // Use models from API directly, not filtered through known_models @@ -470,9 +468,7 @@ fn select_model_from_list( } } - recommended_models.sort_by(|a, b| { - a.to_lowercase().cmp(&b.to_lowercase()) - }); + recommended_models.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase())); if !recommended_models.is_empty() { let mut model_items: Vec<(String, String, &str)> = recommended_models @@ -510,8 +506,10 @@ fn select_model_from_list( Ok(interactive_model_search(&sorted_models)?) } } else { - let mut model_items: Vec<(String, String, &str)> = - sorted_models.iter().map(|m| (m.clone(), m.clone(), "")).collect(); + let mut model_items: Vec<(String, String, &str)> = sorted_models + .iter() + .map(|m| (m.clone(), m.clone(), "")) + .collect(); model_items.push(( UNLISTED_MODEL_KEY.to_string(), diff --git a/crates/leaf-cli/src/commands/session.rs b/crates/leaf-cli/src/commands/session.rs index 145669043e16..c4851ed3762f 100644 --- a/crates/leaf-cli/src/commands/session.rs +++ b/crates/leaf-cli/src/commands/session.rs @@ -89,25 +89,18 @@ pub async fn handle_session_remove( regex_string: Option, ) -> Result<()> { let session_manager = SessionManager::instance(); - let all_sessions = match session_manager.list_sessions().await { - Ok(sessions) => sessions, - Err(e) => { - tracing::error!("Failed to retrieve sessions: {:?}", e); - return Err(anyhow::anyhow!("Failed to retrieve sessions")); - } - }; let matched_sessions: Vec; if let Some(id_val) = session_id { - if let Some(session) = all_sessions.iter().find(|s| s.id == id_val) { - matched_sessions = vec![session.clone()]; - } else { - return Err(anyhow::anyhow!("Session ID '{}' not found.", id_val)); + match session_manager.get_session(&id_val, false).await { + Ok(session) => matched_sessions = vec![session], + Err(_) => return Err(anyhow::anyhow!("Session ID '{}' not found.", id_val)), } } else if let Some(name_val) = name { - if let Some(session) = all_sessions.iter().find(|s| s.name == name_val) { - matched_sessions = vec![session.clone()]; + let all_sessions = session_manager.list_all_sessions().await?; + if let Some(session) = all_sessions.into_iter().find(|s| s.name == name_val) { + matched_sessions = vec![session]; } else { return Err(anyhow::anyhow!( "Session with name '{}' not found.", @@ -118,7 +111,8 @@ pub async fn handle_session_remove( let session_regex = Regex::new(®ex_val) .with_context(|| format!("Invalid regex pattern '{}'", regex_val))?; - matched_sessions = all_sessions + let visible_sessions = session_manager.list_sessions().await?; + matched_sessions = visible_sessions .into_iter() .filter(|session| session_regex.is_match(&session.id)) .collect(); @@ -128,10 +122,11 @@ pub async fn handle_session_remove( return Ok(()); } } else { - if all_sessions.is_empty() { + let visible_sessions = session_manager.list_sessions().await?; + if visible_sessions.is_empty() { return Err(anyhow::anyhow!("No sessions found.")); } - matched_sessions = prompt_interactive_session_removal(&all_sessions)?; + matched_sessions = prompt_interactive_session_removal(&visible_sessions)?; } if matched_sessions.is_empty() { diff --git a/crates/leaf-cli/src/commands/update.rs b/crates/leaf-cli/src/commands/update.rs index a22de096e1ac..27736863841d 100644 --- a/crates/leaf-cli/src/commands/update.rs +++ b/crates/leaf-cli/src/commands/update.rs @@ -1,6 +1,8 @@ use anyhow::{bail, Context, Result}; +use sha2::{Digest, Sha256}; use std::env; use std::fs; +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; @@ -40,10 +42,90 @@ fn binary_name() -> &'static str { } } +// --------------------------------------------------------------------------- +// Sigstore / SLSA provenance verification +// --------------------------------------------------------------------------- + +/// Compute the SHA-256 hex digest of a byte slice. +fn sha256_hex(data: &[u8]) -> String { + let mut hasher = Sha256::new(); + hasher.update(data); + format!("{:x}", hasher.finalize()) +} + +/// Verify the downloaded archive against GitHub's Sigstore SLSA provenance. +/// +/// Uses the attestations published by `actions/attest-build-provenance` in the +/// release/canary/nightly workflows (added in PR #7097). Verification fetches +/// the attestation bundle from GitHub's attestation API and validates the +/// Sigstore signature chain, Rekor transparency log inclusion, and artifact +/// digest match. +/// Returns `Ok(true)` when the attestation is fully verified, `Ok(false)` when +/// verification was skipped with a warning (no attestation, transient error), +/// and `Err` when verification actively fails (invalid signature, digest +/// mismatch, etc.). +async fn verify_provenance(archive_data: &[u8], tag: &str) -> Result { + let digest = sha256_hex(archive_data); + println!("Archive SHA-256: {digest}"); + + let mut tmp_file = tempfile::NamedTempFile::new() + .context("Failed to create temp file for provenance verification")?; + tmp_file + .write_all(archive_data) + .context("Failed to write archive to temp file")?; + tmp_file.flush().context("Failed to flush temp file")?; + + let workflow = match tag { + "canary" => "canary.yml", + _ => "release.yml", + }; + + let token = std::env::var("GITHUB_TOKEN") + .ok() + .or_else(|| std::env::var("GH_TOKEN").ok()); + + println!("Verifying SLSA provenance via Sigstore..."); + + match sigstore_verification::verify_github_attestation( + tmp_file.path(), + "block", + "goose", + token.as_deref(), + Some(workflow), + ) + .await + { + Ok(_) => { + println!("Sigstore provenance verification passed."); + Ok(true) + } + Err(sigstore_verification::AttestationError::NoAttestations) => { + eprintln!( + "Warning: No Sigstore attestation found for this build. \ + This may be expected for canary or nightly builds." + ); + Ok(false) + } + Err(sigstore_verification::AttestationError::Verification(msg)) => Err(anyhow::anyhow!( + "Sigstore verification failed: {}\n\nAborting update due to security check failure.", + msg + )), + Err(e) => { + eprintln!( + "Warning: Sigstore provenance check could not complete: {e}\n\ + This may be expected for releases published before provenance \ + attestations were enabled." + ); + Ok(false) + } + } +} + /// Update the goose binary to the latest release. /// -/// Downloads the platform-appropriate archive from GitHub releases, -/// extracts it, and replaces the current binary in-place. +/// Downloads the platform-appropriate archive from GitHub releases, verifies +/// its SLSA provenance via Sigstore, extracts it with path-traversal +/// hardening, and replaces the current binary in-place. pub async fn update(canary: bool, reconfigure: bool) -> Result<()> { #[cfg(feature = "disable-update")] { @@ -78,7 +160,10 @@ pub async fn update(canary: bool, reconfigure: bool) -> Result<()> { println!("Downloaded {} bytes.", bytes.len()); - // --- Extract to temp dir ------------------------------------------------ + // --- Verify SLSA provenance via Sigstore -------------------------------- + let provenance_verified = verify_provenance(&bytes, tag).await?; + + // --- Extract to temp dir (hardened against path traversal) -------------- let tmp_dir = tempfile::tempdir().context("Failed to create temp directory")?; #[cfg(target_os = "windows")] @@ -103,7 +188,11 @@ pub async fn update(canary: bool, reconfigure: bool) -> Result<()> { #[cfg(target_os = "windows")] copy_dlls(&extracted_binary, ¤t_exe)?; - println!("goose updated successfully!"); + if provenance_verified { + println!("goose updated successfully (verified with Sigstore SLSA provenance)."); + } else { + println!("goose updated successfully."); + } // --- Reconfigure if requested ------------------------------------------- if reconfigure { @@ -125,27 +214,94 @@ pub async fn update(canary: bool, reconfigure: bool) -> Result<()> { // Archive extraction // --------------------------------------------------------------------------- -/// Extract a .zip archive (Windows). +/// Extract a .zip archive with path-traversal hardening (Windows). +/// +/// Iterates entries individually and uses `enclosed_name()` to reject any +/// path that escapes the destination directory (zip-slip protection). #[cfg(target_os = "windows")] fn extract_zip(data: &[u8], dest: &Path) -> Result<()> { use std::io::Cursor; let cursor = Cursor::new(data); let mut archive = zip::ZipArchive::new(cursor).context("Failed to open zip archive")?; - archive - .extract(dest) - .context("Failed to extract zip archive")?; + + for i in 0..archive.len() { + let mut entry = archive + .by_index(i) + .with_context(|| format!("Failed to read zip entry at index {i}"))?; + + let safe_path = match entry.enclosed_name() { + Some(p) => p.to_owned(), + None => bail!("Zip entry has unsafe path: {}", entry.name()), + }; + + let target = dest.join(&safe_path); + + if entry.is_dir() { + fs::create_dir_all(&target)?; + } else { + if let Some(parent) = target.parent() { + fs::create_dir_all(parent)?; + } + let mut out = fs::File::create(&target)?; + std::io::copy(&mut entry, &mut out)?; + } + } + Ok(()) } -/// Extract a .tar.bz2 archive (macOS / Linux). +/// Validate that an archive entry path is safe (no absolute paths, no `..`). +fn validate_entry_path(path: &Path) -> Result<()> { + if path.is_absolute() { + bail!("Tar entry has absolute path: {}", path.display()); + } + for component in path.components() { + if matches!(component, std::path::Component::ParentDir) { + bail!("Tar entry contains path traversal: {}", path.display()); + } + } + Ok(()) +} + +/// Extract a .tar.bz2 archive with path-traversal hardening (macOS / Linux). +/// +/// Iterates entries individually, rejecting any entry whose path is absolute +/// or contains `..` components (tar-slip protection). #[cfg(not(target_os = "windows"))] fn extract_tar_bz2(data: &[u8], dest: &Path) -> Result<()> { use bzip2::read::BzDecoder; let decoder = BzDecoder::new(data); let mut archive = tar::Archive::new(decoder); - archive - .unpack(dest) - .context("Failed to extract tar.bz2 archive")?; + + for entry in archive.entries().context("Failed to read tar entries")? { + let mut entry = entry.context("Failed to read tar entry")?; + let path = entry + .path() + .context("Failed to read entry path")? + .into_owned(); + + validate_entry_path(&path)?; + + // Block symlinks and hardlinks whose targets escape the destination directory. + // Use entry.link_name() (not entry.header().link_name()) so GNU/PAX extended + // metadata (linkpath) is resolved; the header field alone may be truncated. + let link_target_opt = entry + .link_name() + .context("Failed to read link name from tar entry")?; + if let Some(link_target) = link_target_opt { + validate_entry_path(&link_target)?; + } + + let target = dest.join(&path); + if let Some(parent) = target.parent() { + fs::create_dir_all(parent)?; + } + + entry + .unpack(&target) + .with_context(|| format!("Failed to extract: {}", path.display()))?; + } + Ok(()) } @@ -457,4 +613,143 @@ mod tests { // DLL should be in goose-package too assert!(tmp.path().join("goose-package/libtest.dll").exists()); } + + // ----------------------------------------------------------------------- + // SHA-256 digest tests + // ----------------------------------------------------------------------- + + #[test] + fn test_sha256_hex_known_value() { + let digest = sha256_hex(b"hello world"); + assert_eq!( + digest, + "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" + ); + } + + #[test] + fn test_sha256_hex_empty() { + let digest = sha256_hex(b""); + assert_eq!( + digest, + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ); + } + + // ----------------------------------------------------------------------- + // Path validation and extraction hardening tests + // ----------------------------------------------------------------------- + + #[test] + fn test_validate_entry_path_accepts_safe_paths() { + assert!(validate_entry_path(Path::new("goose")).is_ok()); + assert!(validate_entry_path(Path::new("goose-package/goose")).is_ok()); + assert!(validate_entry_path(Path::new("subdir/nested/file.txt")).is_ok()); + } + + #[test] + fn test_validate_entry_path_rejects_absolute() { + let result = validate_entry_path(Path::new("/etc/malicious")); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("absolute path")); + } + + #[test] + fn test_validate_entry_path_rejects_traversal() { + let result = validate_entry_path(Path::new("../../escape.txt")); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("path traversal")); + } + + #[test] + fn test_validate_entry_path_rejects_nested_traversal() { + let result = validate_entry_path(Path::new("safe/../../escape")); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("path traversal")); + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn test_extract_tar_bz2_safe_archive() { + use bzip2::write::BzEncoder; + use bzip2::Compression; + + let tmp = tempdir().unwrap(); + + let mut builder_buf = Vec::new(); + { + let encoder = BzEncoder::new(&mut builder_buf, Compression::default()); + let mut builder = tar::Builder::new(encoder); + + let data = b"goose binary content"; + let mut header = tar::Header::new_gnu(); + header.set_size(data.len() as u64); + header.set_mode(0o755); + header.set_cksum(); + builder + .append_data(&mut header, "goose-package/goose", &data[..]) + .unwrap(); + builder.into_inner().unwrap().finish().unwrap(); + } + + extract_tar_bz2(&builder_buf, tmp.path()).unwrap(); + + let extracted = tmp.path().join("goose-package/goose"); + assert!(extracted.exists()); + assert_eq!( + fs::read_to_string(extracted).unwrap(), + "goose binary content" + ); + } + + // ----------------------------------------------------------------------- + // Sigstore provenance verification test + // ----------------------------------------------------------------------- + + #[tokio::test] + async fn test_verify_provenance_warns_on_missing_attestation() { + let result = verify_provenance(b"not a real archive", "stable").await; + // Network failures and missing attestations are soft warnings: Ok(false), not hard errors. + assert_eq!( + result.ok(), + Some(false), + "verify_provenance should return Ok(false) when attestations cannot be fetched" + ); + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn test_extract_tar_bz2_blocks_symlink_escape() { + use bzip2::write::BzEncoder; + use bzip2::Compression; + + let tmp = tempdir().unwrap(); + + let mut builder_buf = Vec::new(); + { + let encoder = BzEncoder::new(&mut builder_buf, Compression::default()); + let mut builder = tar::Builder::new(encoder); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_mode(0o777); + header.set_cksum(); + // Symlink whose target escapes the destination directory. + builder + .append_link(&mut header, "evil_link", "../../etc/passwd") + .unwrap(); + builder.into_inner().unwrap().finish().unwrap(); + } + + let result = extract_tar_bz2(&builder_buf, tmp.path()); + assert!( + result.is_err(), + "extraction should fail when a symlink target escapes the destination" + ); + let err_msg = result.unwrap_err().to_string(); + assert!( + err_msg.contains("path traversal"), + "error should mention path traversal, got: {err_msg}" + ); + } } diff --git a/crates/leaf-cli/src/logging.rs b/crates/leaf-cli/src/logging.rs index b126cc49573c..758614be3d6e 100644 --- a/crates/leaf-cli/src/logging.rs +++ b/crates/leaf-cli/src/logging.rs @@ -67,14 +67,12 @@ fn setup_logging_internal(name: Option<&str>, force: bool, verbose: bool) -> Res EnvFilter::try_from_default_env().unwrap_or_else(|_| default_env_filter()); // Start building the subscriber - let mut layers = vec![ - file_layer.with_filter(env_filter).boxed(), - ]; + let mut layers = vec![file_layer.with_filter(env_filter).boxed()]; // Console logging layer when verbose mode is enabled if verbose { - let console_filter = EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new("debug")); + let console_filter = + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("debug")); let console_layer = fmt::layer() .with_target(true) .with_level(true) diff --git a/crates/leaf-cli/src/scenario_tests/scenarios.rs b/crates/leaf-cli/src/scenario_tests/scenarios.rs index 6b476e3f0a8b..d1c7695742cc 100644 --- a/crates/leaf-cli/src/scenario_tests/scenarios.rs +++ b/crates/leaf-cli/src/scenario_tests/scenarios.rs @@ -17,7 +17,7 @@ mod tests { |result| { assert!(result.error.is_none()); assert!( - result.last_message()?.to_lowercase().contains("leaf"), + result.last_message()?.to_lowercase().contains("goose"), "Response should contain 'goose': {}", result.last_message()? ); diff --git a/crates/leaf-cli/src/session/builder.rs b/crates/leaf-cli/src/session/builder.rs index 6de5dfbed7f6..a975f80efd7f 100644 --- a/crates/leaf-cli/src/session/builder.rs +++ b/crates/leaf-cli/src/session/builder.rs @@ -187,8 +187,7 @@ async fn offer_extension_debugging_help( // Create a debugging prompt with context about the extension failure let debug_prompt = format!( "I'm having trouble starting an extension called '{}'. Here's the error I encountered:\n\n{}\n\nCan you help me diagnose what might be wrong and suggest how to fix it? Please consider common issues like:\n- Missing dependencies or tools\n- Configuration problems\n- Network connectivity (for remote extensions)\n- Permission issues\n- Path or environment variable problems", - extension_name, - error_message + extension_name, error_message ); // Create a minimal agent for debugging diff --git a/crates/leaf-cli/src/session/mod.rs b/crates/leaf-cli/src/session/mod.rs index 47f4c96b87f2..9c8f4550b397 100644 --- a/crates/leaf-cli/src/session/mod.rs +++ b/crates/leaf-cli/src/session/mod.rs @@ -207,7 +207,9 @@ pub async fn classify_planner_response( message_text: String, provider: Arc, ) -> Result { - let prompt = format!("The text below is the output from an AI model which can either provide a plan or list of clarifying questions. Based on the text below, decide if the output is a \"plan\" or \"clarifying questions\".\n---\n{message_text}"); + let prompt = format!( + "The text below is the output from an AI model which can either provide a plan or list of clarifying questions. Based on the text below, decide if the output is a \"plan\" or \"clarifying questions\".\n---\n{message_text}" + ); let message = Message::user().with_text(&prompt); let model_config = provider.get_model_config(); @@ -628,6 +630,7 @@ impl CliSession { let _provider = self.agent.provider().await?; + println!(); output::run_status_hook("thinking"); output::show_thinking(); let start_time = Instant::now(); @@ -1751,7 +1754,9 @@ fn display_log_notification( let _ = progress_bars.hide(); } if !is_json_mode { - print!("{}", formatted_message); + for line in formatted_message.lines() { + println!(" {}", console::style(line).dim()); + } std::io::stdout().flush().unwrap(); } } else if ntype == "shell_output" { @@ -1766,7 +1771,7 @@ fn display_log_notification( let _ = progress_bars.hide(); } if !is_json_mode { - println!("{}", formatted_message); + println!(" {}", console::style(formatted_message).dim()); } } } diff --git a/crates/leaf-cli/src/session/output.rs b/crates/leaf-cli/src/session/output.rs index 70f236f0eb60..ec911f0945af 100644 --- a/crates/leaf-cli/src/session/output.rs +++ b/crates/leaf-cli/src/session/output.rs @@ -235,7 +235,7 @@ pub fn render_message(message: &Message, debug: bool) { }, MessageContent::Text(text) => print_markdown(&text.text, theme), MessageContent::ToolRequest(req) => render_tool_request(req, theme, debug), - MessageContent::ToolResponse(resp) => render_tool_response(resp, theme, debug), + MessageContent::ToolResponse(resp) => render_tool_response(resp, debug), MessageContent::Image(image) => { println!("Image: [data: {}, type: {}]", image.data, image.mime_type); } @@ -295,7 +295,7 @@ pub fn render_message_streaming( } MessageContent::ToolResponse(resp) => { flush_markdown_buffer(buffer, theme); - render_tool_response(resp, theme, debug); + render_tool_response(resp, debug); } MessageContent::ActionRequired(action) => { flush_markdown_buffer(buffer, theme); @@ -480,7 +480,7 @@ fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) { Ok(call) => match call.name.to_string().as_str() { name if is_shell_tool_name(name) => render_shell_request(call, debug), name if is_file_tool_name(name) => render_text_editor_request(call, debug), - "execute" | "execute_code" => render_execute_code_request(call, debug), + "execute_typescript" | "execute_code" => render_execute_code_request(call, debug), "delegate" => render_delegate_request(call, debug), "subagent" => render_delegate_request(call, debug), "todo__write" => render_todo_request(call, debug), @@ -491,7 +491,7 @@ fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) { } } -fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) { +fn render_tool_response(resp: &ToolResponse, debug: bool) { let config = Config::global(); match &resp.tool_result { @@ -519,11 +519,52 @@ fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) { if debug { println!("{:#?}", content); } else if let Some(text) = content.as_text() { - print_markdown(&text.text, theme); + print_tool_output(&text.text); } } } - Err(e) => print_markdown(&e.to_string(), theme), + Err(e) => { + println!(" {}", style(e.to_string()).red().dim()); + } + } +} + +fn print_tool_output(text: &str) { + if text.is_empty() { + return; + } + if !std::io::stdout().is_terminal() { + print!("{}", text); + return; + } + let max_lines = if get_show_full_tool_output() { + usize::MAX + } else { + 20 + }; + let lines: Vec<&str> = text.lines().collect(); + if lines.len() <= max_lines { + for line in &lines { + println!(" {}", style(line).dim()); + } + } else { + let head = max_lines / 2; + let tail = max_lines - head; + for line in &lines[..head] { + println!(" {}", style(line).dim()); + } + println!( + " {}", + style(format!( + "... ({} lines hidden, /toggle to show all)", + lines.len() - head - tail + )) + .dim() + .italic() + ); + for line in &lines[lines.len() - tail..] { + println!(" {}", style(line).dim()); + } } } @@ -822,7 +863,7 @@ pub fn render_subagent_tool_call( arguments: Option<&JsonObject>, debug: bool, ) { - if tool_name == "code_execution__execute_code" { + if tool_name == "code_execution__execute_typescript" { let tool_graph = arguments .and_then(|args| args.get("tool_graph")) .and_then(Value::as_array) @@ -851,7 +892,7 @@ fn render_subagent_tool_graph(subagent_id: &str, tool_graph: &[Value]) { " {} {} {} {} tool call{}", style("▸").dim(), style(format!("[subagent:{}]", short_id)).dim(), - style("execute_code").dim(), + style("execute_typescript").dim(), style(count).dim(), plural, ); @@ -904,6 +945,7 @@ fn print_tool_header(call: &CallToolRequestParams) { ) }; println!(); + println!(" {}", style("─".repeat(40)).dim()); println!("{}", tool_header); } diff --git a/crates/leaf-cli/src/session/task_execution_display/mod.rs b/crates/leaf-cli/src/session/task_execution_display/mod.rs index 92d3330f3603..5301723275fb 100644 --- a/crates/leaf-cli/src/session/task_execution_display/mod.rs +++ b/crates/leaf-cli/src/session/task_execution_display/mod.rs @@ -9,10 +9,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(test)] mod tests; -const CLEAR_SCREEN: &str = "\x1b[2J\x1b[H"; -const MOVE_TO_PROGRESS_LINE: &str = "\x1b[4;1H"; -const CLEAR_TO_EOL: &str = "\x1b[K"; -const CLEAR_BELOW: &str = "\x1b[J"; pub const TASK_EXECUTION_NOTIFICATION_TYPE: &str = "task_execution"; static INITIAL_SHOWN: AtomicBool = AtomicBool::new(false); @@ -85,18 +81,14 @@ fn format_tasks_update_from_event(event: &TaskExecutionNotificationEvent) -> Str let mut display = String::new(); if !INITIAL_SHOWN.swap(true, Ordering::SeqCst) { - display.push_str(CLEAR_SCREEN); display.push_str("🎯 Task Execution Dashboard\n"); display.push_str("═══════════════════════════\n\n"); - } else { - display.push_str(MOVE_TO_PROGRESS_LINE); } display.push_str(&format!( - "📊 Progress: {} total | ⏳ {} pending | 🏃 {} running | ✅ {} completed | ❌ {} failed", + "📊 Progress: {} total | ⏳ {} pending | 🏃 {} running | ✅ {} completed | ❌ {} failed\n\n", stats.total, stats.pending, stats.running, stats.completed, stats.failed )); - display.push_str(&format!("{}\n\n", CLEAR_TO_EOL)); let mut sorted_tasks = tasks.clone(); sorted_tasks.sort_by(|a, b| a.id.cmp(&b.id)); @@ -105,7 +97,6 @@ fn format_tasks_update_from_event(event: &TaskExecutionNotificationEvent) -> Str display.push_str(&format_task_display(&task)); } - display.push_str(CLEAR_BELOW); display } else { String::new() @@ -155,25 +146,22 @@ fn format_task_display(task: &TaskInfo) -> String { }; task_display.push_str(&format!( - "{} {} ({}){}\n", - status_icon, task.task_name, task.task_type, CLEAR_TO_EOL + "{} {} ({})\n", + status_icon, task.task_name, task.task_type )); if !task.task_metadata.is_empty() { - task_display.push_str(&format!( - " 📋 Parameters: {}{}\n", - task.task_metadata, CLEAR_TO_EOL - )); + task_display.push_str(&format!(" 📋 Parameters: {}\n", task.task_metadata)); } if let Some(duration_secs) = task.duration_secs { - task_display.push_str(&format!(" ⏱️ {:.1}s{}\n", duration_secs, CLEAR_TO_EOL)); + task_display.push_str(&format!(" ⏱️ {:.1}s\n", duration_secs)); } if matches!(task.status, TaskStatus::Running) && !task.current_output.trim().is_empty() { let processed_output = process_output_for_display(&task.current_output); if !processed_output.is_empty() { - task_display.push_str(&format!(" 💬 {}{}\n", processed_output, CLEAR_TO_EOL)); + task_display.push_str(&format!(" 💬 {}\n", processed_output)); } } @@ -181,7 +169,7 @@ fn format_task_display(task: &TaskInfo) -> String { if let Some(result_data) = &task.result_data { let result_preview = format_result_data_for_display(result_data); if !result_preview.is_empty() { - task_display.push_str(&format!(" 📄 {}{}\n", result_preview, CLEAR_TO_EOL)); + task_display.push_str(&format!(" 📄 {}\n", result_preview)); } } } @@ -189,14 +177,10 @@ fn format_task_display(task: &TaskInfo) -> String { if matches!(task.status, TaskStatus::Failed) { if let Some(error) = &task.error { let error_preview = safe_truncate(error, 80); - task_display.push_str(&format!( - " ⚠️ {}{}\n", - error_preview.replace('\n', " "), - CLEAR_TO_EOL - )); + task_display.push_str(&format!(" ⚠️ {}\n", error_preview.replace('\n', " "))); } } - task_display.push_str(&format!("{}\n", CLEAR_TO_EOL)); + task_display.push('\n'); task_display } diff --git a/crates/leaf-cli/src/session/task_execution_display/tests.rs b/crates/leaf-cli/src/session/task_execution_display/tests.rs index 722f3df63499..c044693f2cd6 100644 --- a/crates/leaf-cli/src/session/task_execution_display/tests.rs +++ b/crates/leaf-cli/src/session/task_execution_display/tests.rs @@ -145,7 +145,7 @@ fn test_format_tasks_update_from_event() { let result2 = format_tasks_update_from_event(&event); assert!(!result2.contains("🎯 Task Execution Dashboard")); - assert!(result2.contains(MOVE_TO_PROGRESS_LINE)); + assert!(result2.contains("📊 Progress:")); } #[test] diff --git a/crates/leaf-server/src/routes/session_events.rs b/crates/leaf-server/src/routes/session_events.rs index 814e69bfdf15..694735fef967 100644 --- a/crates/leaf-server/src/routes/session_events.rs +++ b/crates/leaf-server/src/routes/session_events.rs @@ -1,5 +1,6 @@ use crate::routes::errors::ErrorResponse; use crate::routes::reply::{get_token_state, track_tool_telemetry, MessageEvent}; +use crate::session_event_bus::RequestGuard; use crate::state::AppState; use axum::{ extract::{DefaultBodyLimit, Path, State}, @@ -320,7 +321,13 @@ pub async fn session_reply( } let bus = state.get_or_create_event_bus(&session_id).await; - let cancel_token = bus.register_request(request_id.clone()).await; + + let cancel_token = bus + .try_register_request(request_id.clone()) + .await + .map_err(|_| { + ErrorResponse::bad_request("Session already has an active request. Cancel it first.") + })?; let user_message = request.user_message; let override_conversation = request.override_conversation; @@ -332,6 +339,8 @@ pub async fn session_reply( let task_bus = bus.clone(); drop(tokio::spawn(async move { + let mut _guard = RequestGuard::new(task_bus.clone(), task_request_id.clone()); + let publish = |rid: Option, event: MessageEvent| { let bus = task_bus.clone(); async move { @@ -350,7 +359,6 @@ pub async fn session_reply( }, ) .await; - task_bus.cleanup_request(&task_request_id).await; return; } }; @@ -370,7 +378,6 @@ pub async fn session_reply( }, ) .await; - task_bus.cleanup_request(&task_request_id).await; return; } }; @@ -420,7 +427,6 @@ pub async fn session_reply( }, ) .await; - task_bus.cleanup_request(&task_request_id).await; return; } }; @@ -562,6 +568,7 @@ pub async fn session_reply( ) .await; + _guard.disarm(); task_bus.cleanup_request(&task_request_id).await; })); diff --git a/crates/leaf-server/src/session_event_bus.rs b/crates/leaf-server/src/session_event_bus.rs index e7a6dddc07c6..a24114c2918a 100644 --- a/crates/leaf-server/src/session_event_bus.rs +++ b/crates/leaf-server/src/session_event_bus.rs @@ -116,7 +116,7 @@ impl SessionEventBus { requests.keys().cloned().collect() } - /// Register a new request and return its cancellation token. + #[cfg(test)] pub async fn register_request(&self, request_id: String) -> CancellationToken { let token = CancellationToken::new(); let mut requests = self.active_requests.lock().await; @@ -124,6 +124,20 @@ impl SessionEventBus { token } + /// Atomically check no requests are active and register one. Returns Err if busy. + pub async fn try_register_request( + &self, + request_id: String, + ) -> Result { + let mut requests = self.active_requests.lock().await; + if !requests.is_empty() { + return Err("Session already has an active request".into()); + } + let token = CancellationToken::new(); + requests.insert(request_id, token.clone()); + Ok(token) + } + /// Cancel a specific request by request_id. pub async fn cancel_request(&self, request_id: &str) -> bool { let requests = self.active_requests.lock().await; @@ -156,6 +170,38 @@ impl Default for SessionEventBus { } } +pub struct RequestGuard { + bus: std::sync::Arc, + request_id: String, + disarmed: bool, +} + +impl RequestGuard { + pub fn new(bus: std::sync::Arc, request_id: String) -> Self { + Self { + bus, + request_id, + disarmed: false, + } + } + + pub fn disarm(&mut self) { + self.disarmed = true; + } +} + +impl Drop for RequestGuard { + fn drop(&mut self) { + if !self.disarmed { + let bus = self.bus.clone(); + let request_id = self.request_id.clone(); + tokio::spawn(async move { + bus.cleanup_request(&request_id).await; + }); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/leaf/src/agents/agent.rs b/crates/leaf/src/agents/agent.rs index b5e6398c9e24..e3328aa84e89 100644 --- a/crates/leaf/src/agents/agent.rs +++ b/crates/leaf/src/agents/agent.rs @@ -1533,6 +1533,11 @@ impl Agent { Err(e) => { crate::posthog::emit_error("compaction_failed", &e.to_string()); error!("Compaction failed: {}", e); + yield AgentEvent::Message( + Message::assistant().with_text( + format!("Ran into this error trying to compact: {e}.\n\nPlease try again or create a new session") + ) + ); break; } } diff --git a/crates/leaf/src/agents/extension_manager.rs b/crates/leaf/src/agents/extension_manager.rs index 32fd9e497170..1698d9cba2a2 100644 --- a/crates/leaf/src/agents/extension_manager.rs +++ b/crates/leaf/src/agents/extension_manager.rs @@ -222,6 +222,12 @@ pub fn is_first_class_extension(name: &str) -> bool { .is_some_and(|def| def.unprefixed_tools) } +pub fn is_hidden_extension(name: &str) -> bool { + PLATFORM_EXTENSIONS + .get(name_to_key(name).as_str()) + .is_some_and(|def| def.hidden) +} + /// Result of resolving a tool call to its owning extension struct ResolvedTool { extension_name: String, @@ -1543,10 +1549,10 @@ impl ExtensionManager { pub async fn search_available_extensions(&self) -> Result, ErrorData> { let mut output_parts = vec![]; - // First get disabled extensions from current config + // First get disabled extensions from current config (skip hidden ones) let mut disabled_extensions: Vec = vec![]; for extension in get_all_extensions() { - if !extension.enabled { + if !extension.enabled && !is_hidden_extension(&extension.config.name()) { let config = extension.config.clone(); let description = match &config { ExtensionConfig::Builtin { @@ -1571,9 +1577,15 @@ impl ExtensionManager { } } - // Get currently enabled extensions that can be disabled - let enabled_extensions: Vec = - self.extensions.lock().await.keys().cloned().collect(); + // Get currently enabled extensions that can be disabled (skip hidden ones) + let enabled_extensions: Vec = self + .extensions + .lock() + .await + .keys() + .filter(|name| !is_hidden_extension(name)) + .cloned() + .collect(); // Build output string if !disabled_extensions.is_empty() { diff --git a/crates/leaf/src/agents/platform_extensions/code_execution.rs b/crates/leaf/src/agents/platform_extensions/code_execution.rs index 22e0f43ae76c..26012035678b 100644 --- a/crates/leaf/src/agents/platform_extensions/code_execution.rs +++ b/crates/leaf/src/agents/platform_extensions/code_execution.rs @@ -4,11 +4,13 @@ use crate::agents::mcp_client::{Error, McpClientTrait}; use crate::agents::tool_execution::ToolCallContext; use anyhow::Result; use async_trait::async_trait; -use indoc::indoc; -use pctx_code_mode::config::ToolDisclosure; -use pctx_code_mode::model::{CallbackConfig, ExecuteInput, GetFunctionDetailsInput}; -use pctx_code_mode::registry::{CallbackFn, PctxRegistry}; -use pctx_code_mode::CodeMode; +use pctx_code_mode::{ + config::ToolDisclosure, + descriptions::{tools as tool_descriptions, workflow::get_workflow_description}, + model::{CallbackConfig, ExecuteBashInput, ExecuteInput, GetFunctionDetailsInput}, + registry::{CallbackFn, PctxRegistry}, + CodeMode, +}; use rmcp::model::{ CallToolRequestParams, CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult, RawContent, Role, ServerCapabilities, Tool as McpTool, ToolAnnotations, @@ -29,6 +31,7 @@ pub static EXTENSION_NAME: &str = "code_execution"; pub struct CodeExecutionClient { info: InitializeResult, context: PlatformExtensionContext, + disclosure: ToolDisclosure, state: RwLock>, } @@ -54,32 +57,18 @@ pub struct ExecuteWithToolGraph { } impl CodeExecutionClient { - pub fn new(context: PlatformExtensionContext) -> Result { + pub fn new(context: PlatformExtensionContext, disclosure: ToolDisclosure) -> Result { let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build()) .with_server_info( Implementation::new(EXTENSION_NAME.to_string(), "1.0.0".to_string()) .with_title("Code Mode"), ) - .with_instructions(indoc! {r#" - BATCH MULTIPLE TOOL CALLS INTO ONE execute CALL. - - This extension exists to reduce round-trips. When a task requires multiple tool calls: - - WRONG: Multiple execute calls, each with one tool - - RIGHT: One execute call with a script that calls all needed tools - - IMPORTANT: All tool calls are ASYNC. Use await for each call. - - Workflow: - 1. Use the list_functions and get_function_details tools to discover tools and signatures - 2. Write ONE script that calls ALL tools needed for the task, no need to import anything, - all the namespaces returned by list_functions and get_function_details will be available - 3. Chain results: use output from one tool as input to the next - 4. Only return and console.log data you need, tools could have very large responses. - "#}.to_string()); + .with_instructions(get_workflow_description(disclosure)); Ok(Self { info, context, + disclosure, state: RwLock::new(None), }) } @@ -95,19 +84,20 @@ impl CodeExecutionClient { .get_prefixed_tools_excluding(session_id, EXTENSION_NAME) .await .ok()?; + let mut cfgs = vec![]; for tool in tools { - let full_name = tool.name.to_string(); - let (namespace, name) = if let Some((server, tool_name)) = full_name.split_once("__") { - (server.to_string(), tool_name.to_string()) + let (name, namespace) = if let Some((prefix, tool_name)) = tool.name.split_once("__") { + (tool_name.to_string(), Some(prefix.to_string())) } else if let Some(owner) = get_tool_owner(&tool) { - (owner, full_name) + (tool.name.to_string(), Some(owner)) } else { - continue; + (tool.name.to_string(), None) }; + cfgs.push(CallbackConfig { name, - namespace: Some(namespace), + namespace, description: tool.description.as_ref().map(|d| d.to_string()), input_schema: Some(json!(tool.input_schema)), output_schema: tool.output_schema.as_ref().map(|s| json!(s)), @@ -146,10 +136,11 @@ impl CodeExecutionClient { let state = CodeModeState::new(cfgs)?; let code_mode = state.code_mode.clone(); *guard = Some(state); + Ok(code_mode) } - /// Build a CallbackRegistry with all tool callbacks registered + /// Build a PctxRegistry with all tool callbacks registered fn build_callback_registry( &self, session_id: &str, @@ -164,7 +155,14 @@ impl CodeExecutionClient { let registry = PctxRegistry::default(); for cfg in code_mode.callbacks() { - let full_name = format!("{}__{}", cfg.namespace.as_deref().unwrap_or(""), &cfg.name); + let full_name = format!( + "{}{}", + cfg.namespace + .clone() + .map(|n| format!("{n}__")) + .unwrap_or_default(), + &cfg.name + ); let callback = create_tool_callback(session_id.to_string(), full_name, manager.clone()); registry .add_callback(&cfg.id(), callback) @@ -200,8 +198,43 @@ impl CodeExecutionClient { Ok(vec![Content::text(output.code)]) } - /// Handle the execute tool call - async fn handle_execute( + /// Handle the execute bash tool call + async fn handle_execute_bash( + &self, + session_id: &str, + arguments: Option, + ) -> Result, String> { + let input: ExecuteBashInput = arguments + .map(|args| serde_json::from_value(Value::Object(args))) + .transpose() + .map_err(|e| format!("Failed to parse arguments: {e}"))? + .ok_or("Missing arguments for execute_bash")?; + let command = input.command; + let code_mode = self.get_code_mode(session_id).await?; + + // Deno runtime is not Send, so we need to run it in a blocking task + // with its own tokio runtime + let output = tokio::task::spawn_blocking(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|e| format!("Failed to create runtime: {e}"))?; + + rt.block_on(async move { + code_mode + .execute_bash(&command) + .await + .map_err(|e| format!("Typescript execution error: {e}")) + }) + }) + .await + .map_err(|e| format!("Typescript execution task failed: {e}"))??; + + Ok(vec![Content::text(output.markdown())]) + } + + /// Handle the execute typescript tool call + async fn handle_execute_typescript( &self, session_id: &str, arguments: Option, @@ -210,11 +243,12 @@ impl CodeExecutionClient { .map(|args| serde_json::from_value(Value::Object(args))) .transpose() .map_err(|e| format!("Failed to parse arguments: {e}"))? - .ok_or("Missing arguments for execute")?; + .ok_or("Missing arguments for execute_typescript")?; let code_mode = self.get_code_mode(session_id).await?; let registry = self.build_callback_registry(session_id, &code_mode)?; let code = args.input.code.clone(); + let disclosure = self.disclosure; // Deno runtime is not Send, so we need to run it in a blocking task // with its own tokio runtime @@ -226,13 +260,13 @@ impl CodeExecutionClient { rt.block_on(async move { code_mode - .execute_typescript(&code, ToolDisclosure::default(), Some(registry)) + .execute_typescript(&code, disclosure, Some(registry)) .await - .map_err(|e| format!("Execution error: {e}")) + .map_err(|e| format!("Typescript execution error: {e}")) }) }) .await - .map_err(|e| format!("Execution task failed: {e}"))??; + .map_err(|e| format!("Typescript execution task failed: {e}"))??; Ok(vec![Content::text(output.markdown())]) } @@ -316,97 +350,79 @@ impl McpClientTrait for CodeExecutionClient { })) .expect("valid schema"); - Ok(ListToolsResult { - tools: vec![ - McpTool::new( - "list_functions".to_string(), - indoc! {r#" - List all available functions across all namespaces. - - This will not return function input and output types. - After determining which functions are needed use - get_function_details to get input and output type - information about specific functions. - "#} - .to_string(), - empty_schema, - ) - .annotate(ToolAnnotations::from_raw( - Some("List functions".to_string()), - Some(true), - Some(false), - Some(true), - Some(false), - )), - McpTool::new( - "get_function_details".to_string(), - indoc! {r#" - Get detailed type information for specific functions. - - Provide a list of function identifiers in the format "Namespace.functionName" - (e.g., "Developer.shell", "Github.createIssue"). - - Returns full TypeScript interface definitions with parameter types, - return types, and descriptions for the requested functions. - "#} - .to_string(), - schema::(), - ) - .annotate(ToolAnnotations::from_raw( - Some("Get function details".to_string()), - Some(true), - Some(false), - Some(true), - Some(false), - )), - McpTool::new( - "execute".to_string(), - indoc! {r#" - Execute TypeScript code that calls available functions. - - SYNTAX - TypeScript with async run() function: - ```typescript - async function run() { - // Access functions via Namespace.functionName({ params }) — always camelCase - const files = await Developer.shell({ command: "ls -la" }); - const readme = await Developer.shell({ command: "cat ./README.md" }); - return { files, readme }; - } - ``` - - TOOL_GRAPH: Always provide tool_graph to describe the execution flow for the UI. - Each node has: tool (Namespace.functionName), description (what it does), depends_on (indices of dependencies). - Example for chained operations: - [ - {"tool": "Developer.shell", "description": "list files", "depends_on": []}, - {"tool": "Developer.shell", "description": "read README.md", "depends_on": []}, - {"tool": "Developer.write", "description": "write output.txt", "depends_on": [0, 1]} - ] - - KEY RULES: - - Code MUST define an async function named `run()` - - All function calls are async - use `await` - - Function names are always camelCase (e.g., Developer.shell, Github.listIssues, Github.createIssue) - - Return value from `run()` is the result, all `console.log()` output will be returned as well. - - Only functions from `list_functions()` and `console` methods are available — no `fetch()`, `fs`, or other Node/Deno APIs - - Variables don't persist between `execute()` calls - return or log anything you need later - - Code runs in an isolated sandbox with restricted network access - - HANDLING RETURN VALUES: - - If a function returns `any`, do NOT assume its shape - log it first: `console.log(JSON.stringify(result))` - - Many functions return wrapper objects, not raw arrays - check the response structure before calling .filter(), .map(), etc. - - Always inspect unfamiliar return values with console.log() before processing them - - TOKEN USAGE WARNING: This tool could return LARGE responses if your code returns big objects. - To minimize tokens: - - Filter/map/reduce data IN YOUR CODE before returning - - Only return specific fields you need (e.g., return {id: result.id, count: items.length}) - - Use console.log() for intermediate results instead of returning everything - - Avoid returning full API responses - extract just what you need - - BEFORE CALLING: Use list_functions or get_function_details to check available functions and their parameters. - "#} - .to_string(), + let tools = match self.disclosure { + ToolDisclosure::Catalog => { + vec![ + McpTool::new( + "list_functions".to_string(), + tool_descriptions::LIST_FUNCTIONS.to_string(), + empty_schema, + ) + .annotate(ToolAnnotations::from_raw( + Some("List functions".to_string()), + Some(true), + Some(false), + Some(true), + Some(false), + )), + McpTool::new( + "get_function_details".to_string(), + tool_descriptions::GET_FUNCTION_DETAILS.to_string(), + schema::(), + ) + .annotate(ToolAnnotations::from_raw( + Some("Get function details".to_string()), + Some(true), + Some(false), + Some(true), + Some(false), + )), + McpTool::new( + "execute_typescript".to_string(), + tool_descriptions::EXECUTE_TYPESCRIPT_CATALOG.to_string(), + schema::(), + ) + .annotate(ToolAnnotations::from_raw( + Some("Execute TypeScript".to_string()), + Some(false), + Some(true), + Some(false), + Some(true), + )), + ] + } + ToolDisclosure::Filesystem => { + vec![ + McpTool::new( + "execute_bash".to_string(), + tool_descriptions::EXECUTE_BASH.to_string(), + schema::(), + ) + .annotate(ToolAnnotations::from_raw( + Some("Get function details".to_string()), + Some(true), + Some(false), + Some(true), + Some(false), + )), + McpTool::new( + "execute_typescript".to_string(), + tool_descriptions::EXECUTE_TYPESCRIPT_FILESYSTEM.to_string(), + schema::(), + ) + .annotate(ToolAnnotations::from_raw( + Some("Execute TypeScript".to_string()), + Some(false), + Some(true), + Some(false), + Some(true), + )), + ] + } + ToolDisclosure::Sidecar => { + vec![McpTool::new( + "execute_typescript".to_string(), + tool_descriptions::EXECUTE_TYPESCRIPT_SIDECAR.to_string(), schema::(), ) .annotate(ToolAnnotations::from_raw( @@ -415,10 +431,14 @@ impl McpClientTrait for CodeExecutionClient { Some(true), Some(false), Some(true), - )), - ], - next_cursor: None, + ))] + } + }; + + Ok(ListToolsResult { meta: None, + next_cursor: None, + tools, }) } @@ -436,7 +456,8 @@ impl McpClientTrait for CodeExecutionClient { self.handle_get_function_details(session_id, arguments) .await } - "execute" => self.handle_execute(session_id, arguments).await, + "execute_bash" => self.handle_execute_bash(session_id, arguments).await, + "execute_typescript" => self.handle_execute_typescript(session_id, arguments).await, _ => Err(format!("Unknown tool: {name}")), }; @@ -454,28 +475,48 @@ impl McpClientTrait for CodeExecutionClient { async fn get_moim(&self, session_id: &str) -> Option { let code_mode = self.get_code_mode(session_id).await.ok()?; - let available: Vec<_> = code_mode - .list_functions() - .functions - .iter() - .map(|f| format!("{}.{}", &f.namespace, &f.name)) - .collect(); + + let disclosure_style_moim = match self.disclosure { + ToolDisclosure::Catalog => { + let available_fns: Vec<_> = code_mode + .list_functions() + .functions + .iter() + .map(|f| format!("{}.{}", &f.namespace, &f.name)) + .collect(); + format!("Available functions: {} + + Use the list_functions & get_function_details tools to see tool signatures and input/output types before calling execute_typescript.", available_fns.join(", ")) + } + ToolDisclosure::Filesystem => { + let available_filepaths: Vec<_> = code_mode + .virtual_fs().keys().map(String::from).collect(); + format!("Use execute_bash to search and read the tool signatures and input/output types before calling execute_typescript. The available files are: {}", available_filepaths.join(", ")) + }, + ToolDisclosure::Sidecar => "Prioritize calling tools with the execute_typescript tool, especially when multiple tools can be called in one script.".into(), + }; Some(format!( indoc::indoc! {r#" - ALWAYS batch multiple tool operations into ONE execute call. - - WRONG: Separate execute calls for read file, then write file - - RIGHT: One execute with an async run() function that reads AND writes - - Available namespaces: {} + ALWAYS batch multiple tool operations into ONE execute_typescript call. + - WRONG: Separate execute_typescript calls for read file, then write file + - RIGHT: One execute_typescript with an async run() function that reads AND writes AND logs/returns as little information as needed for the next step. - Use the list_functions & get_function_details tools to see tool signatures and input/output types before calling unfamiliar tools. + {} "#}, - available.join(", ") + disclosure_style_moim )) } } +pub fn get_tool_disclosure() -> ToolDisclosure { + let config = crate::config::Config::global(); + let tool_disclosure_str: String = config + .get_param("CODE_MODE_TOOL_DISCLOSURE") + .unwrap_or_else(|_| "catalog".to_string()); + serde_json::from_value(serde_json::json!(tool_disclosure_str)).unwrap_or_default() +} + struct CodeModeState { code_mode: CodeMode, hash: u64, diff --git a/crates/leaf/src/agents/platform_extensions/mod.rs b/crates/leaf/src/agents/platform_extensions/mod.rs index 23a484c17c9f..9813f9a097ab 100644 --- a/crates/leaf/src/agents/platform_extensions/mod.rs +++ b/crates/leaf/src/agents/platform_extensions/mod.rs @@ -3,6 +3,7 @@ pub mod apps; pub mod chatrecall; pub mod developer; pub mod ext_manager; +pub mod orchestrator; pub mod summarize; pub mod summon; pub mod todo; @@ -35,6 +36,7 @@ pub static PLATFORM_EXTENSIONS: Lazy "Analyze code structure with tree-sitter: directory overviews, file details, symbol call graphs", default_enabled: true, unprefixed_tools: true, + hidden: false, client_factory: |ctx| Box::new(analyze::AnalyzeClient::new(ctx).unwrap()), }, ); @@ -48,6 +50,7 @@ pub static PLATFORM_EXTENSIONS: Lazy "Enable a todo list for goose so it can keep track of what it is doing", default_enabled: true, unprefixed_tools: false, + hidden: false, client_factory: |ctx| Box::new(todo::TodoClient::new(ctx).unwrap()), }, ); @@ -61,6 +64,7 @@ pub static PLATFORM_EXTENSIONS: Lazy "Create and manage custom Goose apps through chat. Apps are HTML/CSS/JavaScript and run in sandboxed windows.", default_enabled: true, unprefixed_tools: false, + hidden: false, client_factory: |ctx| Box::new(apps::AppsManagerClient::new(ctx).unwrap()), }, ); @@ -74,6 +78,7 @@ pub static PLATFORM_EXTENSIONS: Lazy "Search past conversations and load session summaries for contextual memory", default_enabled: false, unprefixed_tools: false, + hidden: false, client_factory: |ctx| Box::new(chatrecall::ChatRecallClient::new(ctx).unwrap()), }, ); @@ -87,6 +92,7 @@ pub static PLATFORM_EXTENSIONS: Lazy "Enable extension management tools for discovering, enabling, and disabling extensions", default_enabled: true, unprefixed_tools: false, + hidden: false, client_factory: |ctx| Box::new(ext_manager::ExtensionManagerClient::new(ctx).unwrap()), }, ); @@ -99,6 +105,7 @@ pub static PLATFORM_EXTENSIONS: Lazy description: "Load knowledge and delegate tasks to subagents", default_enabled: true, unprefixed_tools: true, + hidden: false, client_factory: |ctx| Box::new(summon::SummonClient::new(ctx).unwrap()), }, ); @@ -111,6 +118,7 @@ pub static PLATFORM_EXTENSIONS: Lazy description: "Load files/directories and get an LLM summary in a single call", default_enabled: false, unprefixed_tools: false, + hidden: false, client_factory: |ctx| Box::new(summarize::SummarizeClient::new(ctx).unwrap()), }, ); @@ -123,10 +131,25 @@ pub static PLATFORM_EXTENSIONS: Lazy description: "Write and edit files, and execute shell commands", default_enabled: true, unprefixed_tools: true, + hidden: false, client_factory: |ctx| Box::new(developer::DeveloperClient::new(ctx).unwrap()), }, ); + map.insert( + orchestrator::EXTENSION_NAME, + PlatformExtensionDef { + name: orchestrator::EXTENSION_NAME, + display_name: "Orchestrator", + description: + "Manage agent sessions: list, view, start, send messages, interrupt, and stop agents", + default_enabled: false, + unprefixed_tools: false, + hidden: true, + client_factory: |ctx| Box::new(orchestrator::OrchestratorClient::new(ctx).unwrap()), + }, + ); + map.insert( tom::EXTENSION_NAME, PlatformExtensionDef { @@ -136,6 +159,7 @@ pub static PLATFORM_EXTENSIONS: Lazy "Inject custom context into every turn via GOOSE_MOIM_MESSAGE_TEXT and GOOSE_MOIM_MESSAGE_FILE environment variables", default_enabled: true, unprefixed_tools: false, + hidden: false, client_factory: |ctx| Box::new(tom::TomClient::new(ctx).unwrap()), }, ); @@ -193,5 +217,7 @@ pub struct PlatformExtensionDef { pub default_enabled: bool, /// If true, tools are exposed without extension prefix for intuitive first-class use. pub unprefixed_tools: bool, + /// If true, the extension is not shown in the UI or discoverable via search_available_extensions. + pub hidden: bool, pub client_factory: fn(PlatformExtensionContext) -> Box, } diff --git a/crates/leaf/src/agents/platform_extensions/orchestrator.rs b/crates/leaf/src/agents/platform_extensions/orchestrator.rs new file mode 100644 index 000000000000..57c3af3e98b9 --- /dev/null +++ b/crates/leaf/src/agents/platform_extensions/orchestrator.rs @@ -0,0 +1,635 @@ +use crate::agents::extension::PlatformExtensionContext; +use crate::agents::mcp_client::{Error, McpClientTrait}; +use crate::agents::tool_execution::ToolCallContext; +use crate::agents::{AgentEvent, SessionConfig}; +use crate::config::LeafMode; +use crate::context_mgmt::format_message_for_compacting; +use crate::conversation::message::Message; +use crate::execution::manager::AgentManager; +use crate::providers::base::Provider; +use crate::session::session_manager::SessionType; +use anyhow::Result; +use async_trait::async_trait; +use futures::StreamExt; +use rmcp::model::{ + CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult, + ServerCapabilities, Tool, +}; +use schemars::{schema_for, JsonSchema}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use std::sync::Arc; +use tokio_util::sync::CancellationToken; + +pub static EXTENSION_NAME: &str = "orchestrator"; + +struct CancelTokenGuard { + manager: Arc, + session_id: String, + disarmed: bool, +} + +impl CancelTokenGuard { + fn new(manager: Arc, session_id: String) -> Self { + Self { + manager, + session_id, + disarmed: false, + } + } + + fn disarm(&mut self) { + self.disarmed = true; + } +} + +impl Drop for CancelTokenGuard { + fn drop(&mut self) { + if !self.disarmed { + let manager = self.manager.clone(); + let session_id = self.session_id.clone(); + tokio::spawn(async move { + manager.unregister_cancel_token(&session_id).await; + }); + } + } +} + +const DEFAULT_LIST_LIMIT: usize = 10; + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +struct ListSessionsParams { + /// Filter by session type: "user", "sub_agent", "scheduled", "hidden", "terminal", "gateway". + /// If omitted, returns all session types. + session_type: Option, + /// Maximum number of sessions to return (most recent first). Defaults to 10. + last_n: Option, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +struct ViewSessionParams { + /// The session ID to inspect + session_id: String, + /// How to view the conversation: "first_last" returns the first and last message, + /// "summarize" calls the LLM to produce a summary. If omitted, returns first and last. + mode: Option, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +struct StartAgentParams { + /// Working directory for the new agent session + working_dir: String, + /// Human-readable name for the session + name: Option, + // TODO: add a "model_tier" parameter (e.g. "fast" vs "normal") to let the orchestrator + // choose between a fast/cheap model and the default one. For now we inherit the + // orchestrator's own provider and model. +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +struct SendMessageParams { + /// The session ID of the agent to send a message to + session_id: String, + /// The message text to send + message: String, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +struct InterruptAgentParams { + /// The session ID of the agent to interrupt + session_id: String, +} + +pub struct OrchestratorClient { + info: InitializeResult, + context: PlatformExtensionContext, +} + +impl OrchestratorClient { + pub fn new(context: PlatformExtensionContext) -> Result { + let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build()) + .with_server_info( + Implementation::new(EXTENSION_NAME, "1.0.0").with_title("Orchestrator"), + ) + .with_instructions( + "Manage agent sessions: list, view, start, send messages, and interrupt agents.", + ); + + Ok(Self { info, context }) + } + + async fn get_agent_manager(&self) -> Result, String> { + AgentManager::instance() + .await + .map_err(|e| format!("Failed to get agent manager: {}", e)) + } + + async fn get_provider(&self) -> Result, String> { + let extension_manager = self + .context + .extension_manager + .as_ref() + .and_then(|weak| weak.upgrade()) + .ok_or("Extension manager not available")?; + + let provider_guard = extension_manager.get_provider().lock().await; + provider_guard + .as_ref() + .cloned() + .ok_or_else(|| "Provider not available".to_string()) + } + + async fn handle_list_sessions( + &self, + arguments: Option, + ) -> Result { + let type_filter = arguments + .as_ref() + .and_then(|args| args.get("session_type")) + .and_then(|v| v.as_str()); + + let limit = arguments + .as_ref() + .and_then(|args| args.get("last_n")) + .and_then(|v| v.as_u64()) + .map(|v| v as usize) + .unwrap_or(DEFAULT_LIST_LIMIT); + + let manager = self.get_agent_manager().await?; + + let mut sessions = if let Some(type_str) = type_filter { + let session_type: SessionType = type_str + .parse() + .map_err(|e| format!("Invalid session type '{}': {}", type_str, e))?; + self.context + .session_manager + .list_sessions_by_types(&[session_type]) + .await + .map_err(|e| format!("Failed to list sessions: {}", e))? + } else { + self.context + .session_manager + .list_sessions() + .await + .map_err(|e| format!("Failed to list sessions: {}", e))? + }; + + // Most recent first + sessions.sort_by(|a, b| b.updated_at.cmp(&a.updated_at)); + let total = sessions.len(); + sessions.truncate(limit); + + if sessions.is_empty() { + return Ok(CallToolResult::success(vec![Content::text( + "No sessions found.", + )])); + } + + let active_ids = manager.list_active_session_ids().await; + + let mut lines = vec![format!( + "Showing {} of {} session(s):\n", + sessions.len(), + total + )]; + for session in &sessions { + let is_loaded = active_ids.contains(&session.id); + let is_busy = if is_loaded { + manager.is_session_busy(&session.id).await + } else { + false + }; + + let status = if is_busy { + "🔄 busy" + } else if is_loaded { + "✓ loaded" + } else { + "○ idle" + }; + + lines.push(format!( + "- **{}** ({})\n Type: {} | Status: {} | Messages: {} | Updated: {}", + session.name, + session.id, + session.session_type, + status, + session.message_count, + session.updated_at.format("%Y-%m-%d %H:%M"), + )); + } + + Ok(CallToolResult::success(vec![Content::text( + lines.join("\n"), + )])) + } + + async fn handle_view_session( + &self, + session_id_for_llm: &str, + arguments: Option, + ) -> Result { + let args = arguments.ok_or("Missing arguments")?; + let session_id = extract_string(&args, "session_id")?; + let mode = args + .get("mode") + .and_then(|v| v.as_str()) + .unwrap_or("first_last"); + + let session = self + .context + .session_manager + .get_session(&session_id, true) + .await + .map_err(|e| format!("Session '{}' not found: {}", session_id, e))?; + + let manager = self.get_agent_manager().await?; + let is_busy = manager.is_session_busy(&session_id).await; + + let mut output = vec![format!( + "# Session: {} ({})\n\nType: {} | Status: {} | Working dir: {}\nMessages: {} | Updated: {}\n", + session.name, + session.id, + session.session_type, + if is_busy { "🔄 busy" } else { "idle" }, + session.working_dir.display(), + session.message_count, + session.updated_at.format("%Y-%m-%d %H:%M"), + )]; + + match mode { + "first_last" => { + if let Some(conversation) = &session.conversation { + let messages = conversation.messages(); + if messages.is_empty() { + output.push("No messages in this session.".to_string()); + } else { + output.push("## First message\n".to_string()); + output.push(format_message_for_compacting(&messages[0])); + + if messages.len() > 1 { + output.push(format!("\n*({} messages omitted)*\n", messages.len() - 2)); + output.push("## Last message\n".to_string()); + output + .push(format_message_for_compacting(&messages[messages.len() - 1])); + } + } + } else { + output.push("No messages in this session.".to_string()); + } + } + "summarize" => { + if let Some(conversation) = &session.conversation { + let messages = conversation.messages(); + if messages.is_empty() { + output.push("No messages to summarize.".to_string()); + } else { + let summary = self + .summarize_conversation(session_id_for_llm, messages) + .await?; + output.push(format!("## Summary\n\n{}", summary)); + } + } else { + output.push("No messages to summarize.".to_string()); + } + } + other => { + return Err(format!( + "Unknown mode '{}'. Use 'first_last' or 'summarize'.", + other + )); + } + } + + Ok(CallToolResult::success(vec![Content::text( + output.join("\n"), + )])) + } + + async fn summarize_conversation( + &self, + session_id: &str, + messages: &[Message], + ) -> Result { + let provider = self.get_provider().await?; + + let conversation_text = messages + .iter() + .filter(|m| m.is_agent_visible()) + .map(format_message_for_compacting) + .collect::>() + .join("\n"); + + let system = + "You are a helpful assistant. Summarize the following conversation concisely, \ + capturing the key topics, decisions, and current state. Be brief."; + + let user_message = Message::user().with_text(format!( + "Summarize this conversation ({} messages):\n\n{}", + messages.len(), + conversation_text + )); + + let (response, _usage) = provider + .complete_fast(session_id, system, &[user_message], &[]) + .await + .map_err(|e| format!("LLM summarization failed: {}", e))?; + + Ok(response + .content + .iter() + .filter_map(|c| { + if let crate::conversation::message::MessageContent::Text(t) = c { + Some(t.text.clone()) + } else { + None + } + }) + .collect::>() + .join("\n")) + } + + async fn handle_start_agent( + &self, + arguments: Option, + ) -> Result { + let args = arguments.ok_or("Missing arguments")?; + let working_dir = extract_string(&args, "working_dir")?; + let name = args + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or("Orchestrated Agent") + .to_string(); + + let raw_path = PathBuf::from(&working_dir); + let path = if raw_path.is_absolute() { + raw_path + } else { + let base = self + .context + .session + .as_ref() + .map(|s| s.working_dir.clone()) + .unwrap_or_else(|| PathBuf::from(".")); + base.join(&raw_path) + }; + + let path = path + .canonicalize() + .map_err(|e| format!("Invalid working directory '{}': {}", working_dir, e))?; + + if !path.is_dir() { + return Err(format!("'{}' is not a directory", working_dir)); + } + + let mode = LeafMode::default(); + + let session = self + .context + .session_manager + .create_session(path, name.clone(), SessionType::User, mode) + .await + .map_err(|e| format!("Failed to create session: {}", e))?; + + let manager = self.get_agent_manager().await?; + let agent = manager + .get_or_create_agent(session.id.clone()) + .await + .map_err(|e| format!("Failed to create agent: {}", e))?; + + // Inherit the orchestrator's provider and model + let provider = self.get_provider().await?; + agent + .update_provider(provider, &session.id) + .await + .map_err(|e| format!("Failed to set provider on new agent: {}", e))?; + + Ok(CallToolResult::success(vec![Content::text(format!( + "Started agent session '{}' with ID: {}\n\nUse send_message with this session_id to interact with it.", + name, session.id + ))])) + } + + async fn handle_send_message( + &self, + parent_session_id: &str, + parent_cancel: &CancellationToken, + arguments: Option, + ) -> Result { + let args = arguments.ok_or("Missing arguments")?; + let session_id = extract_string(&args, "session_id")?; + let message_text = extract_string(&args, "message")?; + + if session_id == parent_session_id { + return Err("Cannot send a message to the orchestrator's own session".into()); + } + + let manager = self.get_agent_manager().await?; + + let agent = manager + .get_or_create_agent(session_id.clone()) + .await + .map_err(|e| format!("Failed to get agent for session '{}': {}", session_id, e))?; + + if agent.provider().await.is_err() { + if let Ok(provider) = self.get_provider().await { + agent + .update_provider(provider, &session_id) + .await + .map_err(|e| format!("Failed to set provider: {}", e))?; + } + } + + let cancel_token = CancellationToken::new(); + manager + .try_register_cancel_token(&session_id, cancel_token.clone()) + .await + .map_err(|_| { + format!( + "Session '{}' is currently busy. Use interrupt_agent first, or wait.", + session_id + ) + })?; + + let mut guard = CancelTokenGuard::new(manager.clone(), session_id.clone()); + + let user_message = Message::user().with_text(&message_text); + let session_config = SessionConfig { + id: session_id.clone(), + schedule_id: None, + max_turns: None, + retry_config: None, + }; + + let mut stream = agent + .reply(user_message, session_config, Some(cancel_token.clone())) + .await + .map_err(|e| format!("Failed to start reply: {}", e))?; + + let mut response_parts: Vec = Vec::new(); + let mut cancelled = false; + + loop { + tokio::select! { + _ = parent_cancel.cancelled() => { + cancel_token.cancel(); + cancelled = true; + break; + } + event = stream.next() => { + match event { + Some(Ok(AgentEvent::Message(msg))) => { + let text = msg.as_concat_text(); + if !text.is_empty() { + response_parts.push(text); + } + } + Some(Ok(_)) => {} + Some(Err(e)) => { + response_parts.push(format!("Error during agent processing: {}", e)); + break; + } + None => break, + } + } + } + } + + drop(stream); + guard.disarm(); + manager.unregister_cancel_token(&session_id).await; + + if cancelled { + return Err("Cancelled by parent session".into()); + } + + if response_parts.is_empty() { + Ok(CallToolResult::success(vec![Content::text( + "Agent completed without producing text output.", + )])) + } else { + Ok(CallToolResult::success(vec![Content::text(format!( + "## Response from session {}\n\n{}", + session_id, + response_parts.join("\n\n") + ))])) + } + } + + async fn handle_interrupt_agent( + &self, + arguments: Option, + ) -> Result { + let args = arguments.ok_or("Missing arguments")?; + let session_id = extract_string(&args, "session_id")?; + + let manager = self.get_agent_manager().await?; + + manager + .cancel_session(&session_id) + .await + .map_err(|e| format!("Failed to interrupt session '{}': {}", session_id, e))?; + + Ok(CallToolResult::success(vec![Content::text(format!( + "Interrupted agent session '{}'.", + session_id + ))])) + } +} + +#[async_trait] +impl McpClientTrait for OrchestratorClient { + async fn list_tools( + &self, + _session_id: &str, + _next_cursor: Option, + _cancel_token: CancellationToken, + ) -> Result { + let tools = vec![ + Tool::new( + "list_sessions".to_string(), + "List agent sessions with their status (loaded, busy, idle). Returns the most recent 10 by default. Optionally filter by session type." + .to_string(), + schema::(), + ), + Tool::new( + "view_session".to_string(), + "View a session's details and conversation. Mode 'first_last' (default) returns the first and last message. Mode 'summarize' calls the LLM to produce a conversation summary." + .to_string(), + schema::(), + ), + Tool::new( + "start_agent".to_string(), + "Start a new agent session with its own working directory. Inherits the current provider and model. Returns a session_id for future interaction." + .to_string(), + schema::(), + ), + Tool::new( + "send_message".to_string(), + "Send a message to an existing agent session and get the response. Returns an error if the agent is currently busy." + .to_string(), + schema::(), + ), + Tool::new( + "interrupt_agent".to_string(), + "Interrupt a busy agent by cancelling its current operation." + .to_string(), + schema::(), + ), + ]; + + Ok(ListToolsResult { + tools, + next_cursor: None, + meta: None, + }) + } + + async fn call_tool( + &self, + ctx: &ToolCallContext, + name: &str, + arguments: Option, + cancel_token: CancellationToken, + ) -> Result { + let result = match name { + "list_sessions" => self.handle_list_sessions(arguments).await, + "view_session" => self.handle_view_session(&ctx.session_id, arguments).await, + "start_agent" => self.handle_start_agent(arguments).await, + "send_message" => { + self.handle_send_message(&ctx.session_id, &cancel_token, arguments) + .await + } + "interrupt_agent" => self.handle_interrupt_agent(arguments).await, + _ => Err(format!("Unknown tool: {}", name)), + }; + + match result { + Ok(result) => Ok(result), + Err(error) => Ok(CallToolResult::error(vec![Content::text(format!( + "Error: {}", + error + ))])), + } + } + + fn get_info(&self) -> Option<&InitializeResult> { + Some(&self.info) + } +} + +fn schema() -> JsonObject { + let mut obj = serde_json::to_value(schema_for!(T)) + .map(|v| v.as_object().unwrap().clone()) + .expect("valid schema"); + obj.entry("properties") + .or_insert_with(|| serde_json::json!({})); + obj +} + +fn extract_string(args: &JsonObject, key: &str) -> Result { + args.get(key) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + .ok_or_else(|| format!("Missing or invalid '{}'", key)) +} diff --git a/crates/leaf/src/agents/reply_parts.rs b/crates/leaf/src/agents/reply_parts.rs index 8e1235132ec4..f830917fef47 100644 --- a/crates/leaf/src/agents/reply_parts.rs +++ b/crates/leaf/src/agents/reply_parts.rs @@ -145,17 +145,6 @@ impl Agent { tools.push(frontend_tool.tool.clone()); } - let code_execution_active = false; - if code_execution_active { - tools.retain(|tool| { - if let Some(owner) = crate::agents::extension_manager::get_tool_owner(tool) { - crate::agents::extension_manager::is_first_class_extension(&owner) - } else { - false - } - }); - } - // Stable tool ordering is important for multi session prompt caching. tools.sort_by(|a, b| a.name.cmp(&b.name)); @@ -181,7 +170,6 @@ impl Agent { .with_extensions(extensions_info.into_iter()) .with_frontend_instructions(self.frontend_instructions.lock().await.clone()) .with_extension_and_tool_counts(extension_count, tool_count) - .with_code_execution_mode(code_execution_active) .with_hints(working_dir) .with_leaf_mode(leaf_mode) .build(); diff --git a/crates/leaf/src/context_mgmt/mod.rs b/crates/leaf/src/context_mgmt/mod.rs index 7a32203189d6..fd7c785744d7 100644 --- a/crates/leaf/src/context_mgmt/mod.rs +++ b/crates/leaf/src/context_mgmt/mod.rs @@ -342,7 +342,7 @@ async fn do_compact( )) } -fn format_message_for_compacting(msg: &Message) -> String { +pub fn format_message_for_compacting(msg: &Message) -> String { let content_parts: Vec = msg .content .iter() diff --git a/crates/leaf/src/execution/manager.rs b/crates/leaf/src/execution/manager.rs index 577d427ae329..258ceb3223fe 100644 --- a/crates/leaf/src/execution/manager.rs +++ b/crates/leaf/src/execution/manager.rs @@ -7,9 +7,11 @@ use crate::scheduler_trait::SchedulerTrait; use crate::session::SessionManager; use anyhow::Result; use lru::LruCache; +use std::collections::HashMap; use std::num::NonZeroUsize; use std::sync::Arc; use tokio::sync::{OnceCell, RwLock}; +use tokio_util::sync::CancellationToken; use tracing::{debug, info}; const DEFAULT_MAX_SESSION: usize = 100; @@ -22,6 +24,7 @@ pub struct AgentManager { session_manager: Arc, default_provider: Arc>>>, default_mode: LeafMode, + cancel_tokens: Arc>>, } impl AgentManager { @@ -42,6 +45,7 @@ impl AgentManager { session_manager, default_provider: Arc::new(RwLock::new(None)), default_mode, + cancel_tokens: Arc::new(RwLock::new(HashMap::new())), }; Ok(manager) @@ -151,6 +155,9 @@ impl AgentManager { } pub async fn remove_session(&self, session_id: &str) -> Result<()> { + if let Some(token) = self.cancel_tokens.write().await.remove(session_id) { + token.cancel(); + } let mut sessions = self.sessions.write().await; sessions .pop(session_id) @@ -166,6 +173,51 @@ impl AgentManager { pub async fn session_count(&self) -> usize { self.sessions.read().await.len() } + + /// Atomically check if busy and register a cancel token. Returns Err if already busy. + pub async fn try_register_cancel_token( + &self, + session_id: &str, + token: CancellationToken, + ) -> Result<()> { + let mut tokens = self.cancel_tokens.write().await; + if tokens.contains_key(session_id) { + anyhow::bail!("Session '{}' is currently busy", session_id); + } + tokens.insert(session_id.to_string(), token); + Ok(()) + } + + /// Remove the cancellation token for a session (called when reply finishes) + pub async fn unregister_cancel_token(&self, session_id: &str) { + self.cancel_tokens.write().await.remove(session_id); + } + + /// Cancel a running agent by triggering its cancellation token + pub async fn cancel_session(&self, session_id: &str) -> Result<()> { + let tokens = self.cancel_tokens.read().await; + let token = tokens + .get(session_id) + .ok_or_else(|| anyhow::anyhow!("No active operation for session {}", session_id))?; + token.cancel(); + Ok(()) + } + + /// Check if a session has an active reply in progress + pub async fn is_session_busy(&self, session_id: &str) -> bool { + let tokens = self.cancel_tokens.read().await; + tokens.contains_key(session_id) + } + + /// List session IDs that currently have active agents loaded + pub async fn list_active_session_ids(&self) -> Vec { + self.sessions + .read() + .await + .iter() + .map(|(id, _)| id.clone()) + .collect() + } } #[cfg(test)] diff --git a/crates/leaf/src/gateway/handler.rs b/crates/leaf/src/gateway/handler.rs index 3a3618c4fd38..5d7464f68f9f 100644 --- a/crates/leaf/src/gateway/handler.rs +++ b/crates/leaf/src/gateway/handler.rs @@ -64,8 +64,7 @@ impl GatewayHandler { .send_message( &message.user, OutgoingMessage::Text { - body: "Welcome! Enter your pairing code to connect to leaf." - .into(), + body: "Welcome! Enter your pairing code to connect to leaf.".into(), }, ) .await?; diff --git a/crates/leaf/src/recipe/template_recipe.rs b/crates/leaf/src/recipe/template_recipe.rs index f127c4c66077..7897ddd87ed2 100644 --- a/crates/leaf/src/recipe/template_recipe.rs +++ b/crates/leaf/src/recipe/template_recipe.rs @@ -147,12 +147,10 @@ fn get_env_with_template_variables( undefined_behavior: UndefinedBehavior, ) -> Result<(Environment<'_>, HashSet)> { let env = add_template_in_env(content, recipe_dir, undefined_behavior)?; - let template = env.get_template(CURRENT_TEMPLATE_NAME).unwrap(); - let state = template.eval_to_state(())?; - let mut template_variables = HashSet::new(); - for (_, template) in state.env().templates() { - template_variables.extend(template.undeclared_variables(true)); - } + let template_variables = { + let template = env.get_template(CURRENT_TEMPLATE_NAME).unwrap(); + template.undeclared_variables(true) + }; Ok((env, template_variables)) } diff --git a/crates/leaf/src/session/session_manager.rs b/crates/leaf/src/session/session_manager.rs index bdc8e2920e06..858c059de3e2 100644 --- a/crates/leaf/src/session/session_manager.rs +++ b/crates/leaf/src/session/session_manager.rs @@ -311,7 +311,11 @@ impl SessionManager { } pub async fn list_sessions_by_types(&self, types: &[SessionType]) -> Result> { - self.storage.list_sessions_by_types(types).await + self.storage.list_sessions_by_types(Some(types)).await + } + + pub async fn list_all_sessions(&self) -> Result> { + self.storage.list_sessions_by_types(None).await } pub async fn delete_session(&self, id: &str) -> Result<()> { @@ -1239,12 +1243,19 @@ impl SessionStorage { Self::replace_conversation_inner(pool, session_id, conversation).await } - async fn list_sessions_by_types(&self, types: &[SessionType]) -> Result> { - if types.is_empty() { - return Ok(Vec::new()); - } + async fn list_sessions_by_types(&self, types: Option<&[SessionType]>) -> Result> { + let (where_clause, binds): (String, Vec) = match types { + Some(t) if !t.is_empty() => { + let placeholders: String = t.iter().map(|_| "?").collect::>().join(", "); + ( + format!("WHERE s.session_type IN ({})", placeholders), + t.iter().map(|t| t.to_string()).collect(), + ) + } + Some(_) => return Ok(Vec::new()), + None => (String::new(), Vec::new()), + }; - let placeholders: String = types.iter().map(|_| "?").collect::>().join(", "); let query = format!( r#" SELECT s.id, s.working_dir, s.name, s.description, s.user_set_name, s.session_type, s.created_at, s.updated_at, s.extension_data, @@ -1255,16 +1266,16 @@ impl SessionStorage { COUNT(m.id) as message_count FROM sessions s INNER JOIN messages m ON s.id = m.session_id - WHERE s.session_type IN ({}) + {} GROUP BY s.id ORDER BY s.updated_at DESC "#, - placeholders + where_clause ); let mut q = sqlx::query_as::<_, Session>(&query); - for t in types { - q = q.bind(t.to_string()); + for b in &binds { + q = q.bind(b); } let pool = self.pool().await?; @@ -1272,7 +1283,7 @@ impl SessionStorage { } async fn list_sessions(&self) -> Result> { - self.list_sessions_by_types(&[SessionType::User, SessionType::Scheduled]) + self.list_sessions_by_types(Some(&[SessionType::User, SessionType::Scheduled])) .await } diff --git a/npm/goose-acp-server-darwin-arm64/package.json b/npm/goose-acp-server-darwin-arm64/package.json index 4c659f6def43..68c6c2f8bdca 100644 --- a/npm/goose-acp-server-darwin-arm64/package.json +++ b/npm/goose-acp-server-darwin-arm64/package.json @@ -14,6 +14,6 @@ "arm64" ], "files": [ - "bin/goose-acp-server" + "bin/goose" ] } diff --git a/npm/goose-acp-server-darwin-x64/package.json b/npm/goose-acp-server-darwin-x64/package.json index f477ffa09fe7..9d04dcf08da8 100644 --- a/npm/goose-acp-server-darwin-x64/package.json +++ b/npm/goose-acp-server-darwin-x64/package.json @@ -14,6 +14,6 @@ "x64" ], "files": [ - "bin/goose-acp-server" + "bin/goose" ] } diff --git a/npm/goose-acp-server-linux-arm64/package.json b/npm/goose-acp-server-linux-arm64/package.json index 363570a1c11c..071b308551bf 100644 --- a/npm/goose-acp-server-linux-arm64/package.json +++ b/npm/goose-acp-server-linux-arm64/package.json @@ -14,6 +14,6 @@ "arm64" ], "files": [ - "bin/goose-acp-server" + "bin/goose" ] } diff --git a/npm/goose-acp-server-linux-x64/package.json b/npm/goose-acp-server-linux-x64/package.json index a6473be39630..e055534c4804 100644 --- a/npm/goose-acp-server-linux-x64/package.json +++ b/npm/goose-acp-server-linux-x64/package.json @@ -14,6 +14,6 @@ "x64" ], "files": [ - "bin/goose-acp-server" + "bin/goose" ] } diff --git a/npm/goose-acp-server-win32-x64/package.json b/npm/goose-acp-server-win32-x64/package.json index 9aea8862310a..e67f8547d3b1 100644 --- a/npm/goose-acp-server-win32-x64/package.json +++ b/npm/goose-acp-server-win32-x64/package.json @@ -14,6 +14,6 @@ "x64" ], "files": [ - "bin/goose-acp-server.exe" + "bin/goose.exe" ] } diff --git a/scripts/test_providers_code_exec.sh b/scripts/test_providers_code_exec.sh index 19243d6622fc..c9d720d202a0 100755 --- a/scripts/test_providers_code_exec.sh +++ b/scripts/test_providers_code_exec.sh @@ -27,10 +27,11 @@ run_test() { cd "$testdir" && "$GOOSE_BIN" run --text "$prompt" --with-builtin "$BUILTINS" 2>&1 ) > "$output_file" 2>&1 - # Matches: "execute | code_execution", "get_function_details | code_execution", + # Matches: "execute_typescript | code_execution", "get_function_details | code_execution", # "tool call | execute", "tool calls | execute" (old format) # "▸ execute N tool call" (new format with tool_graph) - if grep -qE "(execute \| code_execution)|(get_function_details \| code_execution)|(tool calls? \| execute)|(▸.*execute.*tool call)" "$output_file"; then + # "▸ execute_typescript" (plain tool name in output) + if grep -qE "(execute_typescript \| code_execution)|(get_function_details \| code_execution)|(tool calls? \| execute)|(▸.*execute.*tool call)|(▸ execute_typescript)" "$output_file"; then echo "success|code_execution tool called" > "$result_file" else echo "failure|no code_execution tool calls found" > "$result_file"