diff --git a/Cargo.lock b/Cargo.lock index 34736b36..fca7b867 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,9 +309,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byteorder" @@ -327,9 +327,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "camino" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ "serde_core", ] @@ -359,9 +359,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.47" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "shlex", @@ -427,9 +427,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.61" +version = "4.5.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992" +checksum = "004eef6b14ce34759aa7de4aea3217e368f463f46a3ed3764ca4b5a4404003b4" dependencies = [ "clap", ] @@ -458,6 +458,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cordyceps" version = "0.3.4" @@ -694,21 +703,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ + "convert_case", "proc-macro2", "quote", + "rustc_version", "syn", "unicode-xid", ] @@ -808,9 +819,9 @@ dependencies = [ [[package]] name = "elizacp" -version = "4.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da396029a7abffe0583ded2907946774b4a77d27069ee123b6128afa014f1b20" +checksum = "609d114650512f091ac0ad726e61e3447369579bea9f1065e03f47b674a6cb17" dependencies = [ "agent-client-protocol-schema", "anyhow", @@ -1120,16 +1131,17 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" dependencies = [ "cc", "cfg-if", "libc", "log", "rustversion", - "windows", + "windows-link 0.2.1", + "windows-result 0.4.1", ] [[package]] @@ -1238,12 +1250,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1329,9 +1340,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64", "bytes", @@ -1363,7 +1374,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -1423,9 +1434,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -1437,9 +1448,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -1611,9 +1622,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -1657,19 +1668,19 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall", + "redox_syscall 0.6.0", ] [[package]] @@ -1701,9 +1712,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -1872,9 +1883,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", @@ -2048,7 +2059,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link 0.2.1", ] @@ -2417,6 +2428,15 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "redox_syscall" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -2479,9 +2499,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ "base64", "bytes", @@ -2579,6 +2599,15 @@ 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" @@ -2621,9 +2650,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "web-time", "zeroize", @@ -2654,9 +2683,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "sacp" -version = "3.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fa4c238414041dfa3b15c7267f87d4d98123ecca5f4c443e6e34cf1669613d" +checksum = "7679525f5c2f4e1cb4b07e3684c4b050b34bafa0f908852879b7411522ff6626" dependencies = [ "agent-client-protocol-schema", "anyhow", @@ -2679,9 +2708,9 @@ dependencies = [ [[package]] name = "sacp-conductor" -version = "5.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "499388500028c1447eb2bf786b25f4d54fb3875992530d83780a6b7bdfd2cf2e" +checksum = "234e8dcd4788b957ba275a29229c2afe33f6634f56d212ab17e5b8c81ce88075" dependencies = [ "agent-client-protocol-schema", "anyhow", @@ -2712,9 +2741,9 @@ dependencies = [ [[package]] name = "sacp-derive" -version = "2.0.1" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7555e77b718ce861e1bb141a71c7dabd87f6c0f81e264eeb7526a5241c74c40f" +checksum = "70a36c27381224c2fc970935c887b26f48ff78105003d9abfa04c2e808569d3b" dependencies = [ "proc-macro2", "quote", @@ -2723,9 +2752,9 @@ dependencies = [ [[package]] name = "sacp-rmcp" -version = "0.9.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6968b77f8f7e1543b474b8ead16763b4e73ad8b9fe33b57e6b4368af7de5969d" +checksum = "edd2e0f13ca9ffe02f0d40aa303e1e0f46706b46e077fea2fa56fbe58e562276" dependencies = [ "futures", "rmcp", @@ -2736,9 +2765,9 @@ dependencies = [ [[package]] name = "sacp-tee" -version = "0.2.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42245961c9346bcf2d725d3a86d9be4fd04d10e4e5c5c0114f3b140265d5d9fa" +checksum = "5ad4745f9f9f79312fb4cfa4c776cf9d11737e627fd60351748af6dcb5d3c59d" dependencies = [ "anyhow", "chrono", @@ -2754,9 +2783,9 @@ dependencies = [ [[package]] name = "sacp-tokio" -version = "3.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610f7bd81f88c298c9e594b4cbe2266b740769a94ace345c8c85abd55ad37570" +checksum = "0b7ad0b5f6b3585cabfc05ea726e4f9fa63ec3ba4cbedb20dcd3e3a45dbd1ad4" dependencies = [ "futures", "sacp", @@ -2769,9 +2798,9 @@ dependencies = [ [[package]] name = "sacp-trace-viewer" -version = "0.1.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604ccaf6fa6fca268818463fd54b17d9f7ff125a29fa5aa00b58e2473256043a" +checksum = "42dfb08c7454e7396b37ef690873c6f3ff83d0b1702905c9b87f0bb5cc04293c" dependencies = [ "anyhow", "axum 0.7.9", @@ -2971,9 +3000,9 @@ dependencies = [ [[package]] name = "shell-words" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" [[package]] name = "shlex" @@ -2992,9 +3021,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "siphasher" @@ -3026,9 +3055,9 @@ dependencies = [ [[package]] name = "sparkle-mcp" -version = "0.1.7" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db4c11f8715fe6208046bbebcb45537dceef37e3b41d47cd80d7dbcefeda79d" +checksum = "41ed32cc329685750984d97bfa5b5bd2381a774417ac4b9382b1c83cddcfe08a" dependencies = [ "anyhow", "chrono", @@ -3570,9 +3599,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "bitflags 2.10.0", "bytes", @@ -3610,9 +3639,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -3622,21 +3651,21 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" dependencies = [ "crossbeam-channel", - "thiserror 1.0.69", + "thiserror 2.0.17", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -3645,9 +3674,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -3676,9 +3705,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -3742,6 +3771,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -3786,9 +3821,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", @@ -3852,9 +3887,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -3865,9 +3900,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -3878,9 +3913,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3888,9 +3923,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -3901,9 +3936,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -3923,9 +3958,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -4002,7 +4037,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.61.2", "windows-future", "windows-link 0.1.3", "windows-numerics", @@ -4014,7 +4049,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -4026,8 +4061,21 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link 0.1.3", - "windows-result", - "windows-strings", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -4036,7 +4084,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link 0.1.3", "windows-threading", ] @@ -4081,7 +4129,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link 0.1.3", ] @@ -4094,6 +4142,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -4103,6 +4160,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4405,9 +4471,9 @@ dependencies = [ [[package]] name = "yopo" -version = "2.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef8777c8b66a1f72e14d52a3b0be8017af2971a9ea426f4c53954bbe5179a65" +checksum = "c4e668e445064d85c69eb63e652f14c715528a760793fc898127c92814db7faf" dependencies = [ "clap", "sacp", @@ -4419,18 +4485,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index e0228ea5..3f49c879 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,15 +13,14 @@ members = [ resolver = "2" [workspace.dependencies] -sacp = "3.0.0" -sacp-tokio = "3.0.0" -sacp-conductor = "5.0.0" -sacp-rmcp = "0.9.0" -sacp-tee = "0.2.0" -sacp-test = "1.0.0" -sacp-acp-client = "0.1.0" -yopo = "2.0.0" -elizacp = "4.0.0" +sacp = "9.0.0" +sacp-tokio = "9.0.0" +sacp-conductor = "9.0.0" +sacp-rmcp = "9.0.0" +sacp-tee = "9.0.0" +sacp-test = "9.0.0" +yopo = "9.0.0" +elizacp = "9.0.0" jsonrpcmsg = "0.1.2" # Core async runtime @@ -49,7 +48,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } rmcp = { version = "0.9", features = ["server", "transport-io", "schemars"] } # Sparkle integration -sparkle = { package = "sparkle-mcp", version = "0.1.7" } +sparkle = { package = "sparkle-mcp", version = "0.2.1" } # CLI parsing clap = { version = "4.0", features = ["derive"] } diff --git a/benchmark-output/serde_from_value_expected.txt b/benchmark-output/serde_from_value_expected.txt new file mode 100644 index 00000000..435416d8 --- /dev/null +++ b/benchmark-output/serde_from_value_expected.txt @@ -0,0 +1 @@ +The response should describe that serde_json::from_value takes a serde_json::Value and deserializes it into a type T. It should mention that it returns a Result. \ No newline at end of file diff --git a/benchmark-output/serde_from_value_prompt.txt b/benchmark-output/serde_from_value_prompt.txt new file mode 100644 index 00000000..4b3da391 --- /dev/null +++ b/benchmark-output/serde_from_value_prompt.txt @@ -0,0 +1 @@ +Please use the `rust_crate_query` tool from the `rust-crate-sources` MCP server to research the signature of the serde_json::from_value API and describe what inputs it accepts. Do not try to read files from disk - use the MCP tool. \ No newline at end of file diff --git a/benchmark-output/serde_from_value_response.txt b/benchmark-output/serde_from_value_response.txt new file mode 100644 index 00000000..25456850 --- /dev/null +++ b/benchmark-output/serde_from_value_response.txt @@ -0,0 +1,24 @@ +## `serde_json::from_value` Signature + +```rust +pub fn from_value(value: Value) -> Result +where + T: DeserializeOwned, +``` + +### Inputs + +- **`value: Value`** - A `serde_json::Value` (the JSON value type). Takes ownership of the value. + +### Type Parameter + +- **`T: DeserializeOwned`** - The target type must implement `DeserializeOwned` (equivalent to `T: for<'de> Deserialize<'de>`), meaning it can deserialize without borrowing from input data. + +### Return Value + +- **`Result`** - Returns `Ok(T)` on success, or `Err(serde_json::Error)` on failure. + +### When It Fails + +1. Structure mismatch (e.g., `T` expects a struct but `Value` is not an object) +2. `T`'s `Deserialize` implementation rejects the data (missing required fields, number overflow, etc.) \ No newline at end of file diff --git a/benchmark-output/serde_from_value_validation.txt b/benchmark-output/serde_from_value_validation.txt new file mode 100644 index 00000000..95699544 --- /dev/null +++ b/benchmark-output/serde_from_value_validation.txt @@ -0,0 +1,6 @@ +PASS + +The actual response correctly describes that `serde_json::from_value` takes a `serde_json::Value` and deserializes it into a type `T`, and that it returns a `Result`. The response actually exceeds the expected requirements by also including: +- The full function signature with the trait bound +- Explanation of the `DeserializeOwned` constraint +- Examples of when deserialization can fail \ No newline at end of file diff --git a/setup/src/acp.rs b/setup/src/acp.rs index 88160e5a..12b8ba7c 100644 --- a/setup/src/acp.rs +++ b/setup/src/acp.rs @@ -4,8 +4,7 @@ use anyhow::{Context, Result, anyhow}; use std::path::{Path, PathBuf}; use std::process::Command; -/// Install ACP binaries: sacp-conductor, elizacp, sacp-tee from crates.io, -/// and symposium-acp-proxy from local repository +/// Install ACP binaries from local repository pub fn install_acp_binaries(repo_root: &Path, dry_run: bool) -> Result<()> { println!("📦 Installing ACP binaries..."); @@ -13,10 +12,10 @@ pub fn install_acp_binaries(repo_root: &Path, dry_run: bool) -> Result<()> { verify_symposium_repo(repo_root)?; // Install from crates.io - install_from_crates_io(&["sacp-conductor", "elizacp", "sacp-tee"], dry_run)?; + install_from_crates_io(&["elizacp"], dry_run)?; - // Install symposium-acp-proxy from local repository - install_symposium_acp_proxy(repo_root, dry_run)?; + // Install symposium-acp-agent from local repository + install_local_binaries(repo_root, dry_run)?; if !dry_run { println!("✅ ACP binaries installed successfully!"); @@ -74,39 +73,44 @@ fn install_from_crates_io(crates: &[&str], dry_run: bool) -> Result<()> { Ok(()) } -/// Install symposium-acp-proxy from local repository -fn install_symposium_acp_proxy(repo_root: &Path, dry_run: bool) -> Result<()> { - let symposium_acp_proxy_dir = repo_root.join("src/symposium-acp-proxy"); +/// Install local symposium binaries from the repository +fn install_local_binaries(repo_root: &Path, dry_run: bool) -> Result<()> { + for binary_name in ["symposium-acp-agent"] { + let binary_dir = repo_root.join("src").join(binary_name); - if !symposium_acp_proxy_dir.exists() { - return Err(anyhow!( - "❌ symposium-acp-proxy directory not found at: {}", - symposium_acp_proxy_dir.display() - )); - } + if !binary_dir.exists() { + return Err(anyhow!( + "❌ {} directory not found at: {}", + binary_name, + binary_dir.display() + )); + } - println!(" Path: {}", symposium_acp_proxy_dir.display()); + if dry_run { + println!(" Would install {} from local repository", binary_name); + } else { + println!(" Installing {} from local repository...", binary_name); - if dry_run { - println!(" Would install symposium-acp-proxy from local repository"); - } else { - println!(" Installing symposium-acp-proxy from local repository..."); + let output = Command::new("cargo") + .args(["install", "--path", ".", "--force"]) + .current_dir(&binary_dir) + .output() + .context(format!( + "Failed to execute cargo install for {}", + binary_name + ))?; - let output = Command::new("cargo") - .args(["install", "--path", ".", "--force"]) - .current_dir(&symposium_acp_proxy_dir) - .output() - .context("Failed to execute cargo install for symposium-acp-proxy")?; + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(anyhow!( + "❌ Failed to install {}:\n Error: {}", + binary_name, + stderr.trim() + )); + } - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - return Err(anyhow!( - "❌ Failed to install symposium-acp-proxy:\n Error: {}", - stderr.trim() - )); + println!(" ✅ {} installed", binary_name); } - - println!(" ✅ symposium-acp-proxy installed"); } Ok(()) } diff --git a/setup/src/main.rs b/setup/src/main.rs index e0d76f18..adbeea3b 100644 --- a/setup/src/main.rs +++ b/setup/src/main.rs @@ -102,9 +102,8 @@ fn main() -> Result<()> { } if configure_zed { - let conductor_path = acp::get_binary_path("sacp-conductor")?; - let symposium_acp_proxy_path = acp::get_binary_path("symposium-acp-proxy")?; - zed::configure_zed(&conductor_path, &symposium_acp_proxy_path, args.dry_run)?; + let symposium_acp_agent_path = acp::get_binary_path("symposium-acp-agent")?; + zed::configure_zed(&symposium_acp_agent_path, args.dry_run)?; println!(); } @@ -143,10 +142,8 @@ fn print_completion_message( if installed_acp { println!("📦 ACP binaries installed to ~/.cargo/bin/:"); - println!(" • sacp-conductor"); println!(" • elizacp"); - println!(" • sacp-tee"); - println!(" • symposium-acp-proxy"); + println!(" • symposium-acp-agent"); println!(); } diff --git a/setup/src/zed.rs b/setup/src/zed.rs index 1b704f88..35648da1 100644 --- a/setup/src/zed.rs +++ b/setup/src/zed.rs @@ -62,11 +62,7 @@ fn is_command_available(cmd: &str) -> bool { } /// Configure Zed with detected agents -pub fn configure_zed( - conductor_path: &Path, - symposium_acp_path: &Path, - dry_run: bool, -) -> Result<()> { +pub fn configure_zed(symposium_acp_agent_path: &Path, dry_run: bool) -> Result<()> { let zed_config_path = get_zed_config_path()?; if !zed_config_path.exists() { @@ -105,8 +101,7 @@ pub fn configure_zed( for agent in &agents { let config_name = agent.config_name(); - let agent_config = - create_agent_config(conductor_path, symposium_acp_path, agent.npx_package()); + let agent_config = create_agent_config(symposium_acp_agent_path, agent.npx_package()); if dry_run { println!(" Would add configuration for: {}", config_name); @@ -137,19 +132,11 @@ pub fn configure_zed( } /// Create an agent server configuration entry -fn create_agent_config( - conductor_path: &Path, - symposium_acp_path: &Path, - npx_package: &str, -) -> Value { +fn create_agent_config(symposium_acp_agent_path: &Path, npx_package: &str) -> Value { json!({ - "default_mode": "bypassPermissions", - "command": conductor_path.to_string_lossy(), - "args": [ - "agent", - symposium_acp_path.to_string_lossy(), - format!("npx -y '{}'", npx_package) - ], + "type": "custom", + "command": symposium_acp_agent_path.to_string_lossy(), + "args": ["--", "npx", "-y", npx_package], "env": {} }) } diff --git a/src/symposium-benchmark/src/main.rs b/src/symposium-benchmark/src/main.rs index 98801c83..004d0b15 100644 --- a/src/symposium-benchmark/src/main.rs +++ b/src/symposium-benchmark/src/main.rs @@ -5,13 +5,11 @@ use anyhow::Result; use clap::Parser; -use sacp::{ByteStreams, Component, DynComponent}; +use sacp::DynComponent; use sacp_conductor::Conductor; use sacp_tokio::AcpAgent; use std::path::PathBuf; use std::str::FromStr; -use tokio::io::duplex; -use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; #[derive(Parser, Debug)] #[command(name = "symposium-benchmark")] @@ -56,19 +54,8 @@ async fn main() -> Result<()> { // Initialize tracing based on --log argument if let Some(log_targets) = &args.log { - let mut filter = tracing_subscriber::EnvFilter::from_default_env(); - for target in log_targets.split(',') { - let target = target.trim(); - if !target.is_empty() { - // If target already has a level (contains '='), use as-is; otherwise default to debug - let directive = if target.contains('=') { - target.to_string() - } else { - format!("{}=debug", target) - }; - filter = filter.add_directive(directive.parse().unwrap()); - } - } + let filter = + tracing_subscriber::EnvFilter::try_new(log_targets).expect("invalid log filter syntax"); tracing_subscriber::fmt() .with_env_filter(filter) .with_writer(std::io::stderr) @@ -115,66 +102,37 @@ async fn run_benchmark(benchmark: &Benchmark, output_dir: &PathBuf) -> Result<() let research_prompt = benchmark.prompt; let expected_result = benchmark.expected; - // Create components: rust-crate-sources-proxy + Claude Code - let proxy = symposium_crate_sources_proxy::CrateSourcesProxy; - let claude_agent = AcpAgent::from_str("npx -y '@zed-industries/claude-code-acp'")?; - - // Create duplex streams for editor <-> conductor communication - let (editor_write, conductor_read) = duplex(8192); - let (conductor_write, editor_read) = duplex(8192); - - // Spawn conductor with proxy + agent chain (with tees for debugging) - let conductor_handle = tokio::spawn(async move { - Conductor::new( - "benchmark-conductor".to_string(), - vec![DynComponent::new(proxy), DynComponent::new(claude_agent)], - Default::default(), - ) - .trace_to_path("killme.jsons") - .map_err(sacp::util::internal_error)? - .run(ByteStreams::new( - conductor_write.compat_write(), - conductor_read.compat(), - )) - .await - }); - - // Send prompt using yopo - let response = yopo::prompt( - ByteStreams::new(editor_write.compat_write(), editor_read.compat()), - research_prompt, + // Build conductor with crate sources proxy and agent + let conductor = Conductor::new( + "benchmark-conductor".to_string(), + vec![ + DynComponent::new(symposium_crate_sources_proxy::CrateSourcesProxy), + DynComponent::new(AcpAgent::from_str( + "npx -y '@zed-industries/claude-code-acp'", + )?), + ], + Default::default(), ) - .await?; + .trace_to_path("killme.jsons") + .map_err(sacp::util::internal_error)?; + + // Run prompt + let response = yopo::prompt(conductor, research_prompt).await?; tracing::info!("Research response received: {} chars", response.len()); // Validate response using another Claude Code instance tracing::info!("Validating response"); - let validator_agent = AcpAgent::from_str("npx -y '@zed-industries/claude-code-acp'")?; - let (validator_write, validator_read) = duplex(8192); - let (validator_out_write, validator_out_read) = duplex(8192); - - let validator_handle = tokio::spawn(async move { - validator_agent - .serve(ByteStreams::new( - validator_out_write.compat_write(), - validator_read.compat(), - )) - .await - }); - - let validation_prompt = format!( - "Compare this response to the expected result and respond with PASS or FAIL. \ + let validation_result = yopo::prompt( + AcpAgent::from_str("npx -y '@zed-industries/claude-code-acp'")?, + &format!( + "Compare this response to the expected result and respond with PASS or FAIL. \ If FAIL, explain what's missing.\n\n\ Expected: {}\n\n\ Actual response:\n{}", - expected_result, response - ); - - let validation_result = yopo::prompt( - ByteStreams::new(validator_write.compat_write(), validator_out_read.compat()), - &validation_prompt, + expected_result, response + ), ) .await?; @@ -199,9 +157,5 @@ async fn run_benchmark(benchmark: &Benchmark, output_dir: &PathBuf) -> Result<() println!("VALIDATION RESULT:\n{}", validation_result); println!("========================\n"); - // Clean up - validator_handle.await??; - conductor_handle.await??; - Ok(()) } diff --git a/src/symposium-crate-sources-proxy/src/crate_sources_mcp.rs b/src/symposium-crate-sources-proxy/src/crate_sources_mcp.rs index c7fb95b7..bc8cb6c0 100644 --- a/src/symposium-crate-sources-proxy/src/crate_sources_mcp.rs +++ b/src/symposium-crate-sources-proxy/src/crate_sources_mcp.rs @@ -6,10 +6,13 @@ //! //! This service is attached to NewSessionRequest when spawning research sessions. +use std::sync::{Arc, Mutex}; + use crate::eg; +use sacp::mcp_server::McpServer; +use sacp::ProxyToConductor; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc; /// Parameters for the get_rust_crate_source tool #[derive(Debug, Deserialize, Serialize, JsonSchema)] @@ -48,13 +51,11 @@ struct ReturnResponseOutput { /// Each instance is created for a specific research session and holds a channel /// to send responses back to the waiting research agent. pub fn build_server( - response_tx: mpsc::Sender, -) -> sacp::mcp_server::McpServer { - use sacp::mcp_server::McpServer; - - McpServer::new() + responses: Arc>>, +) -> McpServer> { + McpServer::builder("rust-crate-sources".to_string()) .instructions("Provides tools for researching Rust crate sources: get_rust_crate_source to locate crates, return_response_to_user to deliver findings") - .tool_fn( + .tool_fn_mut( "get_rust_crate_source", "Locate and extract Rust crate sources from crates.io. Returns the local path where the crate sources are available for reading.", async move |input: GetRustCrateSourceParams, _context| { @@ -91,29 +92,31 @@ pub fn build_server( message, }) }, - |f, args, cx| Box::pin(f(args, cx)), + sacp::tool_fn_mut!(), ) - .tool_fn( + .tool_fn_mut( "return_response_to_user", "Record the results that will be returned to the user. If invoked multiple times, the results will be appended to the previous response.", { - let response_tx = response_tx.clone(); - async move |input: ReturnResponseParams, _context| { - let ReturnResponseParams { response } = input; - - tracing::info!("Research complete, returning response"); - tracing::debug!("Response: {}", response); - - // Send the response through the channel to the waiting research agent - response_tx.send(response.clone()).await.map_err(|_| { - anyhow::anyhow!("Failed to send response: channel closed") - })?; - - Ok(ReturnResponseOutput { - message: "Response delivered to waiting agent.".to_string(), - }) + let responses = responses.clone(); + move |input: ReturnResponseParams, _context| { + let responses = responses.clone(); + async move { + let ReturnResponseParams { response } = input; + + tracing::info!("Research complete, returning response"); + tracing::debug!("Response: {}", response); + + // Send the response through the channel to the waiting research agent + responses.lock().expect("not poisoned").push(response); + + Ok(ReturnResponseOutput { + message: "Response delivered to waiting agent.".to_string(), + }) + } } }, - |f, args, cx| Box::pin(f(args, cx)), + sacp::tool_fn_mut!(), ) + .build() } diff --git a/src/symposium-crate-sources-proxy/src/lib.rs b/src/symposium-crate-sources-proxy/src/lib.rs index d823efdb..6cbea331 100644 --- a/src/symposium-crate-sources-proxy/src/lib.rs +++ b/src/symposium-crate-sources-proxy/src/lib.rs @@ -6,14 +6,10 @@ mod crate_sources_mcp; mod eg; mod research_agent; -mod state; use anyhow::Result; use sacp::component::Component; -use sacp::mcp_server::McpServiceRegistry; use sacp::ProxyToConductor; -use state::ResearchState; -use std::sync::Arc; /// Run the proxy as a standalone binary connected to stdio pub async fn run() -> Result<()> { @@ -37,19 +33,9 @@ pub struct CrateSourcesProxy; impl Component for CrateSourcesProxy { async fn serve(self, client: impl Component) -> Result<(), sacp::Error> { - // Create shared state for tracking active research sessions - let state = Arc::new(ResearchState::new()); - - // Create MCP service registry with the user-facing service - let mcp_registry = McpServiceRegistry::new().with_mcp_server( - "rust-crate-query", - research_agent::build_server(state.clone()), - )?; - ProxyToConductor::builder() .name("rust-crate-sources-proxy") - .with_handler(mcp_registry) - .with_handler(research_agent::PermissionAutoApprover::new(state.clone())) + .with_mcp_server(research_agent::build_server()) .serve(client) .await } diff --git a/src/symposium-crate-sources-proxy/src/research_agent.rs b/src/symposium-crate-sources-proxy/src/research_agent.rs index 27313a09..fb8bfd54 100644 --- a/src/symposium-crate-sources-proxy/src/research_agent.rs +++ b/src/symposium-crate-sources-proxy/src/research_agent.rs @@ -7,106 +7,19 @@ //! 4. Auto-approves permissions and logs session notifications //! 5. Collects responses and returns findings to the user -use crate::{crate_sources_mcp, state::ResearchState}; +use crate::crate_sources_mcp; use indoc::formatdoc; use sacp::{ - mcp_server::{McpServer, McpServiceRegistry}, + mcp_server::McpServer, schema::{ - NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, - RequestPermissionOutcome, RequestPermissionRequest, RequestPermissionResponse, - SessionNotification, + RequestPermissionOutcome, RequestPermissionRequest, RequestPermissionResponse, StopReason, }, - Handled, JrConnectionCx, JrMessageHandler, MessageCx, ProxyToConductor, + util::MatchMessage, + ProxyToConductor, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::{pin::pin, sync::Arc}; -use tokio::sync::mpsc; - -/// Handler for auto-approving permission requests from research sessions. -pub struct PermissionAutoApprover { - state: Arc, -} - -impl PermissionAutoApprover { - pub fn new(state: Arc) -> Self { - Self { state } - } -} - -impl JrMessageHandler for PermissionAutoApprover { - type Role = ProxyToConductor; - - fn describe_chain(&self) -> impl std::fmt::Debug { - "permission-auto-approver" - } - - async fn handle_message( - &mut self, - message: MessageCx, - _cx: JrConnectionCx, - ) -> Result, sacp::Error> { - sacp::util::MatchMessage::new(message) - .if_request(async |request: RequestPermissionRequest, request_cx| { - // Auto-approve all permissions for research sessions - if self.state.is_research_session(&request.session_id) { - tracing::debug!( - "Auto-approving permission request for research session {}", - request.session_id - ); - - // Find the first option that looks like "allow" and use it. - for option in &request.options { - match option.kind { - sacp::schema::PermissionOptionKind::AllowOnce - | sacp::schema::PermissionOptionKind::AllowAlways => { - request_cx.respond(RequestPermissionResponse { - outcome: RequestPermissionOutcome::Selected { - option_id: option.id.clone(), - }, - meta: None, - })?; - return Ok(Handled::Yes); - } - sacp::schema::PermissionOptionKind::RejectOnce - | sacp::schema::PermissionOptionKind::RejectAlways => {} - } - } - } - - Ok(Handled::No((request, request_cx))) - }) - .await - .if_notification({ - let state = self.state.clone(); - async move |notification: SessionNotification| { - // Log all notifications for research sessions - if state.is_research_session(¬ification.session_id) { - tracing::debug!("Research session notification: {:?}", notification); - return Ok(Handled::Yes); - } - - Ok(Handled::No(notification)) - } - }) - .await - .done() - } -} - -/// Create a NewSessionRequest for the research agent. -pub fn research_agent_session_request( - sub_agent_mcp_registry: McpServiceRegistry, -) -> Result { - let cwd = std::env::current_dir().map_err(|_| sacp::Error::internal_error())?; - let mut new_session_req = NewSessionRequest { - cwd, - mcp_servers: vec![], - meta: None, - }; - sub_agent_mcp_registry.add_registered_mcp_servers_to(&mut new_session_req); - Ok(new_session_req) -} +use std::sync::{Arc, Mutex}; /// Build the research prompt with context and instructions for the sub-agent. pub fn build_research_prompt(user_prompt: &str) -> String { @@ -151,14 +64,14 @@ pub struct RustCrateQueryParams { /// Output from the rust_crate_query tool #[derive(Debug, Serialize, Deserialize, JsonSchema)] -struct RustCrateQueryOutput { +pub struct RustCrateQueryOutput { /// The research findings - result: serde_json::Value, + pub result: Vec, } /// Build the MCP server for crate research queries -pub fn build_server(state: Arc) -> McpServer { - McpServer::new() +pub fn build_server() -> McpServer> { + McpServer::builder("rust-crate-query".to_string()) .instructions(indoc::indoc! {" Research Rust crate source code and APIs. Essential for working with unfamiliar crates. @@ -168,7 +81,7 @@ pub fn build_server(state: Arc) -> McpServer { - When implementation details matter: explore how features work internally - When documentation is unclear: see concrete code examples "}) - .tool_fn( + .tool_fn_mut( "rust_crate_query", indoc::indoc! {r#" Research a Rust crate by examining its actual source code. @@ -181,104 +94,115 @@ pub fn build_server(state: Arc) -> McpServer { The research agent will examine the crate sources and return relevant code examples, signatures, and implementation details. "#}, - { - async move |input: RustCrateQueryParams, mcp_cx: sacp::mcp_server::McpContext| { - let RustCrateQueryParams { - crate_name, - crate_version, - prompt, - } = input; - - tracing::info!( - "Received crate query for '{}' version: {:?}", - crate_name, - crate_version - ); - tracing::debug!("Research prompt: {}", prompt); - - let cx = mcp_cx.connection_cx(); - - // Create a channel for receiving responses from the sub-agent's return_response_to_user calls - let (response_tx, mut response_rx) = mpsc::channel::(32); - - // Create a fresh MCP service registry for this research session - let sub_agent_mcp_registry = McpServiceRegistry::new() - .with_mcp_server( - "rust-crate-sources", - crate_sources_mcp::build_server(response_tx.clone()), - ) - .map_err(|e| anyhow::anyhow!("Failed to create MCP registry: {}", e))?; - - // Spawn the sub-agent session with the per-instance MCP registry - let NewSessionResponse { - session_id, - modes: _, - meta: _, - } = cx - .send_request_to(sacp::Agent, research_agent_session_request( - sub_agent_mcp_registry, - ).map_err(|e| anyhow::anyhow!("Failed to create session request: {}", e))?) - .block_task() - .await - .map_err(|e| anyhow::anyhow!("Failed to spawn research session: {}", e))?; - - tracing::info!("Research session created: {}", session_id); - - // Register this session_id in shared state so permission requests are auto-approved - state.register_session(&session_id); - - let mut responses = vec![]; - let (result, _) = futures::future::select( - // Collect responses from the response channel - pin!(async { - while let Some(response) = response_rx.recv().await { - responses.push(response); - } - Ok::<(), anyhow::Error>(()) - }), - pin!(async { - let research_prompt = build_research_prompt(&prompt); - let prompt_request = PromptRequest { - session_id: session_id.clone(), - prompt: vec![research_prompt.into()], - meta: None, - }; + async move |input: RustCrateQueryParams, mcp_cx: sacp::mcp_server::McpContext| { + run_research_query(input, mcp_cx).await + }, + sacp::tool_fn_mut!(), + ) + .build() +} - let PromptResponse { - stop_reason, - meta: _, - } = cx - .send_request_to(sacp::Agent, prompt_request) - .block_task() - .await - .map_err(|e| anyhow::anyhow!("Prompt request failed: {}", e))?; +async fn run_research_query( + input: RustCrateQueryParams, + mcp_cx: sacp::mcp_server::McpContext, +) -> Result { + let RustCrateQueryParams { + crate_name, + crate_version, + prompt, + } = input; + + tracing::info!( + "Received crate query for '{}' version: {:?}", + crate_name, + crate_version + ); + tracing::debug!("Research prompt: {}", prompt); + + let cx = mcp_cx.connection_cx(); + + // Create a channel for receiving responses from the sub-agent's return_response_to_user calls + let responses: Arc>> = Default::default(); + let mcp_server = crate_sources_mcp::build_server(responses.clone()); + + // Spawn the sub-agent session with the per-instance MCP server + // Use current directory since we don't have access to session cwd here + let cwd = std::env::current_dir().unwrap_or_default(); + + cx.build_session(cwd) + .with_mcp_server(mcp_server)? + .run_session(async |mut active_session| { + tracing::debug!(session_id = ?active_session.session_id(), "Session active"); + + active_session.send_prompt(build_research_prompt(&prompt))?; + tracing::debug!("Sent research prompt to session"); + + loop { + match active_session.read_update().await? { + sacp::SessionMessage::SessionMessage(message_cx) => { + MatchMessage::new(message_cx) + .if_request(async |request: RequestPermissionRequest, request_cx| { + approve_tool_request(request, request_cx) + }) + .await + .otherwise(async |message| { + // Log any other messages, we don't care about them + tracing::trace!(?message); + Ok(()) + }) + .await? + } - tracing::info!( - "Research complete for session {session_id} ({stop_reason:?})" + // Once the turn is over, we stop. + sacp::SessionMessage::StopReason(stop_reason) => match stop_reason { + StopReason::EndTurn => { + // Once the agent finishes its turn, results should have been collected into the responses vector + let result = std::mem::replace( + &mut *responses.lock().expect("not poisoned"), + vec![], ); + return Ok(RustCrateQueryOutput { result }); + } - Ok::<(), anyhow::Error>(()) - }), - ) - .await - .factor_first(); - result?; - - // Unregister the session now that research is complete - state.unregister_session(&session_id); - - // Return the accumulated responses - let response = if responses.len() == 1 { - responses.pop().expect("singleton") - } else { - serde_json::Value::Array(responses) - }; - - tracing::info!("Research complete for '{}'", crate_name); + // Other stop reasons are an error. What gives! + StopReason::MaxTokens + | StopReason::MaxTurnRequests + | StopReason::Refusal + | StopReason::Cancelled => { + return Err(sacp::util::internal_error(format!( + "researcher stopped early: {stop_reason:?}" + ))); + } + }, - Ok(RustCrateQueryOutput { result: response }) + // Anything else, just ignore. + _ => {} } - }, - |f, args, cx| Box::pin(f(args, cx)), - ) + } + }) + .await +} + +fn approve_tool_request( + request: RequestPermissionRequest, + request_cx: sacp::JrRequestCx, +) -> Result<(), sacp::Error> { + let outcome = request + .options + .iter() + .find(|option| match option.kind { + sacp::schema::PermissionOptionKind::AllowOnce + | sacp::schema::PermissionOptionKind::AllowAlways => true, + sacp::schema::PermissionOptionKind::RejectOnce + | sacp::schema::PermissionOptionKind::RejectAlways => false, + }) + .map(|option| RequestPermissionOutcome::Selected { + option_id: option.id.clone(), + }) + .unwrap_or(RequestPermissionOutcome::Cancelled); + + request_cx.respond(RequestPermissionResponse { + outcome, + meta: None, + }) } diff --git a/src/symposium-crate-sources-proxy/src/state.rs b/src/symposium-crate-sources-proxy/src/state.rs deleted file mode 100644 index 71f66d90..00000000 --- a/src/symposium-crate-sources-proxy/src/state.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Shared state for tracking active research sessions. - -use fxhash::FxHashSet; -use sacp::schema::SessionId; -use std::sync::Mutex; - -/// Shared state tracking active research sessions. -/// -/// This state is shared between: -/// - The main event loop (in Component::serve) which uses it to identify research sessions -/// when handling RequestPermissionRequest, tool calls, etc. -/// - The research_agent functions which register/unregister session_ids -/// -/// Note: The oneshot::Sender for sending responses back is NOT stored here. -/// It's owned by the research_agent::run function and used directly when -/// return_response_to_user is called. -pub struct ResearchState { - /// Set of session IDs that correspond to active research requests. - /// The main loop checks this to decide how to handle session-specific messages. - active_research_session_ids: Mutex>, -} - -impl ResearchState { - /// Create a new ResearchState with no active sessions. - pub fn new() -> Self { - Self { - active_research_session_ids: Mutex::new(FxHashSet::default()), - } - } - - /// Register a new research session ID. - /// - /// Called by research_agent::run after spawning a sub-agent session. - pub fn register_session(&self, session_id: &SessionId) { - let mut sessions = self.active_research_session_ids.lock().unwrap(); - sessions.insert(session_id.clone()); - } - - /// Check if a session ID corresponds to an active research session. - /// - /// Used by the main event loop to determine if special handling is needed - /// (e.g., auto-approving Read permissions). - pub fn is_research_session(&self, session_id: &SessionId) -> bool { - let sessions = self.active_research_session_ids.lock().unwrap(); - sessions.contains(session_id) - } - - /// Unregister a research session ID. - /// - /// Called by research_agent::run when the session completes or fails. - pub fn unregister_session(&self, session_id: &SessionId) { - let mut sessions = self.active_research_session_ids.lock().unwrap(); - sessions.remove(session_id); - } -} diff --git a/src/symposium-crate-sources-proxy/tests/basic_integration.rs b/src/symposium-crate-sources-proxy/tests/basic_integration.rs index ebdb614c..de3e31b4 100644 --- a/src/symposium-crate-sources-proxy/tests/basic_integration.rs +++ b/src/symposium-crate-sources-proxy/tests/basic_integration.rs @@ -45,7 +45,7 @@ async fn test_rust_crate_query_with_elizacp() -> Result<()> { // Verify the response matches expected output // The research sub-agent session is spawned successfully. Eliza responds with // a greeting, then calls the get_rust_crate_source tool which returns empty results. - expect![[r#"Hello. How are you feeling today?OK: CallToolResult { content: [Annotated { raw: Text(RawTextContent { text: "{\"result\":[]}", meta: None }), annotations: None }], structured_content: Some(Object {"result": Array []}), is_error: Some(false), meta: None }"#]].assert_eq(&response); + expect![[r#"OK: CallToolResult { content: [Annotated { raw: Text(RawTextContent { text: "{\"result\":[]}", meta: None }), annotations: None }], structured_content: Some(Object {"result": Array []}), is_error: Some(false), meta: None }"#]].assert_eq(&response); Ok(()) } diff --git a/src/symposium-math/src/server.rs b/src/symposium-math/src/server.rs index dea517b7..cb0dd07d 100644 --- a/src/symposium-math/src/server.rs +++ b/src/symposium-math/src/server.rs @@ -106,15 +106,14 @@ pub async fn run_mcp_stdio() -> Result<()> { /// Run as ACP proxy component that provides the MCP server. pub async fn run_acp_proxy() -> Result<()> { use sacp::ProxyToConductor; - use sacp::mcp_server::McpServiceRegistry; - use sacp_rmcp::McpServiceRegistryRmcpExt; + use sacp::mcp_server::McpServer; + use sacp_rmcp::McpServerExt; - let mcp_registry = - McpServiceRegistry::new().with_rmcp_server("symposium-math", MathServer::new)?; + let mcp_server = McpServer::from_rmcp("symposium-math", MathServer::new); ProxyToConductor::builder() .name("symposium-math-proxy") - .with_handler(mcp_registry) + .with_mcp_server(mcp_server) .serve(sacp_tokio::Stdio::new()) .await?; diff --git a/zed-extension/dev/README.md b/zed-extension/dev/README.md deleted file mode 100644 index 3c65b024..00000000 --- a/zed-extension/dev/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Symposium Dev Extension - -Development version of the Symposium Zed extension that uses your locally installed `symposium-acp-agent` instead of downloading release binaries. - -## Prerequisites - -Install symposium-acp-agent locally: - -```bash -cargo install --path crates/symposium-acp-agent -``` - -## How it works - -The extension downloads a tiny wrapper script that simply calls `symposium-acp-agent` from your PATH. This means you can `cargo install` new versions and they take effect immediately without updating the extension. - -The wrapper archives are automatically updated on each push to main via CI. - -## Installing the extension - -Add this directory as a dev extension in Zed. diff --git a/zed-extension/dev/extension.toml b/zed-extension/dev/extension.toml deleted file mode 100644 index 6fdbba54..00000000 --- a/zed-extension/dev/extension.toml +++ /dev/null @@ -1,31 +0,0 @@ -id = "symposium-dev" -name = "Symposium Dev" -version = "0.1.0" -schema_version = 1 -authors = ["Niko Matsakis "] -description = "Development version - uses locally installed symposium-acp-agent" -repository = "https://github.com/symposium-dev/symposium" - -[agent_servers.symposium-dev] -name = "Symposium Dev" -icon = "./icons/symposium.svg" - -[agent_servers.symposium-dev.targets.darwin-aarch64] -archive = "https://github.com/symposium-dev/symposium/releases/download/zed-dev-wrapper/symposium-dev-darwin.tar.gz" -cmd = "./symposium-dev" -args = ["--", "npx", "-y", "@anthropic-ai/claude-code@latest"] - -[agent_servers.symposium-dev.targets.darwin-x86_64] -archive = "https://github.com/symposium-dev/symposium/releases/download/zed-dev-wrapper/symposium-dev-darwin.tar.gz" -cmd = "./symposium-dev" -args = ["--", "npx", "-y", "@anthropic-ai/claude-code@latest"] - -[agent_servers.symposium-dev.targets.linux-x86_64] -archive = "https://github.com/symposium-dev/symposium/releases/download/zed-dev-wrapper/symposium-dev-linux.tar.gz" -cmd = "./symposium-dev" -args = ["--", "npx", "-y", "@anthropic-ai/claude-code@latest"] - -[agent_servers.symposium-dev.targets.linux-aarch64] -archive = "https://github.com/symposium-dev/symposium/releases/download/zed-dev-wrapper/symposium-dev-linux.tar.gz" -cmd = "./symposium-dev" -args = ["--", "npx", "-y", "@anthropic-ai/claude-code@latest"] diff --git a/zed-extension/dev/icons/symposium.svg b/zed-extension/dev/icons/symposium.svg deleted file mode 100644 index 15862117..00000000 --- a/zed-extension/dev/icons/symposium.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/zed-extension/dev/wrapper/.gitignore b/zed-extension/dev/wrapper/.gitignore deleted file mode 100644 index 335ec957..00000000 --- a/zed-extension/dev/wrapper/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.tar.gz diff --git a/zed-extension/dev/wrapper/build-archives.sh b/zed-extension/dev/wrapper/build-archives.sh deleted file mode 100755 index 28dec99f..00000000 --- a/zed-extension/dev/wrapper/build-archives.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# Build wrapper archives for the Symposium Dev Zed extension - -set -e -cd "$(dirname "$0")" - -# Darwin (macOS) - same script works for both architectures -tar -czvf symposium-dev-darwin.tar.gz symposium-dev - -# Linux - same script works for both architectures -tar -czvf symposium-dev-linux.tar.gz symposium-dev - -echo "Built archives:" -ls -la *.tar.gz diff --git a/zed-extension/dev/wrapper/symposium-dev b/zed-extension/dev/wrapper/symposium-dev deleted file mode 100755 index 85514801..00000000 --- a/zed-extension/dev/wrapper/symposium-dev +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exec symposium-acp-agent "$@"