diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 23c4fd3b..f7f37372 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 # Installed from its own lockfile so the scanner is not itself a moving target. - run: cargo install cargo-audit --locked --version 0.22.2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c98b7371..36030984 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 with: components: clippy, rustfmt - name: Install mold linker @@ -137,7 +137,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 - run: cargo build --locked -p iris-agentic-dev - name: MCP handshake smoke test on Windows @@ -324,7 +324,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 - run: cargo build --locked --release -p iris-agentic-dev - uses: azure/login@a641126d1b8aa4d1fa005f4f92df94a3a4c4c906 # v3.1.0 @@ -373,7 +373,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 with: # Separate cache per IRIS version so a binary built against one version @@ -583,7 +583,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 with: components: llvm-tools - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 @@ -700,7 +700,7 @@ jobs: # docs/connecting.md — where the error codes, config keys, env vars, and tool # parameters are documented — had no CI check at all, which is how a per-server # allowlist that was never implemented stayed documented for two releases. - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 - name: Docs contract (docs/tools.md, docs/connecting.md, iad SKILL.md) # test_docs_contract is a module of the aggregated `unit` target now, so it needs @@ -715,7 +715,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 with: targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 @@ -734,7 +734,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 with: targets: x86_64-unknown-linux-musl - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fdd3fe91..648254da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,7 +68,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 with: targets: ${{ matrix.target }} # No rust-cache here. A release build restores nothing it did not compile itself: a cache @@ -163,7 +163,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@3da603d4464771f1a8c3dd4acb41f03bc8107551 # 1.92.0 + - uses: dtolnay/rust-toolchain@035fceb3c12fb0c6a9461b52775e16473133e591 # 1.95.0 - name: Install cargo-cyclonedx run: cargo install cargo-cyclonedx --locked --version 0.5.9 - name: Generate the SBOM diff --git a/Cargo.lock b/Cargo.lock index 1991b5ed..9e270894 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aead" version = "0.5.2" @@ -134,6 +140,30 @@ dependencies = [ "security-framework", ] +[[package]] +name = "ascot" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5e2a69f218843b9c5e0cbb18be6bcefe66725f8a1c55c4dcd3e0f7a7d16258" +dependencies = [ + "base64 0.23.1", + "clap", + "either", + "lsp-server", + "lsp-types", + "percent-encoding", + "rmcp", + "schemars", + "serde", + "serde_json", + "tokio", + "tygr", + "tygr-objectscript", + "ureq", + "url", + "wit-bindgen 0.62.0", +] + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -301,9 +331,15 @@ checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bitflags" -version = "2.11.0" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "block-buffer" @@ -465,9 +501,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" dependencies = [ "clap_builder", "clap_derive", @@ -475,26 +511,27 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.5", ] [[package]] @@ -509,6 +546,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -538,6 +585,7 @@ dependencies = [ "cookie", "document-features", "idna", + "indexmap", "log", "publicsuffix", "serde", @@ -581,6 +629,24 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba106e844045ddc02c20768ac4b0b7cb45f2cc2ecbd833ac9f5dacb183c2055" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -637,7 +703,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] @@ -648,7 +714,7 @@ checksum = "68f5792fa0d41cd2325ce0ffa64f0a340eaebd4971a3a0c5e1ffd2cc488a355e" dependencies = [ "darling_core", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] @@ -836,6 +902,26 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "fnv" version = "1.0.7" @@ -848,6 +934,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 = "form_urlencoded" version = "1.2.2" @@ -1053,14 +1145,17 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -1266,7 +1361,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -1394,12 +1489,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -1465,6 +1560,7 @@ dependencies = [ "aes-gcm", "anyhow", "apple-native-keyring-store", + "ascot", "base64 0.22.1", "bollard", "chrono", @@ -1513,6 +1609,55 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "js-sys" version = "0.3.91" @@ -1627,6 +1772,43 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "lsp-server" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee25a31f2e571e426eef2896179450cafc7e2f5be00d8a93b1c2d21c0ff7656" +dependencies = [ + "crossbeam-channel", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "lsp-types" +version = "0.97.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53353550a17c04ac46c585feb189c2db82154fc84b79c7a66c96c2c644f66071" +dependencies = [ + "bitflags 1.3.2", + "fluent-uri", + "serde", + "serde_json", + "serde_repr", +] + +[[package]] +name = "macro-string" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e7b3a5bbb2f63aaa223a671ea1bd0e7bc16bd30ce387324cf63995deaddbbd2" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + [[package]] name = "matchers" version = "0.2.0" @@ -1651,11 +1833,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" -version = "1.1.1" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" dependencies = [ "libc", "wasi", @@ -1930,6 +2122,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "prettyplease" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" +dependencies = [ + "proc-macro2", + "syn 3.0.5", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -2026,7 +2228,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -2120,7 +2322,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.13.2", ] [[package]] @@ -2239,9 +2441,9 @@ dependencies = [ [[package]] name = "rmcp" -version = "3.1.3" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f17072af977b0f86f714dbd64b3d37d0715bb63064f9d13483f0a1775813374" +checksum = "ab5023804cbe0304cbe06197ac16e645a0a0d0ffa2953bf735c15c2fa0213350" dependencies = [ "async-trait", "base64 0.23.1", @@ -2271,15 +2473,15 @@ dependencies = [ [[package]] name = "rmcp-macros" -version = "3.1.3" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54817231d63a35330d5c3a5782a4a166fbe3b914a9faab440d3dd8bd37971890" +checksum = "967fa7f7935d7d6217d593df0e8e5c9307bcaf256974a5a9ee64476c97735389" dependencies = [ "darling", "proc-macro2", "quote", "serde_json", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] @@ -2288,7 +2490,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags", + "bitflags 2.13.2", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2302,17 +2504,26 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2321,7 +2532,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.12.1", @@ -2334,6 +2545,7 @@ version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -2364,6 +2576,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + [[package]] name = "rustls-webpki" version = "0.103.16" @@ -2387,6 +2626,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.29" @@ -2398,9 +2646,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "chrono", "dyn-clone", @@ -2412,14 +2660,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.117", + "syn 3.0.5", ] [[package]] @@ -2453,7 +2701,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.13.2", "core-foundation", "core-foundation-sys", "libc", @@ -2512,13 +2760,13 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.5", ] [[package]] @@ -2614,6 +2862,28 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -2692,9 +2962,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", @@ -2734,6 +3004,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "terminal_size" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" +dependencies = [ + "rustix 1.1.4", + "windows-sys 0.61.2", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2841,9 +3121,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -2858,13 +3138,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.5", ] [[package]] @@ -3009,7 +3289,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags", + "bitflags 2.13.2", "bytes", "futures-util", "http", @@ -3161,6 +3441,38 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tygr" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e1b0d0e8e9d559c19ab6bf14df937ad31a8894204874bc3285ba5a2976b559" +dependencies = [ + "either", + "tygr-derive", +] + +[[package]] +name = "tygr-derive" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9fd39d2505d05056a142f30fc44e75503d311bc587c61bc90c259f1a294447" +dependencies = [ + "either", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tygr-objectscript" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d699c80c6d8d0801b812b22dd6a15ba4568b48665f7d23f049d403c8758f302d" +dependencies = [ + "either", + "tygr", +] + [[package]] name = "typenum" version = "1.20.1" @@ -3206,6 +3518,40 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde93ff05758a36a851a398a772b0b79a82fbe3c186a46f2696baaeaf84c966b" +dependencies = [ + "base64 0.23.1", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "ureq-proto", + "url", + "utf8-zero", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841583fc36089d7d59419568ae157f43a5a03a2e5a81ba87c77d1596ef46e922" +dependencies = [ + "base64 0.23.1", + "http", + "httparse", + "log", +] + [[package]] name = "url" version = "2.5.8" @@ -3230,6 +3576,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -3272,6 +3624,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[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" @@ -3293,7 +3655,7 @@ version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] @@ -3302,7 +3664,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] @@ -3371,7 +3733,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ "leb128fmt", - "wasmparser", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d0246511d901aacf25d2dc9111f0054947de5e093fe662099e709ff7530dc3" +dependencies = [ + "leb128fmt", + "wasmparser 0.259.0", ] [[package]] @@ -3382,8 +3754,20 @@ checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", "indexmap", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7bf119eb01f4a246864c10bd87c8f26c566350abb25aa4d5273c415bbab338" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.259.0", + "wasmparser 0.259.0", ] [[package]] @@ -3392,12 +3776,24 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags", + "bitflags 2.13.2", "hashbrown 0.15.5", "indexmap", "semver", ] +[[package]] +name = "wasmparser" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7c12eac7bb587801590f6a67ff0bc84d0748513864b71108e4b311cc3df694" +dependencies = [ + "bitflags 2.13.2", + "hashbrown 0.17.1", + "indexmap", + "semver", +] + [[package]] name = "web-sys" version = "0.3.91" @@ -3418,6 +3814,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "6.0.3" @@ -3446,6 +3860,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3458,7 +3881,7 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core 0.58.0", + "windows-core", "windows-targets 0.52.6", ] @@ -3468,26 +3891,13 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-implement" version = "0.58.0" @@ -3499,17 +3909,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "windows-interface" version = "0.58.0" @@ -3521,17 +3920,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "windows-link" version = "0.2.1" @@ -3560,34 +3948,16 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result 0.2.0", + "windows-result", "windows-targets 0.52.6", ] -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -3606,24 +3976,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -3657,30 +4009,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3693,12 +4028,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3711,12 +4040,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3729,24 +4052,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3759,12 +4070,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3777,12 +4082,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3795,12 +4094,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3813,12 +4106,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.15" @@ -3872,7 +4159,17 @@ version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" dependencies = [ - "wit-bindgen-rust-macro", + "wit-bindgen-rust-macro 0.51.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cb4b5556c3a791e86838ea287782bdafa704d55b0e68b5b81a3a16b9ea5f4b" +dependencies = [ + "bitflags 2.13.2", + "wit-bindgen-rust-macro 0.62.0", ] [[package]] @@ -3883,7 +4180,18 @@ checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", "heck", - "wit-parser", + "wit-parser 0.244.0", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ad22a37ecbc0e1fdca34d2b670ba41368cb0920735643e00fdf39a16ee5431" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.259.0", ] [[package]] @@ -3895,11 +4203,27 @@ dependencies = [ "anyhow", "heck", "indexmap", - "prettyplease", + "prettyplease 0.2.37", "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", + "wasm-metadata 0.244.0", + "wit-bindgen-core 0.51.0", + "wit-component 0.244.0", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84ba5213d4332c260a0d4e69e34b0f7e4b85ddae10b6ebfbac00d9c185ba628a" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease 0.3.0", + "syn 3.0.5", + "wasm-metadata 0.259.0", + "wit-bindgen-core 0.62.0", + "wit-component 0.259.0", ] [[package]] @@ -3909,12 +4233,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" dependencies = [ "anyhow", - "prettyplease", + "prettyplease 0.2.37", "proc-macro2", "quote", "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", + "wit-bindgen-core 0.51.0", + "wit-bindgen-rust 0.51.0", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f19d63da8e478a370ef8192d90ccb517965b7747d6b46cd2d831dd0ef4d2541" +dependencies = [ + "anyhow", + "macro-string", + "prettyplease 0.3.0", + "proc-macro2", + "quote", + "syn 3.0.5", + "wit-bindgen-core 0.62.0", + "wit-bindgen-rust 0.62.0", ] [[package]] @@ -3924,16 +4264,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.13.2", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.244.0", + "wasm-metadata 0.244.0", + "wasmparser 0.244.0", + "wit-parser 0.244.0", +] + +[[package]] +name = "wit-component" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092f783dee3253265fcde131475055f8c275ac0176a9c0396db1dc7b5544bfb4" +dependencies = [ + "anyhow", + "bitflags 2.13.2", "indexmap", "log", "serde", "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", + "wasm-encoder 0.259.0", + "wasm-metadata 0.259.0", + "wasmparser 0.259.0", + "wit-parser 0.259.0", ] [[package]] @@ -3951,7 +4310,26 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser", + "wasmparser 0.244.0", +] + +[[package]] +name = "wit-parser" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6390f4f02493ce678d509c859cf1698e38929fb75c8012a6ee2f7d6b6cb66cd7" +dependencies = [ + "anyhow", + "hashbrown 0.17.1", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-ident", + "wasmparser 0.259.0", ] [[package]] @@ -4135,6 +4513,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + [[package]] name = "zmij" version = "1.0.21" diff --git a/README.md b/README.md index 828050be..db757835 100644 --- a/README.md +++ b/README.md @@ -602,6 +602,7 @@ blocked and how to permit it. | `iris_execute_method` | Invoke a `ClassMethod` directly by class+method+args, no boilerplate. | | `iris_query` | Execute SQL, return rows as JSON. `mode=explain\|count\|write` for plans, counts, and gated DML. | | `iris_test` | Run `%UnitTest` tests, return structured pass/fail results. | +| `iris_check` | Check one document with [ascot](https://github.com/intersystems-community/ascot), no compile. | | `iris_global` | Read, write, kill, or list IRIS global nodes. Patient-data and system globals are gated. | | `iris_coverage` | Measure ObjectScript line coverage via `%Monitor.System.LineByLine`. | | `iris_source_control` ✦ | Check lock status, checkout, execute SCM actions. | diff --git a/coverage-floors.toml b/coverage-floors.toml index 61153ba0..0b30b33e 100644 --- a/coverage-floors.toml +++ b/coverage-floors.toml @@ -111,6 +111,7 @@ overall = 88 # `overall` floor, which is measured with IRIS up. "src/tools/admin.rs" = 69 "src/tools/admin_tools.rs" = 12 +"src/tools/check.rs" = 4 "src/tools/comparison_tools.rs" = 11 "src/tools/coverage.rs" = 21 "src/tools/dict.rs" = 53 diff --git a/crates/iris-agentic-dev-bin/src/cmd/tool.rs b/crates/iris-agentic-dev-bin/src/cmd/tool.rs index d008d325..b62545c4 100644 --- a/crates/iris-agentic-dev-bin/src/cmd/tool.rs +++ b/crates/iris-agentic-dev-bin/src/cmd/tool.rs @@ -34,6 +34,7 @@ pub const TOOL_NAMES: &[&str] = &[ "iris_add_server", "iris_admin", "iris_business_rule_info", + "iris_check", "iris_compile", "iris_containers", "iris_coverage", diff --git a/crates/iris-agentic-dev-bin/src/main.rs b/crates/iris-agentic-dev-bin/src/main.rs index 834330d4..a34fbbe6 100644 --- a/crates/iris-agentic-dev-bin/src/main.rs +++ b/crates/iris-agentic-dev-bin/src/main.rs @@ -10,6 +10,8 @@ use tracing_subscriber::EnvFilter; version, about = "CLI and package manager for InterSystems IRIS developer ecosystem", long_about = None, + // Source formatting: a dependency turning on clap's `wrap_help` must not rewrap this help. + term_width = 0, )] struct Cli { #[command(subcommand)] diff --git a/crates/iris-agentic-dev-core/Cargo.toml b/crates/iris-agentic-dev-core/Cargo.toml index 768e5a1f..d3dd8987 100644 --- a/crates/iris-agentic-dev-core/Cargo.toml +++ b/crates/iris-agentic-dev-core/Cargo.toml @@ -44,6 +44,8 @@ futures-util = { version = "0.3", features = ["sink"] } base64 = "0.22" aes-gcm = "0.10" similar = "2" +# 0.4.0 is the first release with the host `Atelier` interface `tools/check.rs` implements. +ascot = "0.4.2" # Platform keychain store crates — already in the dep tree via keyring v4. diff --git a/crates/iris-agentic-dev-core/build.rs b/crates/iris-agentic-dev-core/build.rs index 969a6b94..7372af0d 100644 --- a/crates/iris-agentic-dev-core/build.rs +++ b/crates/iris-agentic-dev-core/build.rs @@ -40,6 +40,17 @@ fn main() { }; println!("cargo:rustc-env=IRIS_AGENTIC_DEV_BUILD_SUFFIX={suffix}"); - println!("cargo:rerun-if-changed=.git/HEAD"); - println!("cargo:rerun-if-changed=.git/refs"); + // Resolved through git: `.git` is two directories up from this crate, and a rerun path that + // does not exist counts as always changed, which recompiled the crate on every build. + let git_dir = Command::new("git") + .args(["rev-parse", "--absolute-git-dir"]) + .current_dir(&manifest_dir) + .output() + .ok() + .filter(|o| o.status.success()) + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| ".git".to_string()); + println!("cargo:rerun-if-changed={git_dir}/HEAD"); + println!("cargo:rerun-if-changed={git_dir}/refs"); } diff --git a/crates/iris-agentic-dev-core/src/iris/server_manager.rs b/crates/iris-agentic-dev-core/src/iris/server_manager.rs index 313f33a5..21a816ec 100644 --- a/crates/iris-agentic-dev-core/src/iris/server_manager.rs +++ b/crates/iris-agentic-dev-core/src/iris/server_manager.rs @@ -976,6 +976,7 @@ fn tool_to_category(tool_name: &str) -> Option ToolCategory::Execute, "iris_query" => ToolCategory::Query, "iris_search" | "iris_symbols" | "iris_symbols_local" => ToolCategory::Search, + "iris_check" => ToolCategory::Docs, "docs_introspect" | "iris_doc" => ToolCategory::Docs, "iris_source_control" => ToolCategory::SourceControl, "debug_capture_packet" diff --git a/crates/iris-agentic-dev-core/src/tools/check.rs b/crates/iris-agentic-dev-core/src/tools/check.rs new file mode 100644 index 00000000..8c5290ba --- /dev/null +++ b/crates/iris-agentic-dev-core/src/tools/check.rs @@ -0,0 +1,198 @@ +//! `iris_check`: ascot's static analysis of one file, with iad's connection answering the lookups. +//! +//! ascot asks its `Atelier` trait for what the file refers to but does not define, as two REST +//! requests iad already makes: `/action/query` and `/doc/{name}`. The trait is synchronous and the +//! connection async, so the check runs on a blocking thread and drives each request through the +//! runtime handle. The context is fresh per call: ascot caches lookups for an hour, and an agent +//! that edits a class on the server and then checks a caller of it must see the edit. + +use std::sync::Arc; + +use ascot::bindings::{Diagnostic, DiagnosticSeverity, GuestWorkspace}; +use ascot::{Atelier, AtelierContext, IrisContext, Workspace}; +use rmcp::model::CallToolResult; +use rmcp::ErrorData as McpError; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use super::output_schemas::ToolError; +use super::{err_json, nopws, ok_json, IrisTools}; +use crate::iris::connection::IrisConnection; + +/// Parameters of `iris_check`. +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct CheckParams { + /// The document to check, with its extension: `Pkg.Class.cls`, `Routine.mac`, `.int` or `.inc`. + pub name: String, + /// The document's text, to check code before saving it. Omit to check the document as stored + /// in the target namespace. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source: Option, + /// Namespace that resolves the document's references. Omit to use the connection's. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + /// Route this call to a named registered IRIS instance. If omitted, uses the default connection. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server: Option, +} + +#[derive(Debug, Serialize, JsonSchema)] +#[serde(untagged)] +pub enum IrisCheckResponse { + Ok(CheckOk), + Err(ToolError), +} + +#[derive(Debug, Serialize, JsonSchema)] +pub struct CheckOk { + pub success: bool, + pub name: String, + /// Whether a connection resolved the document's references; without one they are hints. + pub iris: bool, + /// Present when `iris` is false: what was not checked, and how to connect. + #[serde(skip_serializing_if = "Option::is_none")] + pub note: Option, + pub namespace: String, + pub diagnostics: Vec, + pub errors: usize, + pub warnings: usize, + pub hints: usize, +} + +/// iad's connection as the two requests ascot asks for. +pub struct IadAtelier { + conn: Arc, + http: reqwest::Client, + namespace: String, + handle: tokio::runtime::Handle, +} + +impl Atelier for IadAtelier { + fn query(&self, sql: &str, params: &[&str]) -> Result { + let params = params.iter().map(|p| Value::from(*p)).collect(); + let body = self + .handle + .block_on(self.conn.query(sql, params, &self.namespace, &self.http)) + .map_err(|e| format!("query in {}: {e}", self.namespace))?; + match body.pointer("/result/content") { + Some(rows) if rows.is_array() => Ok(rows.to_string()), + _ => Err(format!("query answered with no rows: {}", body["status"])), + } + } + + fn get_doc(&self, name: &str) -> Result, String> { + let path = format!("/doc/{}", urlencoding::encode(name)); + let url = self.conn.versioned_ns_url(&self.namespace, &path); + let err = |e: &dyn std::fmt::Display| format!("GET {url}: {e}"); + self.handle.block_on(async { + let resp = self + .http + .get(&url) + .basic_auth(&self.conn.username, Some(&self.conn.password)) + .send() + .await + .map_err(|e| err(&e))?; + if resp.status() == reqwest::StatusCode::NOT_FOUND { + return Ok(None); + } + let body: Value = + crate::iris::http::capped_json(resp.error_for_status().map_err(|e| err(&e))?) + .await + .map_err(|e| err(&e))?; + let lines: Vec<&str> = body["result"]["content"] + .as_array() + .map(|a| a.iter().filter_map(Value::as_str).collect()) + .unwrap_or_default(); + Ok((!lines.is_empty()).then(|| lines.join("\n"))) + }) + } +} + +/// The diagnostics of one document, `atelier` resolving what it does not define. +pub fn check(uri: &str, text: &str, atelier: Option) -> Vec { + let iris = atelier.map(|a| Box::new(AtelierContext::from(a)) as Box); + let workspace = Workspace::native(iris, None); + workspace.open(uri.to_string(), text.to_string()); + workspace.diagnostics(uri.to_string()) +} + +pub(crate) async fn handle_iris_check( + tools: &IrisTools, + name: &str, + source: Option<&str>, + namespace: Option<&str>, +) -> Result { + // The extension decides how ascot parses the document, so an unknown one is refused up front. + let ext = name.rsplit_once('.').map(|(_, e)| e.to_ascii_lowercase()); + if !matches!(ext.as_deref(), Some("cls" | "mac" | "int" | "inc")) { + return err_json( + "INVALID_PARAMS", + &format!("name must end in .cls, .mac, .int or .inc, got {name}"), + ); + } + let namespace = tools.target_ns(namespace)?; + // Lookups go through the target connection when it speaks Atelier (a docker-only one cannot). + let atelier = match tools + .target_conn_opt()? + .filter(|conn| !nopws::atelier_unavailable(conn)) + { + Some(conn) => Some(IadAtelier { + conn, + http: tools.http_client()?.clone(), + namespace: namespace.clone(), + handle: tokio::runtime::Handle::current(), + }), + None => None, + }; + let iris = atelier.is_some(); + if source.is_none() && !iris { + return Err(super::iris_unreachable()); + } + let (doc, source) = (name.to_string(), source.map(str::to_string)); + // ascot is synchronous and its lookups block on the runtime, so it runs off the worker threads. + let diagnostics = match tokio::task::spawn_blocking(move || { + // Without `source` the document comes from the server, through the same request ascot uses. + let text = match source { + Some(text) => Some(text), + None => match atelier.as_ref().map(|a| a.get_doc(&doc)) { + Some(Ok(text)) => text, + Some(Err(e)) => return Err(e), + None => None, + }, + }; + Ok(text.map(|text| check(&doc, &text, atelier))) + }) + .await + .map_err(|e| McpError::internal_error(format!("check: {e}"), None))? + { + Ok(Some(diagnostics)) => diagnostics, + Ok(None) => { + return err_json( + "DOCUMENT_NOT_FOUND", + &format!("no document {name} in {namespace}"), + ) + } + Err(e) => return err_json("IRIS_UNREACHABLE", &e), + }; + use DiagnosticSeverity as S; + let n = |s: S| diagnostics.iter().filter(|d| d.severity == s).count(); + let ok = CheckOk { + success: true, + name: name.to_string(), + iris, + note: (!iris).then(|| { + "No IRIS connection, so what this document refers to but does not define was not \ + checked and appears as hints. Set the IRIS_HOST and IRIS_WEB_PORT environment \ + variables, or register a server with iris_add_server, to check it." + .to_string() + }), + namespace, + errors: n(S::Error), + warnings: n(S::Warning), + hints: n(S::Hint), + diagnostics, + }; + ok_json(serde_json::to_value(ok).unwrap_or(Value::Null)) +} diff --git a/crates/iris-agentic-dev-core/src/tools/mod.rs b/crates/iris-agentic-dev-core/src/tools/mod.rs index ee8c0bfd..9d494f95 100644 --- a/crates/iris-agentic-dev-core/src/tools/mod.rs +++ b/crates/iris-agentic-dev-core/src/tools/mod.rs @@ -96,6 +96,7 @@ pub use crate::iris::connection::{mcp_peer, MCP_PEER}; pub mod admin; pub mod admin_tools; +pub mod check; pub mod comparison_tools; pub mod coverage; pub mod dict; @@ -141,16 +142,16 @@ pub use scm::{ScmAction, ScmParams}; /// happened and how it's now derived instead of hand-maintained.) #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Toolset { - /// 84 tools — 93 total `#[tool]` methods minus the 9 that are Merged-tier-only + /// 85 tools — 94 total `#[tool]` methods minus the 9 that are Merged-tier-only /// dispatchers (iris_admin, iris_debug, iris_containers, iris_get_log, iris_global, /// iris_execute_method, iris_message_body, iris_business_rule_info, /// iris_production_diff — added by later specs and deliberately scoped to Merged /// rather than the original tool surface). Default when `IRIS_TOOLSET` is unset. Baseline, - /// 80 tools — Baseline (84) minus the 4 stub tools (skill_propose/skill_optimize/ + /// 81 tools — Baseline (85) minus the 4 stub tools (skill_propose/skill_optimize/ /// skill_share/skill_community_install). Nostub, - /// 81 tools — 93 total minus the 4 stubs minus 8 tools replaced by 2 consolidated + /// 82 tools — 94 total minus the 4 stubs minus 8 tools replaced by 2 consolidated /// dispatchers (4 debug_* tools → iris_debug; agent_info/iris_list_containers/ /// iris_select_container/iris_start_sandbox → iris_containers). The 9 /// Merged-tier-only dispatchers from Baseline's note above are present here, which @@ -6250,6 +6251,23 @@ impl IrisTools { })) } + #[tool( + description = "Check an ObjectScript document before compilation, powered by intersystems-community/ascot. ascot (https://github.com/intersystems-community/ascot) is a static analyzer and flow-sensitive type checker; it reports undefined labels and macros, unknown classes, methods and properties, wrong argument counts, unreachable code. name: the document with its extension (Pkg.Class.cls, Routine.mac, .int, .inc). source: the document's text, to check code before saving it; omitted, the document is read as stored in the target namespace. What the document refers to but does not define (superclasses, called classes and routines, included .inc files) is resolved against the target namespace through this connection with read-only Atelier requests; no code runs on IRIS. Without a connection those references are reported as hints, not checked, and the response says so in `note`. Returns LSP-style diagnostics: zero-based range, severity (Error, Warning, Information, Hint), a fixed code and its docs.rs page. Skill: objectscript-tdd. `server` (optional): name of a registered IRIS instance. If omitted, uses the default connection. Use `iris_servers` to list available instances.", + + annotations(read_only_hint = true), + output_schema = output_schemas::oneof_output_schema::() + )] + async fn iris_check( + &self, + Parameters(p): Parameters, + ) -> Result { + let result = + check::handle_iris_check(self, &p.name, p.source.as_deref(), p.namespace.as_deref()) + .await; + self.record_call("iris_check", result.is_ok()); + result + } + #[tool( description = "Introspect an ObjectScript class — returns methods, properties, and type information. Methods include FormalSpec as a structured array of {name, type, byref, output, default} objects and a ReturnType field. For BPL and DTL classes, an xdata_flow field describes the process steps (BPL: kind=bpl, steps array with Call/Code/If/Other entries, has_dynamic_dispatch flag; DTL: kind=dtl, source_class, target_class, subtransforms, assign_count). Skill: objectscript-navigation. `server` (optional): name of a registered IRIS instance. If omitted, uses the default connection. Use `iris_servers` to list available instances.", annotations(read_only_hint = true), @@ -10202,10 +10220,10 @@ impl ServerHandler for IrisTools { .await } - fn get_info(&self) -> ServerInfo { + fn get_info(&self) -> ServerConfig { // Cap to 2025-11-25: the 2026-07-28 draft requires per-tool cache metadata that // clients using that version will reject when absent. Pin until we opt into caching. - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) .with_protocol_version(ProtocolVersion::V_2025_11_25) .with_server_info(Implementation::new( "iris-agentic-dev".to_string(), @@ -12019,6 +12037,7 @@ impl IrisTools { }; } dispatch!("iris_compile", CompileParams, iris_compile); + dispatch!("iris_check", check::CheckParams, iris_check); dispatch!("iris_execute", ExecuteParams, iris_execute); dispatch!("iris_test", TestParams, iris_test); dispatch!("iris_query", QueryParams, iris_query); diff --git a/crates/iris-agentic-dev-core/src/tools/write_gate.rs b/crates/iris-agentic-dev-core/src/tools/write_gate.rs index 208ebf7d..73fa174c 100644 --- a/crates/iris-agentic-dev-core/src/tools/write_gate.rs +++ b/crates/iris-agentic-dev-core/src/tools/write_gate.rs @@ -699,6 +699,7 @@ pub const CLASSIFICATION: &[ToolClass] = &[ .local_default("lists the server registry from the launch settings and files on disk"), ro("iris_symbols"), ro("iris_symbols_local").local("parses source files under the project directory on the host"), + ro("iris_check"), mixed( "iris_system_performance", &[ diff --git a/crates/iris-agentic-dev-core/tests/binary/check_ascot.rs b/crates/iris-agentic-dev-core/tests/binary/check_ascot.rs new file mode 100644 index 00000000..46ab3b79 --- /dev/null +++ b/crates/iris-agentic-dev-core/tests/binary/check_ascot.rs @@ -0,0 +1,60 @@ +//! `iris_check` over stdio: its advertised contract, and one check of inline source with no IRIS +//! configured. + +use iris_agentic_dev_core::testing::{ + assert_advertised_contracts, assert_source_contracts, require_iad_binary, McpSession, + ParamContract, +}; + +const CONTRACT: &[ParamContract] = &[ParamContract { + tool: "iris_check", + params: &[ + ("name", "string"), + ("source", "string"), + ("namespace", "string"), + ("server", "string"), + ], + required: &["name"], +}]; + +#[test] +#[ignore = "spawns the built binary; run with --include-ignored"] +fn iris_check_advertises_its_contract() { + let Some(_bin) = require_iad_binary() else { + return; + }; + assert_advertised_contracts(CONTRACT); +} + +#[test] +fn iris_check_reads_what_it_advertises() { + assert_source_contracts(CONTRACT); +} + +#[test] +#[ignore = "spawns the built binary; run with --include-ignored"] +fn inline_source_is_checked_without_iris() { + let Some(_bin) = require_iad_binary() else { + return; + }; + let answer = McpSession::start(&[]).call( + "iris_check", + &serde_json::json!({"name": "Test.mac", "source": "ROUTINE Test [Type=MAC]\n Do Missing\n Quit\n"}), + ); + let text = answer["result"]["content"][0]["text"] + .as_str() + .unwrap_or_else(|| panic!("no text content: {answer}")); + let b: serde_json::Value = serde_json::from_str(text).expect("JSON body"); + assert_eq!(b["success"], true, "{b}"); + assert_eq!(b["iris"], false, "{b}"); + assert!( + b["note"] + .as_str() + .unwrap_or("") + .contains("No IRIS connection"), + "{b}" + ); + assert_eq!(b["errors"], 1, "{b}"); + assert_eq!(b["diagnostics"][0]["severity"], "Error", "{b}"); + assert_eq!(b["diagnostics"][0]["range"]["start"]["line"], 1, "{b}"); +} diff --git a/crates/iris-agentic-dev-core/tests/binary/main.rs b/crates/iris-agentic-dev-core/tests/binary/main.rs index b2c3c25c..bafbc5d0 100644 --- a/crates/iris-agentic-dev-core/tests/binary/main.rs +++ b/crates/iris-agentic-dev-core/tests/binary/main.rs @@ -14,6 +14,7 @@ fn isolate_the_test_process() { iris_agentic_dev_core::testing::isolate_process(); } +mod check_ascot; mod cli_discovery; mod coding_pack_129; mod gate_normalize_163; diff --git a/crates/iris-agentic-dev-core/tests/integration/main.rs b/crates/iris-agentic-dev-core/tests/integration/main.rs index c96c5c8f..19e7fd37 100644 --- a/crates/iris-agentic-dev-core/tests/integration/main.rs +++ b/crates/iris-agentic-dev-core/tests/integration/main.rs @@ -35,6 +35,7 @@ mod test_aihub_139_live; mod test_attribution_live; mod test_benchmark_live; mod test_body_read_ack_171_live; +mod test_check_ascot_live; mod test_cmd_live; mod test_codemode_gate_live; mod test_comparison_e2e; diff --git a/crates/iris-agentic-dev-core/tests/integration/test_check_ascot_live.rs b/crates/iris-agentic-dev-core/tests/integration/test_check_ascot_live.rs new file mode 100644 index 00000000..9f481c75 --- /dev/null +++ b/crates/iris-agentic-dev-core/tests/integration/test_check_ascot_live.rs @@ -0,0 +1,56 @@ +//! `iris_check` against iris-dev-iris: a document stored on the server is fetched and checked, and +//! a name it does not define is resolved through iad's connection, so a wrong member of a system +//! class is an error rather than a hint. +//! +//! Run: `IRIS_HOST=localhost IRIS_WEB_PORT=52780 IRIS_USERNAME=_SYSTEM IRIS_PASSWORD=SYS +//! IAD_BINARY=$PWD/target/debug/iris-agentic-dev cargo test --features testing --test integration +//! test_check_ascot -- --include-ignored --test-threads=1` + +use iris_agentic_dev_core::testing::{live_env, McpSession}; + +fn body(answer: &serde_json::Value) -> serde_json::Value { + let text = answer["result"]["content"][0]["text"] + .as_str() + .unwrap_or_else(|| panic!("no text content: {answer}")); + serde_json::from_str(text).unwrap_or_else(|e| panic!("not JSON ({e}): {text}")) +} + +#[test] +#[ignore = "requires live IRIS"] +fn a_stored_document_is_fetched_and_a_wrong_system_member_is_an_error() { + let mut env = live_env(); + env.retain(|(k, _)| k != "IRIS_CONTAINER"); + let mut s = McpSession::start(&env); + let name = "Live.CheckProbe.cls"; + let source = "Class Live.CheckProbe\n{\nClassMethod Run()\n{\n Write ##class(%SYSTEM.Version).GetVersion()\n Write ##class(%SYSTEM.Version).NoSuchMethod()\n}\n}\n"; + let put = body(&s.call( + "iris_doc", + &serde_json::json!({"mode": "put", "name": name, "content": source, "namespace": "USER"}), + )); + assert_eq!(put["success"], true, "{put}"); + let b = body(&s.call( + "iris_check", + &serde_json::json!({"name": name, "namespace": "USER"}), + )); + s.call( + "iris_doc", + &serde_json::json!({"mode": "delete", "name": name, "namespace": "USER"}), + ); + assert_eq!(b["success"], true, "{b}"); + assert_eq!(b["iris"], true, "{b}"); + assert!(b["note"].is_null(), "{b}"); + assert_eq!(b["errors"], 1, "{b}"); + assert_eq!(b["hints"], 0, "{b}"); + assert!( + b["diagnostics"][0]["message"] + .as_str() + .unwrap_or("") + .contains("NoSuchMethod"), + "{b}" + ); + let missing = body(&s.call( + "iris_check", + &serde_json::json!({"name": "Live.NoSuchDoc.cls", "namespace": "USER"}), + )); + assert_eq!(missing["error_code"], "DOCUMENT_NOT_FOUND", "{missing}"); +} diff --git a/crates/iris-agentic-dev-core/tests/mcp_handshake.rs b/crates/iris-agentic-dev-core/tests/mcp_handshake.rs index d97f5ed1..a6578639 100644 --- a/crates/iris-agentic-dev-core/tests/mcp_handshake.rs +++ b/crates/iris-agentic-dev-core/tests/mcp_handshake.rs @@ -444,8 +444,14 @@ fn mcp_server_negotiates_all_known_protocol_versions() { return; }; - // All known protocol versions should be echoed back - for client_version in &["2026-07-28", "2025-11-25", "2024-11-05"] { + // Every known version with an `initialize` handshake is echoed back. 2026-07-28 moved the + // lifecycle into per-request `_meta`, so a client naming it over `initialize` is answered with + // the newest version that still has one (rmcp's `LATEST_WITH_INITIALIZE`). + for (client_version, expected) in &[ + ("2026-07-28", "2025-11-25"), + ("2025-11-25", "2025-11-25"), + ("2024-11-05", "2024-11-05"), + ] { let mut child = iris_agentic_dev_core::testing::clean_mcp_command(&bin) .env("IRIS_HOST", "127.0.0.1") .env("IRIS_WEB_PORT", "9") @@ -471,8 +477,8 @@ fn mcp_server_negotiates_all_known_protocol_versions() { .unwrap_or("missing"); assert_eq!( - server_version, *client_version, - "client sent {client_version}, server replied {server_version} — should echo known versions" + server_version, *expected, + "client sent {client_version}, server replied {server_version}, expected {expected}" ); iris_agentic_dev_core::testing::stop_server(&mut child); diff --git a/crates/iris-agentic-dev-core/tests/unit/main.rs b/crates/iris-agentic-dev-core/tests/unit/main.rs index 7fa0a953..bb54e7cc 100644 --- a/crates/iris-agentic-dev-core/tests/unit/main.rs +++ b/crates/iris-agentic-dev-core/tests/unit/main.rs @@ -38,6 +38,7 @@ mod test_body_read_ack_171; mod test_bundled_skills; mod test_cargo_config; mod test_changelog; +mod test_check_ascot; mod test_check_config_source_values_141; mod test_clean_command_home_141; mod test_cli_dispatch_helpers; diff --git a/crates/iris-agentic-dev-core/tests/unit/test_changelog.rs b/crates/iris-agentic-dev-core/tests/unit/test_changelog.rs index 4e2de059..0920041f 100644 --- a/crates/iris-agentic-dev-core/tests/unit/test_changelog.rs +++ b/crates/iris-agentic-dev-core/tests/unit/test_changelog.rs @@ -44,7 +44,7 @@ fn release_notes() -> Vec<(String, String)> { Some((key, version, read(&format!("docs/release-notes/{name}")))) }) .collect(); - notes.sort_by(|a, b| b.0.cmp(&a.0)); + notes.sort_by_key(|n| std::cmp::Reverse(n.0)); notes.into_iter().map(|(_, v, text)| (v, text)).collect() } diff --git a/crates/iris-agentic-dev-core/tests/unit/test_check_ascot.rs b/crates/iris-agentic-dev-core/tests/unit/test_check_ascot.rs new file mode 100644 index 00000000..02b2b854 --- /dev/null +++ b/crates/iris-agentic-dev-core/tests/unit/test_check_ascot.rs @@ -0,0 +1,20 @@ +//! `iris_check` without IRIS: a routine checked on its own. The live half is +//! `tests/integration/test_check_ascot_live.rs`. + +use ascot::bindings::DiagnosticSeverity; +use iris_agentic_dev_core::tools::check::check; + +#[test] +fn a_routine_calling_a_label_it_lacks_is_an_error_without_iris() { + let diagnostics = check( + "/w/Test.mac", + "ROUTINE Test [Type=MAC]\n Do Missing\n Quit\n", + None, + ); + assert_eq!(diagnostics.len(), 1, "{diagnostics:?}"); + assert_eq!(diagnostics[0].severity, DiagnosticSeverity::Error); + assert!( + diagnostics[0].message.contains("Missing"), + "{diagnostics:?}" + ); +} diff --git a/crates/iris-agentic-dev-core/tests/unit/test_toolset.rs b/crates/iris-agentic-dev-core/tests/unit/test_toolset.rs index 759bfafd..3c891949 100644 --- a/crates/iris-agentic-dev-core/tests/unit/test_toolset.rs +++ b/crates/iris-agentic-dev-core/tests/unit/test_toolset.rs @@ -133,8 +133,8 @@ fn test_baseline_tool_count() { let tools = IrisTools::new_with_toolset(None, Toolset::Baseline).expect("IrisTools::new"); let count = tools.registered_tool_names().len(); assert_eq!( - count, 84, - "Baseline toolset must have exactly 84 tools (93 total #[tool] methods - 9 \ + count, 85, + "Baseline toolset must have exactly 85 tools (94 total #[tool] methods - 9 \ Merged-tier-only dispatchers), got {}. If this changed on purpose, update this \ number — do not just silence the assertion.", count @@ -266,8 +266,8 @@ fn test_merged_tool_count() { let tools = IrisTools::new_with_toolset(None, Toolset::Merged).expect("IrisTools::new"); let count = tools.registered_tool_names().len(); assert_eq!( - count, 81, - "Merged toolset must have exactly 81 tools (93 total #[tool] methods - stubs 4 - \ + count, 82, + "Merged toolset must have exactly 82 tools (94 total #[tool] methods - stubs 4 - \ replaced-by-dispatcher 8), got {}. If this changed on purpose (a tool was added, \ removed, or moved tiers), update this number — do not just silence the assertion.", count diff --git a/docs/tools.md b/docs/tools.md index 7d67f240..0b2c300c 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -24,6 +24,7 @@ unless there is something non-obvious to say about it. | [`iris_import_servers`](#iris_import_servers) | Server Management | | [`iris_doc`](#iris_doc) | Code | | [`iris_compile`](#iris_compile) | Code | +| [`iris_check`](#iris_check) | Code | | [`iris_execute`](#iris_execute) | Code | | [`iris_execute_method`](#iris_execute_method) | Code | | [`iris_query`](#iris_query) | Code | @@ -141,7 +142,7 @@ forced call is judged by what it actually runs. ## Tool catalog size -This server exposes 80 to 84 tools depending on toolset (`IRIS_TOOLSET=baseline|nostub|merged`), +This server exposes 81 to 85 tools depending on toolset (`IRIS_TOOLSET=baseline|nostub|merged`), with full schemas and descriptions: 94 KB to 107 KB of JSON, roughly 26K to 30K tokens, if a client loads the whole catalog on every connection. Two independent ways to avoid paying that cost, and you don't have to pick just one: @@ -469,6 +470,35 @@ iris_compile(target="MyApp.*.cls", flags="cukd") --- +### `iris_check` + +Check an ObjectScript document before compilation, powered by +[intersystems-community/ascot](https://github.com/intersystems-community/ascot), a static analyzer +and flow-sensitive type checker. + +| Parameter | Type | Default | Notes | +| ----------- | ------ | ------- | --------------------------------------------------------------------------------------------- | +| `name` | string | — | **Required.** The document with its extension: `Pkg.Class.cls`, `Routine.mac`, `.int`, `.inc` | +| `source` | string | — | The document's text, to check code before saving it; omit to check it as stored on the server | +| `namespace` | string | — | Namespace that resolves the document's references; defaults to the connection's | +| `server` | string | — | Named server; omit for default | + +Classes, routines and `.inc` files the document refers to but does not define are read from the +target server with read-only Atelier requests; nothing runs on IRIS. Without a connection they are +reported as hints and the response carries a `note` saying so; without a connection and without +`source` there is nothing to check, and the call is refused with `IRIS_UNREACHABLE`. A `name` the +server does not hold answers `DOCUMENT_NOT_FOUND`. + +Returns LSP-style diagnostics (zero-based `range`, `severity`, `code`, `code_description`) with +`errors`, `warnings` and `hints` counts, and `iris` saying whether a connection answered the lookups. + +```text +iris_check(name="MyApp.Service.cls") +iris_check(name="MyApp.Service.cls", source="Class MyApp.Service ...") +``` + +--- + ### `iris_execute` Run arbitrary ObjectScript and return the output. @@ -2313,7 +2343,7 @@ can act on before calling the tool. | Annotation | Set on | What it means | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -| `read_only_hint` | 52 tools — all query, inspect, list, history, and comparison tools | The tool makes no changes to IRIS state | +| `read_only_hint` | 53 tools — all query, inspect, list, history, and comparison tools | The tool makes no changes to IRIS state | | `destructive_hint` | 7 tools — `global_kill`, `iris_admin`, `iris_credential_manage`, `iris_lookup_manage`, `iris_namespace_create`, `iris_remove_server`, `skill_forget` | The tool can irreversibly delete or overwrite data | MCP clients that respect `read_only_hint` can run read-only tools in parallel or in diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 711b3a48..38ab2c6b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.92.0" +channel = "1.95.0" components = ["clippy", "rustfmt"] diff --git a/tests/e2e/skill_eval/tool_applicability.toml b/tests/e2e/skill_eval/tool_applicability.toml index 35dfb049..7f7504e0 100644 --- a/tests/e2e/skill_eval/tool_applicability.toml +++ b/tests/e2e/skill_eval/tool_applicability.toml @@ -83,6 +83,7 @@ iris_system_performance = "instance-admin" iris_table_info = "sql" iris_test = "testing" iris_test_server = "configuration" +iris_check = "objectscript-code" iris_ws_close = "interactive-session" iris_ws_exec = "interactive-session" iris_ws_open = "interactive-session" diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-43.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-43.yaml new file mode 100644 index 00000000..96507b95 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-43.yaml @@ -0,0 +1,42 @@ +id: CORPUS-43 +prompt: | + Bench.Label.Describe(pScore) in the BENCHMARK namespace should return "pass" for a score of 50 or + more and "fail" for anything lower. It raises for every score below 50. Fix it and + leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Label + content: | + Class Bench.Label Extends %RegisteredObject + { + + ClassMethod Describe(pScore As %Integer) As %String + { + If pScore >= 50 { + Set tLabel = "pass" + } + Quit tLabel + } + + } +check: | + try { set ok=((##class(Bench.Label).Describe(70)="pass")&&(##class(Bench.Label).Describe(20)="fail")) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Label + content: | + Class Bench.Label Extends %RegisteredObject + { + + ClassMethod Describe(pScore As %Integer) As %String + { + Set tLabel = "fail" + If pScore >= 50 { + Set tLabel = "pass" + } + Quit tLabel + } + + } +tags: [undefined, branches] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-44.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-44.yaml new file mode 100644 index 00000000..0f839fc9 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-44.yaml @@ -0,0 +1,42 @@ +id: CORPUS-44 +prompt: | + Bench.Sum.Total(pList) in the BENCHMARK namespace should return the sum of the numbers in the + %List pList, and 0 for an empty list. It raises for an empty list. Fix it and leave + the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Sum + content: | + Class Bench.Sum Extends %RegisteredObject + { + + ClassMethod Total(pList As %List) As %Integer + { + For i = 1:1:$ListLength(pList) { + Set tTotal = $Get(tTotal, 0) + $List(pList, i) + } + Quit tTotal + } + + } +check: | + try { set ok=((##class(Bench.Sum).Total($ListBuild(1,2,3))=6)&&(##class(Bench.Sum).Total("")=0)) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Sum + content: | + Class Bench.Sum Extends %RegisteredObject + { + + ClassMethod Total(pList As %List) As %Integer + { + Set tTotal = 0 + For i = 1:1:$ListLength(pList) { + Set tTotal = tTotal + $List(pList, i) + } + Quit tTotal + } + + } +tags: [undefined, loops] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-45.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-45.yaml new file mode 100644 index 00000000..0c208907 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-45.yaml @@ -0,0 +1,56 @@ +id: CORPUS-45 +prompt: | + Bench.Tree.ValueOf(pKey) in the BENCHMARK namespace should return the value stored at + ^Bench.Tree(pKey), or an empty string when that node holds no value of its own, which includes a + node that only has child nodes. Setup() stores a value at "a" and a child under "b". ValueOf("b") + raises . Fix it and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Tree + content: | + Class Bench.Tree Extends %RegisteredObject + { + + ClassMethod Setup() + { + Kill ^Bench.Tree + Set ^Bench.Tree("a") = 1 + Set ^Bench.Tree("b", "x") = 2 + } + + ClassMethod ValueOf(pKey As %String) As %String + { + If $Data(^Bench.Tree(pKey), tValue) { + Quit tValue + } + Quit "" + } + + } +check: | + try { do ##class(Bench.Tree).Setup() set ok=((##class(Bench.Tree).ValueOf("a")=1)&&(##class(Bench.Tree).ValueOf("b")="")&&(##class(Bench.Tree).ValueOf("zz")="")) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Tree + content: | + Class Bench.Tree Extends %RegisteredObject + { + + ClassMethod Setup() + { + Kill ^Bench.Tree + Set ^Bench.Tree("a") = 1 + Set ^Bench.Tree("b", "x") = 2 + } + + ClassMethod ValueOf(pKey As %String) As %String + { + If $Data(^Bench.Tree(pKey), tValue) # 2 { + Quit tValue + } + Quit "" + } + + } +tags: [globals, undefined] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-46.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-46.yaml new file mode 100644 index 00000000..422178db --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-46.yaml @@ -0,0 +1,47 @@ +id: CORPUS-46 +prompt: | + Bench.Find.FirstOver(pList, pMin) in the BENCHMARK namespace should return the first number in + the %List pList that is greater than pMin, and an empty string when there is none. It raises + as soon as it finds one. Fix it and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Find + content: | + Class Bench.Find Extends %RegisteredObject + { + + ClassMethod FirstOver(pList As %List, pMin As %Integer) As %Integer + { + For i = 1:1:$ListLength(pList) { + Set tItem = $List(pList, i) + If tItem > pMin { + Quit tItem + } + } + Quit "" + } + + } +check: | + try { set ok=((##class(Bench.Find).FirstOver($ListBuild(1,5,9),3)=5)&&(##class(Bench.Find).FirstOver($ListBuild(1,2),3)="")) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Find + content: | + Class Bench.Find Extends %RegisteredObject + { + + ClassMethod FirstOver(pList As %List, pMin As %Integer) As %Integer + { + For i = 1:1:$ListLength(pList) { + Set tItem = $List(pList, i) + If tItem > pMin { + Return tItem + } + } + Quit "" + } + + } +tags: [loops, control-flow] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-47.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-47.yaml new file mode 100644 index 00000000..bb08bd8e --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-47.yaml @@ -0,0 +1,41 @@ +id: CORPUS-47 +prompt: | + Bench.Check.Validate(pAge) in the BENCHMARK namespace should return an error %Status for a + negative age and $$$OK otherwise. Set sc = ##class(Bench.Check).Validate(5) raises . Fix + it and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Check + content: | + Class Bench.Check Extends %RegisteredObject + { + + ClassMethod Validate(pAge As %Integer) As %Status + { + If pAge < 0 { + Quit $$$ERROR($$$GeneralError, "age must not be negative") + } + Quit + } + + } +check: | + try { set ok=($system.Status.IsOK(##class(Bench.Check).Validate(5))&&$system.Status.IsError(##class(Bench.Check).Validate(-1))) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Check + content: | + Class Bench.Check Extends %RegisteredObject + { + + ClassMethod Validate(pAge As %Integer) As %Status + { + If pAge < 0 { + Quit $$$ERROR($$$GeneralError, "age must not be negative") + } + Quit $$$OK + } + + } +tags: [status-handling, return-value] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-48.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-48.yaml new file mode 100644 index 00000000..9538ba91 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-48.yaml @@ -0,0 +1,45 @@ +id: CORPUS-48 +prompt: | + Bench.Price.Net(pGross) in the BENCHMARK namespace should return pGross with a 20% discount + applied, so Net(100) is 80. It raises . Fix it and leave the class compiled on the + server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Price + content: | + Class Bench.Price Extends %RegisteredObject + { + + ClassMethod Net(pGross As %Numeric) As %Numeric + { + Quit ..Apply(pGross, 0.2, 1) + } + + ClassMethod Apply(pAmount As %Numeric, pRate As %Numeric) As %Numeric + { + Quit pAmount * (1 - pRate) + } + + } +check: | + try { set ok=(##class(Bench.Price).Net(100)=80) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Price + content: | + Class Bench.Price Extends %RegisteredObject + { + + ClassMethod Net(pGross As %Numeric) As %Numeric + { + Quit ..Apply(pGross, 0.2) + } + + ClassMethod Apply(pAmount As %Numeric, pRate As %Numeric) As %Numeric + { + Quit pAmount * (1 - pRate) + } + + } +tags: [method-calls, arguments] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-49.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-49.yaml new file mode 100644 index 00000000..850038a6 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-49.yaml @@ -0,0 +1,64 @@ +id: CORPUS-49 +prompt: | + Bench.Temp in the BENCHMARK namespace converts Celsius to Fahrenheit. ToFahrenheit(pCelsius) + converts a value and needs nothing from an instance; Fahrenheit() converts an instance's Celsius + property by calling it. Boiling() should return the Fahrenheit value of 100 degrees Celsius, which + is 212. It raises . Fix it and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Temp + content: | + Class Bench.Temp Extends %RegisteredObject + { + + Property Celsius As %Numeric; + + /// The Fahrenheit reading of this instance's Celsius. + Method Fahrenheit() As %Numeric + { + Quit ..ToFahrenheit(..Celsius) + } + + /// The Fahrenheit value of a Celsius reading. + Method ToFahrenheit(pCelsius As %Numeric) As %Numeric + { + Quit (pCelsius * 9 / 5) + 32 + } + + ClassMethod Boiling() As %Numeric + { + Quit ##class(Bench.Temp).ToFahrenheit(100) + } + + } +check: | + try { set t=##class(Bench.Temp).%New() set t.Celsius=0 set ok=((##class(Bench.Temp).Boiling()=212)&&(t.Fahrenheit()=32)) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Temp + content: | + Class Bench.Temp Extends %RegisteredObject + { + + Property Celsius As %Numeric; + + /// The Fahrenheit reading of this instance's Celsius. + Method Fahrenheit() As %Numeric + { + Quit ..ToFahrenheit(..Celsius) + } + + /// The Fahrenheit value of a Celsius reading. + ClassMethod ToFahrenheit(pCelsius As %Numeric) As %Numeric + { + Quit (pCelsius * 9 / 5) + 32 + } + + ClassMethod Boiling() As %Numeric + { + Quit ##class(Bench.Temp).ToFahrenheit(100) + } + + } +tags: [method-calls, instance-vs-class] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-50.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-50.yaml new file mode 100644 index 00000000..db17635a --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-50.yaml @@ -0,0 +1,64 @@ +id: CORPUS-50 +prompt: | + Bench.Card.Example(pTitle, pOwner) in the BENCHMARK namespace builds a card and returns its + Summary(), the title followed by the owner in parentheses, so Example("Dune", "Ann") is + "Dune (Ann)". It raises . Fix it and leave the class compiled on the + server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Card + content: | + Class Bench.Card Extends %RegisteredObject + { + + Property Title As %String; + + Property Owner As %String; + + /// The title followed by the owner in parentheses. + Method Summary() As %String + { + Quit ..Title_" ("_..Owner_")" + } + + /// The summary of a card titled pTitle and owned by pOwner. + ClassMethod Example(pTitle As %String, pOwner As %String) As %String + { + Set tCard = ..%New() + Set tCard.Turtle = pTitle + Set tCard.Owner = pOwner + Quit tCard.Summary() + } + + } +check: | + try { set ok=(##class(Bench.Card).Example("Dune","Ann")="Dune (Ann)") } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Card + content: | + Class Bench.Card Extends %RegisteredObject + { + + Property Title As %String; + + Property Owner As %String; + + /// The title followed by the owner in parentheses. + Method Summary() As %String + { + Quit ..Title_" ("_..Owner_")" + } + + /// The summary of a card titled pTitle and owned by pOwner. + ClassMethod Example(pTitle As %String, pOwner As %String) As %String + { + Set tCard = ..%New() + Set tCard.Title = pTitle + Set tCard.Owner = pOwner + Quit tCard.Summary() + } + + } +tags: [properties, wrong-member] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-51.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-51.yaml new file mode 100644 index 00000000..9c86e23b --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-51.yaml @@ -0,0 +1,35 @@ +id: CORPUS-51 +prompt: | + Bench.Info.Major() in the BENCHMARK namespace should return the major version number of the + running IRIS instance, as the %SYSTEM.Version class reports it. It raises . + Fix it and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Info + content: | + Class Bench.Info Extends %RegisteredObject + { + + ClassMethod Major() As %Integer + { + Quit $System.Version.GetMajorVersion() + } + + } +check: | + try { set ok=(##class(Bench.Info).Major()=$System.Version.GetMajor()) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Info + content: | + Class Bench.Info Extends %RegisteredObject + { + + ClassMethod Major() As %Integer + { + Quit $System.Version.GetMajor() + } + + } +tags: [system-classes, wrong-member] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-52.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-52.yaml new file mode 100644 index 00000000..41323e40 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-52.yaml @@ -0,0 +1,45 @@ +id: CORPUS-52 +prompt: | + Bench.Payload.Build(pName, pQty) in the BENCHMARK namespace should return a %DynamicObject with + a name member and a qty member, so that Qty(pName, pQty) can read qty back. Qty("x", 3) raises + . Fix it and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Payload + content: | + Class Bench.Payload Extends %RegisteredObject + { + + ClassMethod Build(pName As %String, pQty As %Integer) As %DynamicObject + { + Quit "{""name"":"""_pName_""",""qty"":"_pQty_"}" + } + + ClassMethod Qty(pName As %String, pQty As %Integer) As %Integer + { + Quit ..Build(pName, pQty).%Get("qty") + } + + } +check: | + try { set ok=((##class(Bench.Payload).Qty("x",3)=3)&&(##class(Bench.Payload).Build("x",3).%Get("name")="x")) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Payload + content: | + Class Bench.Payload Extends %RegisteredObject + { + + ClassMethod Build(pName As %String, pQty As %Integer) As %DynamicObject + { + Quit {"name": (pName), "qty": (pQty)} + } + + ClassMethod Qty(pName As %String, pQty As %Integer) As %Integer + { + Quit ..Build(pName, pQty).%Get("qty") + } + + } +tags: [json, dynamic-objects, types] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-53.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-53.yaml new file mode 100644 index 00000000..b2cad916 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-53.yaml @@ -0,0 +1,49 @@ +id: CORPUS-53 +prompt: | + Bench.Basket in the BENCHMARK namespace keeps its items in a list collection. Add(pItem) appends + one, and First() should return the first item added, or an empty string when nothing has been + added. First() raises . Fix it and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Basket + content: | + Class Bench.Basket Extends %RegisteredObject + { + + Property Items As list Of %String; + + Method Add(pItem As %String) As %Status + { + Quit ..Items.Insert(pItem) + } + + Method First() As %String + { + Quit ..Items(1) + } + + } +check: | + try { set b=##class(Bench.Basket).%New() do b.Add("pear") do b.Add("fig") set ok=((b.First()="pear")&&(##class(Bench.Basket).%New().First()="")) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Basket + content: | + Class Bench.Basket Extends %RegisteredObject + { + + Property Items As list Of %String; + + Method Add(pItem As %String) As %Status + { + Quit ..Items.Insert(pItem) + } + + Method First() As %String + { + Quit ..Items.GetAt(1) + } + + } +tags: [collections, properties] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-54.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-54.yaml new file mode 100644 index 00000000..06d97a53 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-54.yaml @@ -0,0 +1,35 @@ +id: CORPUS-54 +prompt: | + Bench.Word.Of(pCode) in the BENCHMARK namespace should return "one", "two" or "three" for the + codes 1 to 3, and "other" for any other code. Of(7) raises . Fix it and leave the + class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Word + content: | + Class Bench.Word Extends %RegisteredObject + { + + ClassMethod Of(pCode As %Integer) As %String + { + Quit $Case(pCode, 1:"one", 2:"two", 3:"three") + } + + } +check: | + try { set ok=((##class(Bench.Word).Of(2)="two")&&(##class(Bench.Word).Of(7)="other")) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Word + content: | + Class Bench.Word Extends %RegisteredObject + { + + ClassMethod Of(pCode As %Integer) As %String + { + Quit $Case(pCode, 1:"one", 2:"two", 3:"three", :"other") + } + + } +tags: [functions, case] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-55.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-55.yaml new file mode 100644 index 00000000..26eb19ca --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-55.yaml @@ -0,0 +1,56 @@ +id: CORPUS-55 +prompt: | + Bench.Pieces.Count(pText) in the BENCHMARK namespace should return how many comma-separated + pieces pText has, so Count("a,b,c") is 3. It calls Split(pText, Output pCount), which is meant to + hand the count back through pCount. Count("a,b,c") raises . Fix it and leave the class + compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Pieces + content: | + Class Bench.Pieces Extends %RegisteredObject + { + + ClassMethod Split(pText As %String, Output pCount As %Integer) As %Status + { + Set tCount = $Length(pText, ",") + Quit $$$OK + } + + ClassMethod Count(pText As %String) As %Integer + { + Set tSC = ..Split(pText, .tCount) + If $$$ISERR(tSC) { + Quit 0 + } + Quit tCount + } + + } +check: | + try { set ok=((##class(Bench.Pieces).Count("a,b,c")=3)&&(##class(Bench.Pieces).Count("x")=1)) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Pieces + content: | + Class Bench.Pieces Extends %RegisteredObject + { + + ClassMethod Split(pText As %String, Output pCount As %Integer) As %Status + { + Set pCount = $Length(pText, ",") + Quit $$$OK + } + + ClassMethod Count(pText As %String) As %Integer + { + Set tSC = ..Split(pText, .tCount) + If $$$ISERR(tSC) { + Quit 0 + } + Quit tCount + } + + } +tags: [output-parameters, undefined] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-56.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-56.yaml new file mode 100644 index 00000000..e5d07e95 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-56.yaml @@ -0,0 +1,74 @@ +id: CORPUS-56 +prompt: | + Bench.Stock.Total() in the BENCHMARK namespace should return the sum of the quantities stored + under ^Bench.Stock, and 0 when the global is empty. After Setup() stores two quantities, Total() + returns 0, and after Clear() empties the global it raises . Fix it and leave the class + compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Stock + content: | + Class Bench.Stock Extends %RegisteredObject + { + + ClassMethod Setup() + { + Kill ^Bench.Stock + Set ^Bench.Stock("apple") = 3 + Set ^Bench.Stock("pear") = 5 + } + + ClassMethod Clear() + { + Kill ^Bench.Stock + } + + ClassMethod Total() As %Integer + { + Set tTotal = 0 + Set tKey = "" + For { + Set tKey = $Order(^Bench.Stock(tKey), 1, tQty) + Quit:tKey'="" + Set tTotal = tTotal + tQty + } + Quit tTotal + } + + } +check: | + try { do ##class(Bench.Stock).Setup() set ok=(##class(Bench.Stock).Total()=8) do ##class(Bench.Stock).Clear() set ok=ok&&(##class(Bench.Stock).Total()=0) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Stock + content: | + Class Bench.Stock Extends %RegisteredObject + { + + ClassMethod Setup() + { + Kill ^Bench.Stock + Set ^Bench.Stock("apple") = 3 + Set ^Bench.Stock("pear") = 5 + } + + ClassMethod Clear() + { + Kill ^Bench.Stock + } + + ClassMethod Total() As %Integer + { + Set tTotal = 0 + Set tKey = "" + For { + Set tKey = $Order(^Bench.Stock(tKey), 1, tQty) + Quit:tKey="" + Set tTotal = tTotal + tQty + } + Quit tTotal + } + + } +tags: [globals, order-traversal, undefined] diff --git a/tests/e2e/tasks/benchmark/corpus/CORPUS-57.yaml b/tests/e2e/tasks/benchmark/corpus/CORPUS-57.yaml new file mode 100644 index 00000000..49d44b76 --- /dev/null +++ b/tests/e2e/tasks/benchmark/corpus/CORPUS-57.yaml @@ -0,0 +1,59 @@ +id: CORPUS-57 +prompt: | + Bench.Guard in the BENCHMARK namespace does not compile. Save(pName) should return the error + %Status from Validate(pName) when there is one, so Save("") is an error, and $$$OK otherwise, so + Save("x") succeeds. Find the error, fix it, and leave the class compiled on the server. + + IRIS: host localhost, web port 52780, username _SYSTEM, password SYS, namespace BENCHMARK. + The instance runs in the Docker container iris-dev-iris. +fixtures: + - name: Bench.Guard + content: | + Class Bench.Guard Extends %RegisteredObject + { + + ClassMethod Save(pName As %String) As %Status + { + Set tSC = ..Validate(pName) + If $$$ISERROR(tSC) { + Quit tSC + } + Quit $$$OK + } + + ClassMethod Validate(pName As %String) As %Status + { + If pName = "" { + Quit $$$ERROR($$$GeneralError, "name required") + } + Quit $$$OK + } + + } +check: | + try { set ok=($system.Status.IsError(##class(Bench.Guard).Save(""))&&$system.Status.IsOK(##class(Bench.Guard).Save("x"))) } catch { set ok=0 } write $select(ok:"PASS",1:"FAIL") +solution: + - name: Bench.Guard + content: | + Class Bench.Guard Extends %RegisteredObject + { + + ClassMethod Save(pName As %String) As %Status + { + Set tSC = ..Validate(pName) + If $$$ISERR(tSC) { + Quit tSC + } + Quit $$$OK + } + + ClassMethod Validate(pName As %String) As %Status + { + If pName = "" { + Quit $$$ERROR($$$GeneralError, "name required") + } + Quit $$$OK + } + + } +tags: [macros, compile-error, status-handling] diff --git a/tests/e2e/tasks/benchmark/split.toml b/tests/e2e/tasks/benchmark/split.toml index f0a2eee9..2c0122ff 100644 --- a/tests/e2e/tasks/benchmark/split.toml +++ b/tests/e2e/tasks/benchmark/split.toml @@ -86,6 +86,12 @@ # class, a ToolSet query's parameters and `MaxRows`, and `AddDocument` with and without a `source`. None # makes an LLM call. # +# - **CORPUS-43 to CORPUS-57 are holdout from the day they were written (2026-10-29).** Each plants +# a defect the IRIS compiler accepts and the program trips on at runtime, drawn from the +# runtime-proven tests of `objectscript-typecheck-corpus`; CORPUS-57, a misspelled macro the +# compiler also catches, was asked for in the PR review. Written to measure `iris_check` (PR #166); +# none involves a routine. +# # One caveat that the split cannot fix and the report has to carry instead: I wrote the 42 corpus # tasks knowing what iad's 81 tools can do. Authoring is not tuning — no description, prompt or skill # was changed while looking at a result from these tasks — but a corpus written by the tool author is @@ -156,6 +162,21 @@ holdout = [ "CORPUS-39", "CORPUS-40", "CORPUS-41", + "CORPUS-43", + "CORPUS-44", + "CORPUS-45", + "CORPUS-46", + "CORPUS-47", + "CORPUS-48", + "CORPUS-49", + "CORPUS-50", + "CORPUS-51", + "CORPUS-52", + "CORPUS-53", + "CORPUS-54", + "CORPUS-55", + "CORPUS-56", + "CORPUS-57", "SKILL-01", "SKILL-04", "SKILL-06",