diff --git a/Cargo.lock b/Cargo.lock index c089de25c..ead6e2a3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,20 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -11,6 +25,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anstream" version = "0.6.21" @@ -105,18 +125,61 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +[[package]] +name = "borrow-or-share" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" + [[package]] name = "bstr" version = "1.12.1" @@ -134,6 +197,12 @@ version = "3.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6f81257d10a0f602a294ae4182251151ff97dbb504ef9afcdda4a64b24d9b4" +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytes" version = "1.11.1" @@ -147,9 +216,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.4" @@ -196,16 +273,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "coding-agent-loop" version = "0.1.0" dependencies = [ "async-trait", "futures", + "jsonschema", "libc", "serde", "serde_json", - "thiserror", + "thiserror 2.0.18", "tokio", "unified-llm", "uuid", @@ -217,6 +304,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -243,6 +340,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + [[package]] name = "difflib" version = "0.4.0" @@ -266,6 +369,21 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -291,6 +409,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "fancy-regex" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -312,6 +441,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "fluent-uri" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + [[package]] name = "fnv" version = "1.0.7" @@ -324,6 +464,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -348,6 +494,22 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7" +dependencies = [ + "lazy_static", + "num", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.32" @@ -447,6 +609,20 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + [[package]] name = "getrandom" version = "0.4.1" @@ -485,7 +661,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -493,6 +669,11 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heck" @@ -766,6 +947,38 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.85" @@ -776,6 +989,41 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonschema" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6103dcd3815c9bd04239a6d0343dac2b7a18ee260e800d0a6e3eb5b9333504fb" +dependencies = [ + "ahash", + "bytecount", + "data-encoding", + "email_address", + "fancy-regex", + "fraction", + "getrandom 0.3.4", + "idna", + "itoa", + "num-cmp", + "num-traits", + "percent-encoding", + "referencing", + "regex", + "regex-syntax", + "reqwest 0.13.2", + "rustls", + "serde", + "serde_json", + "unicode-general-category", + "uuid-simd", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "leb128fmt" version = "0.1.0" @@ -861,6 +1109,76 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -926,6 +1244,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "parking_lot" version = "0.12.5" @@ -1094,6 +1418,41 @@ dependencies = [ "bitflags", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "referencing" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a76b0c93f3dd49da2900cfb3c6b883602471a0e3e34b1578a3310ca1269d418" +dependencies = [ + "ahash", + "fluent-uri", + "getrandom 0.3.4", + "hashbrown 0.16.1", + "parking_lot", + "percent-encoding", + "serde_json", +] + [[package]] name = "regex" version = "1.12.3" @@ -1166,6 +1525,45 @@ dependencies = [ "web-sys", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -1199,6 +1597,7 @@ version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ + "aws-lc-rs", "once_cell", "rustls-pki-types", "rustls-webpki", @@ -1206,6 +1605,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -1215,12 +1626,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1238,6 +1677,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.28" @@ -1464,13 +1912,33 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1636,6 +2104,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unicode-general-category" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1660,10 +2134,10 @@ dependencies = [ "futures", "http", "rand", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-stream", "tokio-util", @@ -1726,12 +2200,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "uuid-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "wait-timeout" version = "0.2.1" @@ -1741,6 +2237,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -1890,6 +2396,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -1925,6 +2449,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -1952,6 +2485,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1985,6 +2533,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -1997,6 +2551,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -2009,6 +2569,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2033,6 +2599,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -2045,6 +2617,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -2057,6 +2635,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -2069,6 +2653,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 496dc91a6..b30530460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,4 @@ base64 = "0.22" bytes = "1" tokio-util = "0.7" clap = { version = "4", features = ["derive"] } +jsonschema = "0.42" diff --git a/crates/coding-agent-loop/Cargo.toml b/crates/coding-agent-loop/Cargo.toml index e04d9e840..6ab3745e2 100644 --- a/crates/coding-agent-loop/Cargo.toml +++ b/crates/coding-agent-loop/Cargo.toml @@ -18,6 +18,7 @@ tokio.workspace = true uuid.workspace = true futures.workspace = true async-trait.workspace = true +jsonschema.workspace = true [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/crates/coding-agent-loop/src/session.rs b/crates/coding-agent-loop/src/session.rs index 03174dc11..e08cb9498 100644 --- a/crates/coding-agent-loop/src/session.rs +++ b/crates/coding-agent-loop/src/session.rs @@ -107,11 +107,19 @@ impl Session { self.state = SessionState::Closed; } + pub fn set_reasoning_effort(&mut self, effort: Option) { + self.config.reasoning_effort = effort; + } + pub fn history(&self) -> &History { &self.history } pub async fn process_input(&mut self, input: &str) -> Result<(), AgentError> { + if self.state == SessionState::Closed { + return Err(AgentError::SessionClosed); + } + self.event_emitter.emit( EventKind::SessionStart, self.id.clone(), @@ -194,7 +202,8 @@ impl Session { // Check abort flag if self.abort_flag.load(Ordering::SeqCst) { - break; + self.state = SessionState::Closed; + return Err(AgentError::Aborted); } // Build request @@ -343,6 +352,19 @@ impl Session { let registry = self.provider_profile.tool_registry(); match registry.get(tool_name) { Some(registered_tool) => { + // Validate arguments against schema + if let Err(validation_error) = + validate_tool_args(®istered_tool.definition.parameters, arguments) + { + return ToolResult { + tool_call_id: tool_call_id.to_string(), + content: serde_json::json!(validation_error), + is_error: true, + image_data: None, + image_media_type: None, + }; + } + let executor = ®istered_tool.executor; match executor(arguments.clone(), self.execution_env.clone()).await { Ok(output) => ToolResult { @@ -429,21 +451,35 @@ impl Session { let registry = profile.tool_registry(); let result = match registry.get(&tc.name) { Some(registered_tool) => { - match (registered_tool.executor)(tc.arguments.clone(), env).await { - Ok(output) => ToolResult { + // Validate arguments against schema + if let Err(validation_error) = validate_tool_args( + ®istered_tool.definition.parameters, + &tc.arguments, + ) { + ToolResult { tool_call_id: tc.id.clone(), - content: serde_json::json!(output), - is_error: false, - image_data: None, - image_media_type: None, - }, - Err(err) => ToolResult { - tool_call_id: tc.id.clone(), - content: serde_json::json!(err), + content: serde_json::json!(validation_error), is_error: true, image_data: None, image_media_type: None, - }, + } + } else { + match (registered_tool.executor)(tc.arguments.clone(), env).await { + Ok(output) => ToolResult { + tool_call_id: tc.id.clone(), + content: serde_json::json!(output), + is_error: false, + image_data: None, + image_media_type: None, + }, + Err(err) => ToolResult { + tool_call_id: tc.id.clone(), + content: serde_json::json!(err), + is_error: true, + image_data: None, + image_media_type: None, + }, + } } } None => ToolResult { @@ -625,6 +661,29 @@ fn is_auth_error(err: &SdkError) -> bool { ) } +fn validate_tool_args(schema: &serde_json::Value, args: &serde_json::Value) -> Result<(), String> { + // Skip validation for empty/trivial schemas + if schema.is_null() + || (schema.is_object() && schema.as_object().map_or(true, |o| o.is_empty())) + { + return Ok(()); + } + + let validator = jsonschema::validator_for(schema) + .map_err(|e| format!("Invalid tool schema: {e}"))?; + + let errors: Vec = validator.iter_errors(args).map(|e| e.to_string()).collect(); + + if errors.is_empty() { + Ok(()) + } else { + Err(format!( + "Tool argument validation failed: {}", + errors.join("; ") + )) + } +} + #[cfg(test)] mod tests { use super::*; @@ -1286,7 +1345,11 @@ mod tests { let mut session = make_session_with_tools_and_config(responses, registry, config).await; // Set abort before processing session.abort(); - session.process_input("Do something").await.unwrap(); + let result = session.process_input("Do something").await; + + // Should return Aborted error and transition to Closed + assert!(matches!(result, Err(AgentError::Aborted))); + assert_eq!(session.state(), SessionState::Closed); // Should have stopped immediately: User turn only, no LLM call let turns = session.history().turns(); @@ -1294,6 +1357,63 @@ mod tests { assert!(matches!(&turns[0], Turn::User { .. })); } + #[tokio::test] + async fn abort_transitions_to_closed() { + let abort_flag = Arc::new(AtomicBool::new(false)); + let abort_flag_for_tool = abort_flag.clone(); + + // Tool that sets the abort flag when executed + let abort_tool = RegisteredTool { + definition: ToolDefinition { + name: "set_abort".into(), + description: "Sets abort flag".into(), + parameters: serde_json::json!({"type": "object"}), + }, + executor: Arc::new(move |_args, _env| { + let flag = abort_flag_for_tool.clone(); + Box::pin(async move { + flag.store(true, Ordering::SeqCst); + Ok("done".to_string()) + }) + }), + }; + + let mut registry = ToolRegistry::new(); + registry.register(abort_tool); + + let responses = vec![ + tool_call_response("set_abort", "call_1", serde_json::json!({})), + text_response("Should not reach this"), + ]; + + let provider = Arc::new(MockLlmProvider::new(responses)); + let client = make_client(provider).await; + let profile = Arc::new(TestProfile::with_tools(registry)); + let env = Arc::new(MemoryExecutionEnvironment::new()); + let config = SessionConfig { + enable_loop_detection: false, + ..Default::default() + }; + let mut session = Session::new(client, profile, env, config); + + // Wire the session's abort_flag to our shared one + session.abort_flag = abort_flag; + + let result = session.process_input("Do something").await; + + // Should return Aborted error and transition to Closed + assert!(matches!(result, Err(AgentError::Aborted))); + assert_eq!(session.state(), SessionState::Closed); + + // Should have processed: User + Assistant(tool_call) + ToolResults = 3 turns + // The tool set the abort flag, so the loop breaks before the next LLM call + let turns = session.history().turns(); + assert_eq!(turns.len(), 3); + assert!(matches!(&turns[0], Turn::User { .. })); + assert!(matches!(&turns[1], Turn::Assistant { tool_calls, .. } if tool_calls.len() == 1)); + assert!(matches!(&turns[2], Turn::ToolResults { .. })); + } + #[tokio::test] async fn auth_error_closes_session() { let error_provider = Arc::new(MockErrorProvider { @@ -1347,6 +1467,26 @@ mod tests { assert!(matches!(result.unwrap_err(), AgentError::SessionClosed)); } + #[tokio::test] + async fn closed_session_does_not_emit_session_start() { + let mut session = make_session(vec![]).await; + session.close(); + + let mut rx = session.subscribe(); + let result = session.process_input("Hello").await; + assert!(matches!(result, Err(AgentError::SessionClosed))); + + // No SessionStart event should have been emitted + let mut events = Vec::new(); + while let Ok(event) = rx.try_recv() { + events.push(event.kind.clone()); + } + assert!( + !events.contains(&EventKind::SessionStart), + "SessionStart should not be emitted for a closed session" + ); + } + // --- Parallel execution support --- struct ParallelTestProfile { @@ -1545,6 +1685,56 @@ mod tests { assert!(found_warning); } + // --- Capturing LLM Provider (captures reasoning_effort from request) --- + + struct CapturingLlmProvider { + captured_effort: Arc>>>, + } + + impl CapturingLlmProvider { + fn new(captured_effort: Arc>>>) -> Self { + Self { captured_effort } + } + } + + #[async_trait] + impl ProviderAdapter for CapturingLlmProvider { + fn name(&self) -> &str { + "mock" + } + + async fn complete(&self, request: &Request) -> Result { + *self.captured_effort.lock().unwrap() = Some(request.reasoning_effort.clone()); + Ok(text_response("captured")) + } + + async fn stream( + &self, + _request: &Request, + ) -> Result { + Err(SdkError::Configuration { + message: "streaming not supported in mock".into(), + }) + } + } + + #[tokio::test] + async fn set_reasoning_effort_mid_session() { + let captured_effort: Arc>>> = Arc::new(Mutex::new(None)); + let provider = Arc::new(CapturingLlmProvider::new(captured_effort.clone())); + let client = make_client(provider).await; + let profile = Arc::new(TestProfile::new()); + let env = Arc::new(MemoryExecutionEnvironment::new()); + let mut session = Session::new(client, profile, env, SessionConfig::default()); + + // Default reasoning_effort is None + session.set_reasoning_effort(Some("high".to_string())); + session.process_input("test").await.unwrap(); + + let effort = captured_effort.lock().unwrap().clone(); + assert_eq!(effort, Some(Some("high".to_string()))); + } + #[tokio::test] async fn context_window_no_warning_under_threshold() { let responses = vec![text_response("OK")]; @@ -1570,4 +1760,85 @@ mod tests { } assert!(!found_warning); } + + #[tokio::test] + async fn invalid_tool_args_returns_validation_error() { + let mut registry = ToolRegistry::new(); + registry.register(RegisteredTool { + definition: ToolDefinition { + name: "strict_tool".into(), + description: "Tool with required params".into(), + parameters: serde_json::json!({ + "type": "object", + "properties": { + "text": {"type": "string"} + }, + "required": ["text"] + }), + }, + executor: Arc::new(|_args, _env| { + Box::pin(async move { Ok("should not reach".to_string()) }) + }), + }); + + let responses = vec![ + tool_call_response("strict_tool", "call_1", serde_json::json!({})), + text_response("Done"), + ]; + + let mut session = make_session_with_tools(responses, registry).await; + session.process_input("Use strict tool").await.unwrap(); + + let turns = session.history().turns(); + if let Turn::ToolResults { results, .. } = &turns[2] { + assert!(results[0].is_error); + let content_str = results[0].content.to_string(); + assert!( + content_str.contains("text") && content_str.contains("required"), + "Expected validation error mentioning 'text' and 'required', got: {content_str}" + ); + } else { + panic!("Expected ToolResults turn at index 2"); + } + } + + #[tokio::test] + async fn valid_tool_args_passes_validation() { + let mut registry = ToolRegistry::new(); + registry.register(RegisteredTool { + definition: ToolDefinition { + name: "strict_tool".into(), + description: "Tool with required params".into(), + parameters: serde_json::json!({ + "type": "object", + "properties": { + "text": {"type": "string"} + }, + "required": ["text"] + }), + }, + executor: Arc::new(|_args, _env| { + Box::pin(async move { Ok("tool executed".to_string()) }) + }), + }); + + let responses = vec![ + tool_call_response( + "strict_tool", + "call_1", + serde_json::json!({"text": "hello"}), + ), + text_response("Done"), + ]; + + let mut session = make_session_with_tools(responses, registry).await; + session.process_input("Use strict tool").await.unwrap(); + + let turns = session.history().turns(); + if let Turn::ToolResults { results, .. } = &turns[2] { + assert!(!results[0].is_error); + } else { + panic!("Expected ToolResults turn at index 2"); + } + } } diff --git a/crates/coding-agent-loop/src/truncation.rs b/crates/coding-agent-loop/src/truncation.rs index 238d9d050..c33db9a0b 100644 --- a/crates/coding-agent-loop/src/truncation.rs +++ b/crates/coding-agent-loop/src/truncation.rs @@ -16,7 +16,7 @@ fn default_char_limits() -> HashMap<&'static str, usize> { m.insert("edit_file", 10_000); m.insert("write_file", 1_000); m.insert("apply_patch", 10_000); - m.insert("spawn_agent", 30_000); + m.insert("spawn_agent", 20_000); m } @@ -219,6 +219,8 @@ mod tests { assert_eq!(limits.get("glob"), Some(&20_000)); assert_eq!(limits.get("edit_file"), Some(&10_000)); assert_eq!(limits.get("write_file"), Some(&1_000)); + assert_eq!(limits.get("apply_patch"), Some(&10_000)); + assert_eq!(limits.get("spawn_agent"), Some(&20_000)); } #[test]