diff --git a/Cargo.lock b/Cargo.lock index 18c53c18ca0..2d3c09ed097 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,7 +117,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -128,7 +128,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -798,7 +798,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.23.0", + "darling 0.20.11", "ident_case", "prettyplease", "proc-macro2", @@ -1566,9 +1566,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -1712,7 +1712,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2333,7 +2333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -2544,7 +2544,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2767,7 +2767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2839,6 +2839,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fancy-regex" version = "0.11.0" @@ -3183,7 +3195,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.1.3", + "windows-link 0.2.1", "windows-result 0.4.1", ] @@ -3407,6 +3419,15 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "hashlink" version = "0.11.0" @@ -4083,9 +4104,9 @@ dependencies = [ [[package]] name = "iroh" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971" +checksum = "460de6bc52163b41b1646931f2897e5ab986f0966ade444467fec25024751a72" dependencies = [ "backon", "blake3", @@ -4134,9 +4155,9 @@ dependencies = [ [[package]] name = "iroh-base" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830a582cd54410dc1aa71d4786a82c3297d7b0165accd8b6dbbb3b240b48140d" +checksum = "6be73e16ee21c923aca9b3121aaa0db936f7c7ecc156ff47b8dac944c68d59a8" dependencies = [ "curve25519-dalek 5.0.0-rc.0", "data-encoding", @@ -4153,9 +4174,9 @@ dependencies = [ [[package]] name = "iroh-dns" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a" +checksum = "46f6a9b39d18e6345f5c151afd299f2488e2cb5c520fe41b107b6bd3dc4c3349" dependencies = [ "arc-swap", "cfg_aliases", @@ -4204,9 +4225,9 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354" +checksum = "24bd586cf927f7b700f56ec3639b53cb5fa901ce284784051ff71092bfbf8193" dependencies = [ "blake3", "bytes", @@ -4243,7 +4264,6 @@ dependencies = [ "tokio-websockets", "tracing", "url", - "vergen-gitcl", "webpki-roots 1.0.7", "ws_stream_wasm", ] @@ -4365,16 +4385,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733a844dbd6fef128e98cb4487b887cb55454d92cd9994b1bafe004fabbe670c" -dependencies = [ - "serde", - "ucd-trie", -] - [[package]] name = "jsonpath-rust" version = "0.7.5" @@ -4575,10 +4585,11 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.30.1" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ + "cc", "pkg-config", "vcpkg", ] @@ -4835,8 +4846,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "hex", "mesh-llm-client", @@ -4845,8 +4856,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -4856,21 +4867,18 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "mesh-llm-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "crypto_box", - "ed25519-dalek", - "hex", "httparse", "iroh", "mesh-llm-identity", @@ -4880,11 +4888,9 @@ dependencies = [ "model-artifact", "nostr-sdk", "prost 0.14.3", - "rand 0.10.1", "rustls", "serde", "serde_json", - "sha2 0.10.9", "thiserror 2.0.18", "tokio", "tracing", @@ -4893,8 +4899,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -4909,8 +4915,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -4919,20 +4925,25 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", + "chrono", "clap", "crossterm 0.28.1", + "libc", "ratatui", + "serde", "serde_json", + "tracing", + "uuid", ] [[package]] name = "mesh-llm-gpu-bench" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", "serde_json", @@ -4941,8 +4952,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", "serde_json", @@ -4950,8 +4961,8 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "mesh-llm-native-runtime", ] @@ -4987,8 +4998,8 @@ dependencies = [ [[package]] name = "mesh-llm-host-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "argon2", @@ -5010,7 +5021,6 @@ dependencies = [ "http-body-util", "httparse", "iroh", - "json5", "keyring", "libc", "mdns-sd", @@ -5022,6 +5032,7 @@ dependencies = [ "mesh-llm-guardrails", "mesh-llm-hf-hub", "mesh-llm-identity", + "mesh-llm-log-store", "mesh-llm-native-runtime", "mesh-llm-node", "mesh-llm-plugin", @@ -5055,7 +5066,6 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml", "sha2 0.10.9", "skippy-coordinator", "skippy-ffi", @@ -5075,14 +5085,16 @@ dependencies = [ "tracing-subscriber", "url", "urlencoding", + "uuid", + "windows-sys 0.61.2", "zeroize", "zip", ] [[package]] name = "mesh-llm-identity" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "argon2", "base64 0.22.1", @@ -5101,10 +5113,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mesh-llm-log-store" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" +dependencies = [ + "chrono", + "data-encoding", + "hex", + "mesh-llm-events", + "rusqlite", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.18", + "uuid", + "windows-sys 0.61.2", +] + [[package]] name = "mesh-llm-native-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "serde", @@ -5114,8 +5144,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-types", @@ -5128,8 +5158,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", @@ -5145,8 +5175,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -5164,8 +5194,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "hex", @@ -5177,8 +5207,8 @@ dependencies = [ [[package]] name = "mesh-llm-release-footer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "hex", "sha2 0.10.9", @@ -5186,16 +5216,17 @@ dependencies = [ [[package]] name = "mesh-llm-routing" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "iroh", + "serde_json", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -5217,8 +5248,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5232,8 +5263,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -5243,8 +5274,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "chrono", @@ -5270,8 +5301,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "hex", "serde", @@ -5281,13 +5312,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "mesh-mixture-of-agents" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5300,18 +5331,19 @@ dependencies = [ [[package]] name = "mesh-native-serving-plugin-api" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "mesh-native-serving-plugin-host" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "libloading", "mesh-native-serving-plugin-api", "skippy-server", + "skippy-tokenizer", ] [[package]] @@ -5451,8 +5483,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", @@ -5462,8 +5494,8 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", @@ -5480,8 +5512,8 @@ dependencies = [ [[package]] name = "model-package" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "bytes", @@ -5500,16 +5532,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "model-artifact", @@ -5860,9 +5892,9 @@ dependencies = [ [[package]] name = "noq" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89" +checksum = "09e4bb6601fa543c110d8957813267d5a8d775a0f8fbaccf1f615d06ba9b10da" dependencies = [ "bytes", "cfg_aliases", @@ -5882,9 +5914,9 @@ dependencies = [ [[package]] name = "noq-proto" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" +checksum = "baa7b5ccd819a9c68a0d955e67a881032d09b1a17219b1f90b0997a0888e1a15" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -5910,9 +5942,9 @@ dependencies = [ [[package]] name = "noq-udp" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e" +checksum = "02bba20e097a5a16cd0ad14ec882fae1e80a092a124e9422fc4dddd92e96a647" dependencies = [ "cfg_aliases", "libc", @@ -6028,7 +6060,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6310,19 +6342,20 @@ checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" [[package]] name = "openai-frontend" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "async-trait", "axum", "futures-core", "futures-util", + "mesh-llm-events", "mesh-llm-guardrails", "serde", "serde_json", "tokio", - "tokio-stream", "tracing", + "uuid", ] [[package]] @@ -7501,7 +7534,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -8102,6 +8135,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "rusqlite" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +dependencies = [ + "bitflags 2.13.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.10.0", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rust-ini" version = "0.21.3" @@ -8172,7 +8219,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8185,7 +8232,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8244,7 +8291,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8527,7 +8574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8887,8 +8934,8 @@ checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "skippy-cache" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "blake3", @@ -8897,29 +8944,29 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "libloading", ] [[package]] name = "skippy-metrics" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "skippy-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "prost 0.14.3", "prost-build 0.14.3", @@ -8930,8 +8977,8 @@ dependencies = [ [[package]] name = "skippy-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "libc", @@ -8944,8 +8991,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "ahash", "anyhow", @@ -8956,6 +9003,7 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-llm-events", "mesh-native-serving-plugin-api", "model-artifact", "openai-frontend", @@ -8968,24 +9016,25 @@ dependencies = [ "skippy-protocol", "skippy-runtime", "skippy-tokenizer", + "skippy-topology", "socket2", "tokio", - "tokio-stream", "tonic", + "uuid", ] [[package]] name = "skippy-tokenizer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", ] [[package]] name = "skippy-topology" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", "serde_json", @@ -9024,7 +9073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -9122,7 +9171,7 @@ dependencies = [ "futures-io", "futures-util", "hashbrown 0.16.1", - "hashlink", + "hashlink 0.11.0", "indexmap", "log", "memchr", @@ -9652,7 +9701,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9665,7 +9714,7 @@ dependencies = [ "parking_lot", "rustix 1.1.4", "signal-hook", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10435,7 +10484,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10643,43 +10692,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vergen" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "vergen-lib", -] - -[[package]] -name = "vergen-gitcl" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "time", - "vergen", - "vergen-lib", -] - -[[package]] -name = "vergen-lib" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", -] - [[package]] name = "version_check" version = "0.9.5" @@ -11019,7 +11031,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Justfile b/Justfile index fe5d7bf2858..8bd2cb5b238 100644 --- a/Justfile +++ b/Justfile @@ -548,7 +548,11 @@ desktop-standalone *ARGS: _ensure-sidecar-stubs fi trap '../scripts/cleanup-instance-agents.sh "$INSTANCE_ID" || true' EXIT echo "Starting standalone desktop on Vite port ${BUZZ_VITE_PORT}; no relay services were started" - pnpm exec tauri dev --config "$BUZZ_TAURI_CONFIG" {{ARGS}} + FEATURES=() + if [[ -n "{{mesh}}" ]]; then + FEATURES=(--features mesh-llm) + fi + pnpm exec tauri dev ${FEATURES[@]+"${FEATURES[@]}"} --config "$BUZZ_TAURI_CONFIG" {{ARGS}} # Run the desktop app against the internal staging relay (installs deps + builds agent tools automatically) staging *ARGS: bootstrap _ensure-sidecar-stubs diff --git a/crates/buzz-relay/Cargo.toml b/crates/buzz-relay/Cargo.toml index deb2e7e16a5..198a6fda40a 100644 --- a/crates/buzz-relay/Cargo.toml +++ b/crates/buzz-relay/Cargo.toml @@ -86,8 +86,8 @@ async-compression = { version = "0.4.42", features = ["tokio", "gzip"] } dev = ["buzz-auth/dev"] [dev-dependencies] -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } # Relay-driven mesh lifecycle smoke (examples/mesh_relay_lifecycle_smoke.rs): # the relay client for discovery notes and the exact ed25519 the mesh owner # keys use for binding verification. diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index fb60a351895..f19a1708454 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -210,7 +210,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -221,7 +221,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1474,9 +1474,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -1639,7 +1639,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -2306,7 +2306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.118", ] [[package]] @@ -2484,7 +2484,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2765,7 +2765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3283,7 +3283,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.1.3", + "windows-link 0.2.1", "windows-result 0.4.1", ] @@ -4357,14 +4357,14 @@ dependencies = [ [[package]] name = "iroh" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971" +checksum = "460de6bc52163b41b1646931f2897e5ab986f0966ade444467fec25024751a72" dependencies = [ "backon", "blake3", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "ctutils", "data-encoding", "derive_more", @@ -4408,9 +4408,9 @@ dependencies = [ [[package]] name = "iroh-base" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830a582cd54410dc1aa71d4786a82c3297d7b0165accd8b6dbbb3b240b48140d" +checksum = "6be73e16ee21c923aca9b3121aaa0db936f7c7ecc156ff47b8dac944c68d59a8" dependencies = [ "curve25519-dalek 5.0.0-rc.0", "data-encoding", @@ -4427,12 +4427,12 @@ dependencies = [ [[package]] name = "iroh-dns" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a" +checksum = "46f6a9b39d18e6345f5c151afd299f2488e2cb5c520fe41b107b6bd3dc4c3349" dependencies = [ "arc-swap", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "hickory-resolver", "iroh-base", @@ -4478,13 +4478,13 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354" +checksum = "24bd586cf927f7b700f56ec3639b53cb5fa901ce284784051ff71092bfbf8193" dependencies = [ "blake3", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "data-encoding", "derive_more", "getrandom 0.4.3", @@ -4517,7 +4517,6 @@ dependencies = [ "tokio-websockets", "tracing", "url", - "vergen-gitcl", "webpki-roots 1.0.8", "ws_stream_wasm", ] @@ -4692,16 +4691,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "json5" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733a844dbd6fef128e98cb4487b887cb55454d92cd9994b1bafe004fabbe670c" -dependencies = [ - "serde", - "ucd-trie", -] - [[package]] name = "jsonptr" version = "0.6.3" @@ -5175,8 +5164,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "hex", "mesh-llm-client", @@ -5185,8 +5174,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5196,21 +5185,18 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "mesh-llm-client" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "crypto_box", - "ed25519-dalek", - "hex", "httparse", "iroh", "mesh-llm-identity", @@ -5220,11 +5206,9 @@ dependencies = [ "model-artifact", "nostr-sdk", "prost 0.14.4", - "rand 0.10.2", "rustls", "serde", "serde_json", - "sha2 0.10.9", "thiserror 2.0.18", "tokio", "tracing", @@ -5233,8 +5217,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -5249,8 +5233,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -5259,20 +5243,25 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", + "chrono", "clap", "crossterm 0.28.1", + "libc", "ratatui", + "serde", "serde_json", + "tracing", + "uuid", ] [[package]] name = "mesh-llm-gpu-bench" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", "serde_json", @@ -5281,8 +5270,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", "serde_json", @@ -5290,8 +5279,8 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "mesh-llm-native-runtime", ] @@ -5327,8 +5316,8 @@ dependencies = [ [[package]] name = "mesh-llm-host-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "argon2", @@ -5350,7 +5339,6 @@ dependencies = [ "http-body-util", "httparse", "iroh", - "json5", "keyring", "libc", "mdns-sd", @@ -5362,6 +5350,7 @@ dependencies = [ "mesh-llm-guardrails", "mesh-llm-hf-hub", "mesh-llm-identity", + "mesh-llm-log-store", "mesh-llm-native-runtime", "mesh-llm-node", "mesh-llm-plugin", @@ -5395,7 +5384,6 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml", "sha2 0.10.9", "skippy-coordinator", "skippy-ffi", @@ -5415,14 +5403,16 @@ dependencies = [ "tracing-subscriber", "url", "urlencoding", + "uuid", + "windows-sys 0.61.2", "zeroize", "zip 2.4.2", ] [[package]] name = "mesh-llm-identity" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "argon2", "base64 0.22.1", @@ -5441,10 +5431,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mesh-llm-log-store" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" +dependencies = [ + "chrono", + "data-encoding", + "hex", + "mesh-llm-events", + "rusqlite", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.18", + "uuid", + "windows-sys 0.61.2", +] + [[package]] name = "mesh-llm-native-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "serde", @@ -5454,8 +5462,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-types", @@ -5468,8 +5476,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", @@ -5485,8 +5493,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -5504,8 +5512,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "hex", @@ -5517,8 +5525,8 @@ dependencies = [ [[package]] name = "mesh-llm-release-footer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "hex", "sha2 0.10.9", @@ -5526,16 +5534,17 @@ dependencies = [ [[package]] name = "mesh-llm-routing" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "iroh", + "serde_json", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -5557,8 +5566,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5572,8 +5581,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "dirs", @@ -5583,8 +5592,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "chrono", @@ -5610,8 +5619,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "hex", "serde", @@ -5621,13 +5630,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "mesh-mixture-of-agents" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5640,18 +5649,19 @@ dependencies = [ [[package]] name = "mesh-native-serving-plugin-api" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "mesh-native-serving-plugin-host" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "libloading 0.8.9", "mesh-native-serving-plugin-api", "skippy-server", + "skippy-tokenizer", ] [[package]] @@ -5741,13 +5751,13 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "model-artifact" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", @@ -5757,8 +5767,8 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "async-trait", @@ -5775,8 +5785,8 @@ dependencies = [ [[package]] name = "model-package" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "bytes", @@ -5795,16 +5805,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "model-artifact", @@ -5900,7 +5910,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5936,7 +5946,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2ab99dfb861450e68853d34ae665243a88b8c493d01ba957321a1e9b2312bbe" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "futures-buffered", "futures-lite", @@ -6126,7 +6136,7 @@ checksum = "4d9cbe01741347ef750d743d6690603f5eed8341e679fb51c8e629337aa11976" dependencies = [ "atomic-waker", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "ipnet", "js-sys", @@ -6181,7 +6191,7 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "memoffset", ] @@ -6194,7 +6204,7 @@ checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -6206,7 +6216,7 @@ checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -6231,12 +6241,12 @@ dependencies = [ [[package]] name = "noq" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89" +checksum = "09e4bb6601fa543c110d8957813267d5a8d775a0f8fbaccf1f615d06ba9b10da" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "noq-proto", "noq-udp", @@ -6253,9 +6263,9 @@ dependencies = [ [[package]] name = "noq-proto" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" +checksum = "baa7b5ccd819a9c68a0d955e67a881032d09b1a17219b1f90b0997a0888e1a15" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -6281,11 +6291,11 @@ dependencies = [ [[package]] name = "noq-udp" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e" +checksum = "02bba20e097a5a16cd0ad14ec882fae1e80a092a124e9422fc4dddd92e96a647" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "socket2", "tracing", @@ -6413,7 +6423,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6523,7 +6533,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate 2.0.2", "proc-macro2", "quote", "syn 2.0.118", @@ -6905,19 +6915,20 @@ dependencies = [ [[package]] name = "openai-frontend" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "async-trait", "axum", "futures-core", "futures-util", + "mesh-llm-events", "mesh-llm-guardrails", "serde", "serde_json", "tokio", - "tokio-stream", "tracing", + "uuid", ] [[package]] @@ -7117,7 +7128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] @@ -8096,7 +8107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "pin-project-lite", "quinn-proto", "quinn-udp", @@ -8138,12 +8149,12 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8874,7 +8885,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8944,7 +8955,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9211,7 +9222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9696,8 +9707,8 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skippy-cache" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "blake3", @@ -9706,29 +9717,29 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "libloading 0.8.9", ] [[package]] name = "skippy-metrics" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" [[package]] name = "skippy-protocol" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "prost 0.14.4", "prost-build 0.14.4", @@ -9739,8 +9750,8 @@ dependencies = [ [[package]] name = "skippy-runtime" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "anyhow", "libc", @@ -9753,8 +9764,8 @@ dependencies = [ [[package]] name = "skippy-server" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "ahash", "anyhow", @@ -9765,6 +9776,7 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-llm-events", "mesh-native-serving-plugin-api", "model-artifact", "openai-frontend", @@ -9777,24 +9789,25 @@ dependencies = [ "skippy-protocol", "skippy-runtime", "skippy-tokenizer", + "skippy-topology", "socket2", "tokio", - "tokio-stream", "tonic", + "uuid", ] [[package]] name = "skippy-tokenizer" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", ] [[package]] name = "skippy-topology" -version = "0.75.1" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.75.1#3295c902d4c4f859aaadf9240042ffdaf06dd07e" +version = "0.76.0-rc7" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc7#5d07a20b674dfa4cd3d86f6721e8c5b2284d1d23" dependencies = [ "serde", "serde_json", @@ -9830,7 +9843,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10855,10 +10868,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -10880,7 +10893,7 @@ dependencies = [ "parking_lot", "rustix 1.1.4", "signal-hook 0.3.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11622,7 +11635,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11723,7 +11736,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11986,43 +11999,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vergen" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "vergen-lib", -] - -[[package]] -name = "vergen-gitcl" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "time", - "vergen", - "vergen-lib", -] - -[[package]] -name = "vergen-lib" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", -] - [[package]] name = "version-compare" version = "0.2.1" @@ -12574,7 +12550,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 3f7189deea1..f14375f8171 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -110,15 +110,15 @@ buzz_voice_pkg = { package = "buzz-voice", path = "../../crates/buzz-voice" } buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" } buzz_ws_client_pkg = { package = "buzz-ws-client", path = "../../crates/buzz-ws-client" } portable-pty = "0.9" -iroh = { version = "1.0.2", optional = true } -mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } -mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } +iroh = { version = "1.0.3", optional = true } +mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true } +mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true } # Model catalog + hardware survey for the Share-compute model picker (same # diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client. -mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-client", optional = true } -mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-node", optional = true } -mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-system", optional = true } -mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.75.1", package = "mesh-llm-events", optional = true } +mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-client", optional = true } +mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-node", optional = true } +mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-system", optional = true } +mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc7", package = "mesh-llm-events", optional = true } base64 = "0.22" sha2 = "0.11" tar = "0.4" diff --git a/desktop/src-tauri/src/commands/mesh_llm.rs b/desktop/src-tauri/src/commands/mesh_llm.rs index 7356cd7fc0c..892ea4bea07 100644 --- a/desktop/src-tauri/src/commands/mesh_llm.rs +++ b/desktop/src-tauri/src/commands/mesh_llm.rs @@ -176,9 +176,71 @@ fn advance_mesh_status_cursor( Ok(cursor) } -async fn query_mesh_discovery_events_at( +/// Resolve whether a relay enforces NIP-43 relay membership, from its NIP-11 +/// document. +/// +/// The relay advertises NIP-43 only when membership is actually enforced +/// (`nip11_facts` in `buzz-relay`), so this is the one signal that separates +/// "no roster because nobody is a direct member here" from "no roster because +/// this read was incomplete". A probe failure is reported as `Err` and treated +/// as enforcing by callers: an unreachable relay must never be able to relax +/// admission. +async fn probe_relay_mesh_mode(relay_url: &str) -> Result { + let http_url = relay::relay_http_base_url(relay_url); + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(5)) + .build() + .map_err(|error| format!("failed to build NIP-11 client: {error}"))?; + let document: serde_json::Value = client + .get(&http_url) + .header("Accept", "application/nostr+json") + .send() + .await + .map_err(|error| format!("NIP-11 probe failed: {error}"))? + .json() + .await + .map_err(|error| format!("NIP-11 document was not JSON: {error}"))?; + mesh_llm::relay_mesh_mode_from_nip11(&document) +} + +/// Probe the relay's membership mode, falling back to the enforcing behaviour. +/// +/// A failed probe must not open the mesh up: an unreachable or malformed NIP-11 +/// document leaves admission exactly as strict as it is today. +/// +/// Successful probes are memoized per relay URL: the deployment mode +/// (`BUZZ_REQUIRE_RELAY_MEMBERSHIP`) does not flap at runtime, and the +/// discovery/reconcile paths would otherwise re-issue this HTTP GET on every +/// poll. Failures are NOT cached, so a node that fell back to enforcing on a +/// transient blip re-probes on the next poll and can recover. +async fn resolved_relay_mesh_mode(relay_url: &str) -> mesh_llm::MeshRelayMode { + static CACHE: std::sync::OnceLock< + std::sync::Mutex>, + > = std::sync::OnceLock::new(); + let cache = CACHE.get_or_init(Default::default); + if let Ok(guard) = cache.lock() { + if let Some(mode) = guard.get(relay_url).copied() { + return mode; + } + } + match probe_relay_mesh_mode(relay_url).await { + Ok(mode) => { + if let Ok(mut guard) = cache.lock() { + guard.insert(relay_url.to_string(), mode); + } + mode + } + Err(error) => { + eprintln!("buzz-mesh: NIP-11 probe failed; assuming membership is enforced: {error}"); + mesh_llm::MeshRelayMode::ClosedMembershipEnforced + } + } +} + +async fn query_mesh_discovery_events_for_mode( state: &AppState, relay_url: &str, + mode: mesh_llm::MeshRelayMode, ) -> Result, String> { let api_base_url = relay::relay_http_base_url(relay_url); let mut events = @@ -193,13 +255,27 @@ async fn query_mesh_discovery_events_at( // means the query is incomplete: surface it as an error so the // reconcile loop keeps the current allowlist instead of flapping the // node down to self-only on a successful-but-empty response. - if !mesh_llm::has_membership_snapshot(&events) { - return Err("relay returned no membership snapshot".to_string()); + // + // A relay that does not enforce membership never populates + // `relay_members` from sign-in, so there is no roster to wait for and + // no incomplete read to guard against. Fall through and read status + // notes unfiltered so the caller can run the mesh unenforced. + if !mode.is_open() { + if !mesh_llm::has_membership_snapshot(&events) { + return Err("relay returned no membership snapshot".to_string()); + } + return Ok(events); } - return Ok(events); } let mut status_filter = mesh_llm::mesh_status_filter(); - status_filter["authors"] = serde_json::json!(member_pubkeys); + // On an enforcing relay, scope status reads to current members. On an open + // relay the snapshot is not a trust boundary even when one exists (added + // out of band) — admission runs unenforced there, so scoping reads to a + // stray roster would hide peers the mesh admits. Read every reporter's + // note and let freshness + endpoint-binding checks gate routing. + if !mode.is_open() && !member_pubkeys.is_empty() { + status_filter["authors"] = serde_json::json!(member_pubkeys); + } let mut previous_cursor: Option<(u64, String)> = None; loop { @@ -219,10 +295,6 @@ async fn query_mesh_discovery_events_at( } } -async fn query_mesh_discovery_events(state: &AppState) -> Result, String> { - query_mesh_discovery_events_at(state, &relay::relay_ws_url_with_override(state)).await -} - /// Resolve the admission roster by intersecting member-signed mesh status /// reporters with the current NIP-43 direct-member list. /// @@ -231,29 +303,39 @@ async fn query_mesh_discovery_events(state: &AppState) -> Result Result, String> { - let events = query_mesh_discovery_events(state).await?; - Ok(mesh_llm::owner_ids_from_events(&events)) +/// `None` means the relay does not enforce membership: there is no roster to +/// intersect and the node must run unenforced (`TrustPolicy::Off`), exactly as +/// stock MeshLLM does. `Some` carries an enforcing relay's roster. +pub(crate) async fn resolve_trusted_owner_ids( + state: &AppState, +) -> Result>, String> { + resolve_trusted_owner_ids_at(state, &relay::relay_ws_url_with_override(state)).await } pub(crate) async fn resolve_trusted_owner_ids_at( state: &AppState, relay_url: &str, -) -> Result, String> { - let events = query_mesh_discovery_events_at(state, relay_url).await?; - Ok(mesh_llm::owner_ids_from_events(&events)) +) -> Result>, String> { + let mode = resolved_relay_mesh_mode(relay_url).await; + let events = query_mesh_discovery_events_for_mode(state, relay_url, mode).await?; + if mode.is_open() { + return Ok(None); + } + Ok(Some(mesh_llm::owner_ids_from_events(&events))) } /// Resolve the roster for an initial node *start*, failing closed to self-only /// (an empty roster) when the relay query fails. This is safe only at start: /// there is no established allowlist to preserve yet. The periodic /// `reconcile_roster` path must NOT use this — it has a live roster to keep. -pub(crate) async fn resolve_trusted_owner_ids_or_self_only(state: &AppState) -> Vec { +pub(crate) async fn resolve_trusted_owner_ids_or_self_only( + state: &AppState, +) -> Option> { match resolve_trusted_owner_ids(state).await { Ok(owners) => owners, Err(error) => { eprintln!("buzz-mesh: roster query failed; allowing only this node: {error}"); - Vec::new() + Some(Vec::new()) } } } @@ -291,12 +373,13 @@ pub(crate) async fn resolve_buzz_mesh_join_targets_at( state: &AppState, relay_url: &str, ) -> Result, String> { - let events = query_mesh_discovery_events_at(state, relay_url).await?; + let mode = resolved_relay_mesh_mode(relay_url).await; + let events = query_mesh_discovery_events_for_mode(state, relay_url, mode).await?; let self_owner_id = mesh_llm::ensure_owner_identity() .map_err(|error| format!("failed to load mesh owner identity: {error}"))? .owner_id; Ok(buzz_mesh_join_targets( - mesh_llm::availability_from_events(events).serve_targets, + mesh_llm::availability_from_events_for_mode(events, mode).serve_targets, &self_owner_id, )) } @@ -308,15 +391,19 @@ pub(crate) async fn resolve_buzz_mesh_join_targets_at( async fn resolve_buzz_mesh_startup_at( state: &AppState, relay_url: &str, -) -> (Vec, Option) { - match query_mesh_discovery_events_at(state, relay_url).await { +) -> (Option>, Option) { + let mode = resolved_relay_mesh_mode(relay_url).await; + match query_mesh_discovery_events_for_mode(state, relay_url, mode).await { Ok(events) => { - let trusted_owner_ids = mesh_llm::owner_ids_from_events(&events); + // An open relay has no roster to enforce: `None` runs the node + // unenforced instead of collapsing it to a self-only allowlist. + let trusted_owner_ids = + (!mode.is_open()).then(|| mesh_llm::owner_ids_from_events(&events)); let join_token = mesh_llm::ensure_owner_identity() .ok() .and_then(|identity| { buzz_mesh_join_targets( - mesh_llm::availability_from_events(events).serve_targets, + mesh_llm::availability_from_events_for_mode(events, mode).serve_targets, &identity.owner_id, ) .into_iter() @@ -332,7 +419,7 @@ async fn resolve_buzz_mesh_startup_at( eprintln!( "buzz-mesh: startup discovery failed; allowing only this node and starting isolated for now: {error}" ); - (Vec::new(), None) + (Some(Vec::new()), None) } } } @@ -373,7 +460,7 @@ pub(crate) async fn restore_mesh_sharing(app: &AppHandle, state: &AppState) -> C join_token, mesh_name: Some(buzz_mesh_name_for_relay(&relay_url)), relay_url: Some(relay_url), - trusted_owner_ids: Some(trusted_owner_ids), + trusted_owner_ids, }; let started = mesh_llm::DesktopMeshRuntime::start(request) .await @@ -446,10 +533,17 @@ pub async fn mesh_start_node( // Frontend requests never carry a roster. Resolve it and the bootstrap // endpoint from one snapshot so UI startup does not repeat relay probes. - if request.trusted_owner_ids.is_none() || request.join_token.is_none() { + let roster_unresolved = request.trusted_owner_ids.is_none(); + if roster_unresolved || request.join_token.is_none() { let (trusted_owner_ids, join_token) = resolve_buzz_mesh_startup_at(&state, &relay_url).await; - request.trusted_owner_ids.get_or_insert(trusted_owner_ids); + // Adopt the resolver's decision only when the caller supplied no + // roster. `None` from the resolver is meaningful (open relay: run + // unenforced), so this cannot use `get_or_insert`, which would treat + // it as "nothing resolved" and leave the field untouched. + if roster_unresolved { + request.trusted_owner_ids = trusted_owner_ids; + } if request.join_token.is_none() { request.join_token = join_token; } @@ -590,7 +684,7 @@ pub(crate) async fn ensure_client_node_for_model( join_token: Some(join_token.clone()), mesh_name: Some(buzz_mesh_name(state)), relay_url: Some(relay::relay_ws_url_with_override(state)), - trusted_owner_ids: Some(resolve_trusted_owner_ids_or_self_only(state).await), + trusted_owner_ids: resolve_trusted_owner_ids_or_self_only(state).await, }; let mut runtime = state.mesh_llm_runtime.lock().await; if let Some(existing) = runtime.as_ref() { @@ -638,9 +732,11 @@ pub(crate) async fn resolve_mesh_bootstrap_target( if model_id.is_empty() { return Ok(None); } - let events = query_mesh_discovery_events(state).await?; + let relay_url = relay::relay_ws_url_with_override(state); + let mode = resolved_relay_mesh_mode(&relay_url).await; + let events = query_mesh_discovery_events_for_mode(state, &relay_url, mode).await?; Ok(pick_serve_target_for_model( - mesh_llm::availability_from_events(events).serve_targets, + mesh_llm::availability_from_events_for_mode(events, mode).serve_targets, model_id, )) } diff --git a/desktop/src-tauri/src/managed_agents/relay_mesh.rs b/desktop/src-tauri/src/managed_agents/relay_mesh.rs index 3858212bbba..b6d5e33f309 100644 --- a/desktop/src-tauri/src/managed_agents/relay_mesh.rs +++ b/desktop/src-tauri/src/managed_agents/relay_mesh.rs @@ -66,6 +66,21 @@ pub fn apply_relay_mesh_env( // survives (see `insert_default_if_unset`, and the copy-forward list in // `relay_mesh_process_env` that preserves it through the spawn path). insert_default_if_unset(env, "BUZZ_AGENT_REQUIRE_REPLY", "1"); + // A local mesh prefills a cold multi-ten-thousand-token prompt at a few + // hundred tokens/second, so the first turn of a large session legitimately + // runs for minutes with no bytes on the wire (`stream: false`). MeshLLM's + // own frontend gives such a request 600 s before it aborts + // (`OpenAiFrontendConfig::DEFAULT_BACKEND_TIMEOUT`); buzz-agent's 240 s + // default abandons it at four minutes, so the client gives up on work the + // server is still doing and retries it — adding load to a box that is + // already prefilling. Seat the client just above the server's budget so the + // mesh's own error is what surfaces, rather than a client-side abort racing + // it. Measured: an 88,318-token cold prompt on an M5 Max returns 200 at + // 503 s. Remote providers keep the 240 s default; only mesh moves. + // A default, not policy — an explicit user value survives via + // `insert_default_if_unset` and the copy-forward list in + // `relay_mesh_process_env`. + insert_default_if_unset(env, "BUZZ_AGENT_LLM_TIMEOUT_SECS", "660"); // Deliberately no BUZZ_AGENT_THINKING_EFFORT default: mesh translates // `reasoning_effort` into the chat template's `enable_thinking` flag, so any // value we pick overrides each model's own template default — and the right @@ -102,6 +117,10 @@ pub fn relay_mesh_process_env( for key in [ "BUZZ_AGENT_MAX_OUTPUT_TOKENS", "BUZZ_AGENT_THINKING_EFFORT", + // Same reason as `BUZZ_AGENT_REQUIRE_REPLY` below: without the + // copy-forward, an explicit user timeout is re-defaulted to 660 by + // `apply_relay_mesh_env`. + "BUZZ_AGENT_LLM_TIMEOUT_SECS", // Must be copied forward for the user's value to survive: this map is // written onto the command *after* the layered user env, so a key absent // here is re-defaulted by `apply_relay_mesh_env` below and an explicit @@ -270,6 +289,74 @@ mod tests { ); } + /// MeshLLM's frontend allows a backend call 600 s + /// (`OpenAiFrontendConfig::DEFAULT_BACKEND_TIMEOUT`). buzz-agent's 240 s + /// default abandons a healthy cold prefill long before the server does and + /// retries it, so the client budget must sit above the server's. + #[test] + fn native_provider_outlasts_the_mesh_backend_timeout() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some(RELAY_MESH_AUTO_MODEL_ID), + ); + + let seconds: u64 = env + .get("BUZZ_AGENT_LLM_TIMEOUT_SECS") + .expect("mesh seeds a client timeout") + .parse() + .expect("timeout is whole seconds"); + assert!( + seconds > 600, + "client budget ({seconds}s) must outlast the mesh frontend's 600s backend timeout" + ); + } + + #[test] + fn native_provider_preserves_explicit_llm_timeout() { + let mut env = + BTreeMap::from([("BUZZ_AGENT_LLM_TIMEOUT_SECS".to_string(), "90".to_string())]); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some(RELAY_MESH_AUTO_MODEL_ID), + ); + + assert_eq!( + env.get("BUZZ_AGENT_LLM_TIMEOUT_SECS").map(String::as_str), + Some("90"), + "an explicit timeout is a user decision, not a value to re-default" + ); + } + + /// Same spawn-ordering hazard as the reply guard: without the copy-forward + /// in `relay_mesh_process_env`, the user's value is re-defaulted here. + #[test] + fn process_env_preserves_explicit_llm_timeout() { + let effective_env = + BTreeMap::from([("BUZZ_AGENT_LLM_TIMEOUT_SECS".to_string(), "90".to_string())]); + + let env = relay_mesh_process_env(&effective_env, "Gemma-4"); + + assert_eq!( + env.get("BUZZ_AGENT_LLM_TIMEOUT_SECS").map(String::as_str), + Some("90") + ); + } + + #[test] + fn non_mesh_provider_leaves_llm_timeout_unset() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env(&mut env, Some("anthropic"), Some("claude-haiku-4.5")); + + assert_eq!( + env.get("BUZZ_AGENT_LLM_TIMEOUT_SECS"), + None, + "remote providers keep buzz-agent's own default" + ); + } + #[test] fn non_mesh_provider_leaves_reply_guard_unset() { let mut env = BTreeMap::new(); diff --git a/desktop/src-tauri/src/mesh_llm/catalog.rs b/desktop/src-tauri/src/mesh_llm/catalog.rs index 1a11fcfcd13..9990b164277 100644 --- a/desktop/src-tauri/src/mesh_llm/catalog.rs +++ b/desktop/src-tauri/src/mesh_llm/catalog.rs @@ -13,38 +13,47 @@ use mesh_llm_system::hardware; use mesh_llm_system::vram::{format_rated_capacity, rated_capacity_gb}; /// Buzz-curated tier picks. These are the models we know survive the agent -/// harness on shared compute — deliberately non-reasoning instruction models, -/// so agents stay snappy instead of burning hidden reasoning tokens. +/// harness on shared compute. /// -/// The large pick is resolved through mesh-llm's remote catalog -/// (huggingface.co/datasets/meshllm/catalog), so it does not need to exist in -/// the compiled `MODEL_CATALOG`; the entry is synthesized below. -const CURATED_LARGE: &str = "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M"; -const CURATED_LARGE_ALIAS: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M"; -const CURATED_LARGE_SIZE: &str = "17GB"; -const CURATED_LARGE_FILE: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf"; -const CURATED_LARGE_DESCRIPTION: &str = - "Gemma 4 26B MoE (4B active) — Buzz default for 64GB+ machines"; +/// The recommended ladder follows rated unified memory: +/// - below 32 GB: Gemma 4 E4B; +/// - 32 through 64 GB: Qwen3.5 9B; +/// - above 64 GB: Qwen3.8 27B. +/// +/// The Qwen entries are canonicalized from mesh-llm's compiled +/// `MODEL_CATALOG` rather than synthesized. +const CURATED_LARGE: &str = "unsloth/Qwen3.8-27B-GGUF:Q4_K_M"; +const CURATED_LARGE_ALIAS: &str = "Qwen3.8-27B-Q4_K_M"; +const CURATED_MEDIUM: &str = "unsloth/Qwen3.5-9B-GGUF:Q4_K_M"; +const CURATED_MEDIUM_ALIAS: &str = "Qwen3.5-9B-Vision-Q4_K_M"; const CURATED_SMALL: &str = "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M"; const CURATED_SMALL_ALIAS: &str = "Gemma-4-E4B-it-Q4_K_M"; -/// Rated-capacity boundary between the two curated tiers, in GB (marketing -/// capacity — a "64GB" Mac rates as 64 even though usable AI memory is less). -const CURATED_LARGE_MIN_RATED_GB: u64 = 64; +/// Superseded large pick, still mapped so machines that already selected it +/// keep resolving to a model id Mesh's OpenAI ingress accepts. +const LEGACY_LARGE: &str = "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M"; +const LEGACY_LARGE_ALIAS: &str = "gemma-4-26B-A4B-it-UD-Q4_K_M"; +/// Rated-capacity boundary for the balanced Qwen3.5 9B tier. +const CURATED_MEDIUM_MIN_RATED_GB: u64 = 32; +/// Qwen3.8 27B is reserved for rated capacity classes above 64 GB. +const CURATED_LARGE_MIN_RATED_GB: u64 = 80; /// The Buzz-curated recommendation for a machine's rated memory capacity. fn buzz_recommended_model(rated_gb: Option) -> &'static str { match rated_gb { Some(gb) if gb >= CURATED_LARGE_MIN_RATED_GB => CURATED_LARGE, - _ => CURATED_SMALL, + Some(gb) if gb >= CURATED_MEDIUM_MIN_RATED_GB => CURATED_MEDIUM, + Some(_) | None => CURATED_SMALL, } } -/// Convert Buzz's pre-0.74 curated package aliases into the canonical model +/// Convert Buzz's historical curated package aliases into the canonical model /// ids advertised and accepted by Mesh's OpenAI ingress. pub(crate) fn canonical_curated_model_id(model_id: &str) -> &str { match model_id.trim() { CURATED_SMALL_ALIAS => CURATED_SMALL, + CURATED_MEDIUM_ALIAS => CURATED_MEDIUM, CURATED_LARGE_ALIAS => CURATED_LARGE, + LEGACY_LARGE_ALIAS => LEGACY_LARGE, other => other, } } @@ -172,31 +181,14 @@ fn build_catalog( }) .collect(); - // The compiled MODEL_CATALOG does not know the Buzz large pick; it - // resolves through mesh-llm's remote catalog at download time. Synthesize - // its entry so the picker can offer it. - if !entries.iter().any(|e| e.name == CURATED_LARGE) { - let size_gb = parse_size_gb(CURATED_LARGE_SIZE); - entries.push(MeshCatalogEntry { - fit: fit_code(size_gb, vram_gb), - installed: is_installed(CURATED_LARGE_FILE, CURATED_LARGE) - || is_installed(CURATED_LARGE_FILE, CURATED_LARGE_ALIAS), - recommended: false, - curated: false, - name: CURATED_LARGE.to_string(), - size: CURATED_LARGE_SIZE.to_string(), - size_gb, - description: CURATED_LARGE_DESCRIPTION.to_string(), - }); - } - let recommended = Some(buzz_recommended_model(rated_capacity_gb(vram_bytes)).to_string()); for entry in &mut entries { entry.recommended = recommended.as_deref() == Some(entry.name.as_str()); - // Both curated tiers are always offered: the recommended one for this - // machine plus the other pick (e.g. the small one as an explicit - // lighter choice on big machines). - entry.curated = entry.name == CURATED_LARGE || entry.name == CURATED_SMALL; + // All curated tiers are always offered: the recommendation plus + // lighter and heavier alternatives appropriate to other machine tiers. + entry.curated = entry.name == CURATED_LARGE + || entry.name == CURATED_MEDIUM + || entry.name == CURATED_SMALL; } entries.sort_by(|a, b| { @@ -269,20 +261,28 @@ mod tests { } #[test] - fn recommendation_follows_buzz_curated_tiers() { + fn recommendation_follows_buzz_curated_ladder() { assert_eq!(CURATED_SMALL, "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M"); - assert_eq!(CURATED_LARGE, "unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M"); - // 64GB+ rated machines get the large curated pick. - let large = build_catalog(None, 64_000_000_000, 64.0, &[]); + assert_eq!(CURATED_MEDIUM, "unsloth/Qwen3.5-9B-GGUF:Q4_K_M"); + assert_eq!(CURATED_LARGE, "unsloth/Qwen3.8-27B-GGUF:Q4_K_M"); + + // Qwen3.8 is reserved for rated capacity classes above 64 GB. + let large = build_catalog(None, 80_000_000_000, 80.0, &[]); assert_eq!(large.recommended.as_deref(), Some(CURATED_LARGE)); let big = build_catalog(None, 128_000_000_000, 128.0, &[]); assert_eq!(big.recommended.as_deref(), Some(CURATED_LARGE)); - // Below the boundary: the small curated pick — never a reasoning - // model, never sub-4B guesswork. - let small = build_catalog(None, 32_000_000_000, 32.0, &[]); + + // The balanced Qwen3.5 tier spans 32 through 64 GB. + let medium = build_catalog(None, 32_000_000_000, 32.0, &[]); + assert_eq!(medium.recommended.as_deref(), Some(CURATED_MEDIUM)); + let medium_max = build_catalog(None, 64_000_000_000, 64.0, &[]); + assert_eq!(medium_max.recommended.as_deref(), Some(CURATED_MEDIUM)); + + // Smaller and unknown machines use the light Gemma tier. + let small = build_catalog(None, 24_000_000_000, 24.0, &[]); assert_eq!(small.recommended.as_deref(), Some(CURATED_SMALL)); - let tiny = build_catalog(None, 16_000_000_000, 16.0, &[]); - assert_eq!(tiny.recommended.as_deref(), Some(CURATED_SMALL)); + let unknown = build_catalog(None, 0, 0.0, &[]); + assert_eq!(unknown.recommended.as_deref(), Some(CURATED_SMALL)); } #[test] @@ -291,10 +291,15 @@ mod tests { canonical_curated_model_id(CURATED_SMALL_ALIAS), CURATED_SMALL ); + assert_eq!( + canonical_curated_model_id(CURATED_MEDIUM_ALIAS), + CURATED_MEDIUM + ); assert_eq!( canonical_curated_model_id(CURATED_LARGE_ALIAS), CURATED_LARGE ); + assert_eq!(canonical_curated_model_id(LEGACY_LARGE_ALIAS), LEGACY_LARGE); assert_eq!( canonical_curated_model_id("other/model:Q4"), "other/model:Q4" @@ -304,14 +309,17 @@ mod tests { #[test] fn curated_picks_lead_the_catalog() { let catalog = build_catalog(None, 96_000_000_000, 96.0, &[]); - // Recommended curated entry first, the other curated pick second, - // advanced entries after. + // Recommended curated entry first, then the other two curated tiers, + // with advanced entries after them. assert_eq!(catalog.entries[0].name, CURATED_LARGE); assert!(catalog.entries[0].recommended && catalog.entries[0].curated); - assert_eq!(catalog.entries[1].name, CURATED_SMALL); + assert_eq!(catalog.entries[1].name, CURATED_MEDIUM); assert!(catalog.entries[1].curated && !catalog.entries[1].recommended); - assert!(catalog.entries[2..].iter().all(|e| !e.curated)); - // The synthesized large pick carries a real size for fit ranking. + assert_eq!(catalog.entries[2].name, CURATED_SMALL); + assert!(catalog.entries[2].curated && !catalog.entries[2].recommended); + assert!(catalog.entries[3..].iter().all(|e| !e.curated)); + // The large pick comes from the compiled catalog with a real size, so + // fit ranking is meaningful rather than a placeholder. assert!(catalog.entries[0].size_gb > 10.0); } diff --git a/desktop/src-tauri/src/mesh_llm/coordinator.rs b/desktop/src-tauri/src/mesh_llm/coordinator.rs index 066fa463730..4bf870172b6 100644 --- a/desktop/src-tauri/src/mesh_llm/coordinator.rs +++ b/desktop/src-tauri/src/mesh_llm/coordinator.rs @@ -234,14 +234,28 @@ fn roster_shrinks(current: &[String], fresh: &[String]) -> bool { /// /// Rules: /// - query failed (`Err`) → `Keep` (never de-admit on a relay blip) +/// - relay enforces no membership (`Ok(None)`), roster beyond self → `Keep` (see below) +/// - relay enforces no membership (`Ok(None)`), self-only roster → `RestartProcess` (see below) /// - resolved roster == current → `Keep` (no-op) /// - grows (only additions) → `RestartProcess` immediately (fast admission) /// - shrinks/empties, first observation → `AwaitConfirm` (hold, re-check next poll) /// - shrinks/empties, confirmed → `RestartProcess` (same reduced roster twice) +/// +/// `Ok(None)` means the relay does not enforce NIP-43 membership, so there is +/// no roster to reconcile against. A node enforcing an allowlist that admits +/// *other* owners keeps it: relaxing a live multi-party allowlist on the +/// strength of a NIP-11 read would let a relay downgrade (or a spoofed +/// document) de-restrict a running mesh. A **self-only** allowlist is +/// different: it admits nobody else, so it protects nothing — and it is +/// exactly the fallback a transient NIP-11 probe failure at startup produces +/// on an open relay. Without recovery here that node stays at "1 NODE" until +/// an app restart (the original bug, reintroduced by a single HTTP blip). +/// Restarting resolves the roster afresh, which on an open relay yields +/// `None` → `TrustPolicy::Off`. fn roster_reconcile_action( current_owners: &[String], pending_shrink: Option<&[String]>, - query: Result, String>, + query: Result>, String>, ) -> RosterReconcileAction { let fresh = match query { Err(error) => { @@ -250,7 +264,20 @@ fn roster_reconcile_action( ); return RosterReconcileAction::Keep; } - Ok(fresh) => fresh, + Ok(None) if current_owners.is_empty() => { + eprintln!( + "buzz-mesh: relay does not enforce membership and this node is isolated by the \ + startup fallback; restarting to run the mesh unenforced" + ); + return RosterReconcileAction::RestartProcess; + } + Ok(None) => { + eprintln!( + "buzz-mesh: relay does not enforce membership; keeping this node's current allowlist" + ); + return RosterReconcileAction::Keep; + } + Ok(Some(fresh)) => fresh, }; if fresh == current_owners { @@ -558,10 +585,38 @@ mod tests { ); } + // Regression: a transient NIP-11 probe failure at startup on an OPEN relay + // falls back to the enforcing path and starts the node with a self-only + // allowlist. If reconcile then treated `Ok(None)` as an unconditional + // `Keep`, that node would sit at "1 NODE" until an app restart — the + // original open-relay bug, reintroduced by a single HTTP blip. A self-only + // roster admits nobody else, so restarting to run unenforced relaxes + // nothing that was protecting anyone. + #[test] + fn open_relay_recovers_a_fallback_isolated_node() { + let self_only: Vec = Vec::new(); + let action = roster_reconcile_action(&self_only, None, Ok(None)); + assert_eq!( + action, + RosterReconcileAction::RestartProcess, + "a fallback-isolated node on an open relay must restart into open mesh" + ); + } + + // The other side of that rule: an allowlist that admits OTHER owners is + // never relaxed on the strength of a NIP-11 read. A relay downgrade (or a + // spoofed document) must not de-restrict a running mesh. + #[test] + fn open_relay_report_keeps_a_multi_party_allowlist() { + let current = vec!["owner-a".to_string(), "owner-b".to_string()]; + let action = roster_reconcile_action(¤t, None, Ok(None)); + assert_eq!(action, RosterReconcileAction::Keep); + } + #[test] fn unchanged_roster_is_a_noop() { let current = vec!["owner-a".to_string()]; - let action = roster_reconcile_action(¤t, None, Ok(vec!["owner-a".to_string()])); + let action = roster_reconcile_action(¤t, None, Ok(Some(vec!["owner-a".to_string()]))); assert_eq!(action, RosterReconcileAction::Keep); } @@ -570,7 +625,7 @@ mod tests { fn roster_growth_requests_process_restart_immediately() { let current = vec!["owner-a".to_string()]; let fresh = vec!["owner-a".to_string(), "owner-c".to_string()]; - let action = roster_reconcile_action(¤t, None, Ok(fresh)); + let action = roster_reconcile_action(¤t, None, Ok(Some(fresh))); assert_eq!(action, RosterReconcileAction::RestartProcess); } @@ -579,7 +634,7 @@ mod tests { fn roster_shrink_awaits_confirmation_first() { let current = vec!["owner-a".to_string(), "owner-b".to_string()]; let reduced = vec!["owner-a".to_string()]; - let action = roster_reconcile_action(¤t, None, Ok(reduced.clone())); + let action = roster_reconcile_action(¤t, None, Ok(Some(reduced.clone()))); assert_eq!(action, RosterReconcileAction::AwaitConfirm(reduced)); } @@ -588,7 +643,7 @@ mod tests { fn roster_shrink_requests_process_restart_once_confirmed() { let current = vec!["owner-a".to_string(), "owner-b".to_string()]; let reduced = vec!["owner-a".to_string()]; - let action = roster_reconcile_action(¤t, Some(&reduced), Ok(reduced.clone())); + let action = roster_reconcile_action(¤t, Some(&reduced), Ok(Some(reduced.clone()))); assert_eq!(action, RosterReconcileAction::RestartProcess); } @@ -599,8 +654,11 @@ mod tests { let current = vec!["a".to_string(), "b".to_string(), "c".to_string()]; let first_reduced = vec!["a".to_string(), "b".to_string()]; let second_reduced = vec!["a".to_string()]; - let action = - roster_reconcile_action(¤t, Some(&first_reduced), Ok(second_reduced.clone())); + let action = roster_reconcile_action( + ¤t, + Some(&first_reduced), + Ok(Some(second_reduced.clone())), + ); assert_eq!(action, RosterReconcileAction::AwaitConfirm(second_reduced)); } @@ -609,10 +667,10 @@ mod tests { #[test] fn genuinely_empty_roster_awaits_then_restarts_to_self_only() { let current = vec!["owner-a".to_string()]; - let first = roster_reconcile_action(¤t, None, Ok(Vec::new())); + let first = roster_reconcile_action(¤t, None, Ok(Some(Vec::new()))); assert_eq!(first, RosterReconcileAction::AwaitConfirm(Vec::new())); let empty: Vec = Vec::new(); - let confirmed = roster_reconcile_action(¤t, Some(&empty), Ok(Vec::new())); + let confirmed = roster_reconcile_action(¤t, Some(&empty), Ok(Some(Vec::new()))); assert_eq!(confirmed, RosterReconcileAction::RestartProcess); } @@ -621,9 +679,10 @@ mod tests { fn roster_shrink_then_recovery_keeps_allowlist() { let current = vec!["owner-a".to_string(), "owner-b".to_string()]; let reduced = vec!["owner-a".to_string()]; - let held = roster_reconcile_action(¤t, None, Ok(reduced.clone())); + let held = roster_reconcile_action(¤t, None, Ok(Some(reduced.clone()))); assert_eq!(held, RosterReconcileAction::AwaitConfirm(reduced.clone())); - let recovered = roster_reconcile_action(¤t, Some(&reduced), Ok(current.clone())); + let recovered = + roster_reconcile_action(¤t, Some(&reduced), Ok(Some(current.clone()))); assert_eq!(recovered, RosterReconcileAction::Keep); } diff --git a/desktop/src-tauri/src/mesh_llm/discovery.rs b/desktop/src-tauri/src/mesh_llm/discovery.rs index 2df4a7f8c5e..173ed4d559d 100644 --- a/desktop/src-tauri/src/mesh_llm/discovery.rs +++ b/desktop/src-tauri/src/mesh_llm/discovery.rs @@ -20,6 +20,54 @@ fn status_is_fresh(event: &nostr::Event, now: u64) -> bool { >= now } +/// Whether the relay enforces NIP-43 relay membership. +/// +/// This is a property of the *relay deployment*, not of any particular query +/// result, and it is resolved from the relay's NIP-11 document (NIP-43 is +/// advertised only when membership is actually enforced). It must never be +/// inferred from an absent membership snapshot: on a closed relay a snapshot +/// can also be missing because of a transient replication gap, and treating +/// that as "open" would silently drop admission enforcement exactly when the +/// relay is least healthy. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum MeshRelayMode { + /// Membership is enforced (`BUZZ_REQUIRE_RELAY_MEMBERSHIP=true`). Every + /// participant is a NIP-43 direct member, so the membership snapshot is the + /// trust boundary for both admission and routing. + ClosedMembershipEnforced, + /// Membership is not enforced. Signing in never writes a `relay_members` + /// row, so the snapshot lists at most whoever was added out of band and is + /// structurally useless as a trust boundary. Mesh runs unenforced (stock + /// MeshLLM behaviour) and is scoped by the relay-derived mesh topic. + OpenNoMembership, +} + +impl MeshRelayMode { + pub(crate) fn is_open(self) -> bool { + matches!(self, Self::OpenNoMembership) + } +} + +/// Resolve [`MeshRelayMode`] from a relay's NIP-11 document. +/// +/// `buzz-relay` lists NIP-43 in `supported_nips` only when it has a stable +/// signing key *and* `BUZZ_REQUIRE_RELAY_MEMBERSHIP=true`, so the advertisement +/// is a faithful proxy for "membership is enforced here" when the field is +/// well formed. A missing or malformed `supported_nips` field is rejected so +/// callers keep the fail-closed fallback rather than treating an ambiguous +/// response as evidence that membership is disabled. +pub fn relay_mesh_mode_from_nip11(document: &serde_json::Value) -> Result { + let nips = document + .get("supported_nips") + .and_then(serde_json::Value::as_array) + .ok_or_else(|| "NIP-11 document has no supported_nips array".to_string())?; + if nips.iter().any(|nip| nip.as_u64() == Some(43)) { + Ok(MeshRelayMode::ClosedMembershipEnforced) + } else { + Ok(MeshRelayMode::OpenNoMembership) + } +} + fn dedupe_targets(targets: Vec) -> Vec { let mut by_endpoint_and_model = BTreeMap::<(String, String), MeshServeTarget>::new(); for target in targets { @@ -173,13 +221,45 @@ fn endpoint_binding_is_valid(event: &nostr::Event, content: &serde_json::Value) } pub fn availability_from_events(events: Vec) -> MeshAvailability { + availability_from_events_for_mode(events, MeshRelayMode::ClosedMembershipEnforced) +} + +/// [`availability_from_events`], scoped to the relay's membership mode. +/// +/// On a membership-enforcing relay the NIP-43 snapshot gates routing: a serve +/// target is selectable only when its status note is signed by a current +/// member. On a relay that does not enforce membership there is no such +/// roster to intersect with — requiring one would make every serving peer +/// permanently undiscoverable — so status notes are accepted on their own +/// merits (fresh, well-formed, and carrying a valid owner-signed endpoint +/// binding). The mesh topic is derived from the relay origin, so this widens +/// discovery to that relay's participants, not to the public internet. +pub fn availability_from_events_for_mode( + events: Vec, + mode: MeshRelayMode, +) -> MeshAvailability { if events.is_empty() { return MeshAvailability::unavailable("Buzz shared compute status is not published yet"); } - let Some(members) = latest_membership_list(&events) else { - return MeshAvailability::unavailable( - "Buzz shared compute is waiting for the current member roster", - ); + // On an open relay the snapshot is not a trust boundary even when one + // happens to exist (added out of band): admission runs `TrustPolicy::Off` + // there, so intersecting routing with a stray roster would hide peers the + // mesh happily admits. Ignore membership entirely and accept status notes + // on their own merits. + let members = if mode.is_open() { + None + } else { + match latest_membership_list(&events) { + Some(members) => Some(members), + // An enforcing relay always publishes a snapshot (even a + // zero-member one), so its absence means an incomplete read: + // stay closed. + None => { + return MeshAvailability::unavailable( + "Buzz shared compute is waiting for the current member roster", + ); + } + } }; // Status is replaceable per member pubkey, so a query returns multiple @@ -191,12 +271,14 @@ pub fn availability_from_events(events: Vec) -> MeshAvailability { let now = nostr::Timestamp::now().as_secs(); for event in events { - if event.kind.as_u16() as u64 != MESH_STATUS_KIND - || !status_is_fresh(&event, now) - || !members.contains(&event.pubkey.to_hex().to_ascii_lowercase()) - { + if event.kind.as_u16() as u64 != MESH_STATUS_KIND || !status_is_fresh(&event, now) { continue; } + if let Some(members) = members.as_ref() { + if !members.contains(&event.pubkey.to_hex().to_ascii_lowercase()) { + continue; + } + } let Ok(content) = serde_json::from_str::(&event.content) else { continue; }; diff --git a/desktop/src-tauri/src/mesh_llm/mod.rs b/desktop/src-tauri/src/mesh_llm/mod.rs index e206c53886a..e2466b55c9c 100644 --- a/desktop/src-tauri/src/mesh_llm/mod.rs +++ b/desktop/src-tauri/src/mesh_llm/mod.rs @@ -6,7 +6,8 @@ pub use coordinator::{start_coordinator, MeshCoordinator, KIND_BUZZ_MESH_MEMBER_ mod discovery; pub use discovery::{ - availability_from_events, mesh_status_filter, owner_ids_from_events, relay_membership_filter, + availability_from_events, availability_from_events_for_mode, mesh_status_filter, + owner_ids_from_events, relay_membership_filter, relay_mesh_mode_from_nip11, MeshRelayMode, }; pub(crate) use discovery::{ current_member_pubkeys, has_membership_snapshot, MESH_STATUS_PAGE_SIZE, diff --git a/desktop/src-tauri/src/mesh_llm/mod_tests.rs b/desktop/src-tauri/src/mesh_llm/mod_tests.rs index 557cd040fab..ff98f4adc52 100644 --- a/desktop/src-tauri/src/mesh_llm/mod_tests.rs +++ b/desktop/src-tauri/src/mesh_llm/mod_tests.rs @@ -665,6 +665,164 @@ fn owner_roster_without_membership_list_fails_closed() { assert!(super::owner_ids_from_events(&events).is_empty()); } +#[test] +fn relay_mesh_mode_reads_nip43_advertisement() { + use super::MeshRelayMode; + + // `buzz-relay` advertises NIP-43 only when membership is enforced, so the + // advertisement is the deployment-mode signal. + assert_eq!( + super::relay_mesh_mode_from_nip11(&json!({"supported_nips": [1, 11, 42, 43]})), + Ok(MeshRelayMode::ClosedMembershipEnforced) + ); + // The live open relay's actual shape (no 43). + assert_eq!( + super::relay_mesh_mode_from_nip11( + &json!({"supported_nips": [1, 2, 10, 11, 16, 17, 23, 25, 29, 33, 38, 42, 50, 56]}) + ), + Ok(MeshRelayMode::OpenNoMembership) + ); + // Missing or malformed mode evidence must fail closed at the caller. An + // ambiguous NIP-11 response is not proof that membership is disabled. + assert!(super::relay_mesh_mode_from_nip11(&json!({})).is_err()); + assert!(super::relay_mesh_mode_from_nip11(&json!({"supported_nips": "not-an-array"})).is_err()); +} + +#[test] +fn open_relay_routes_without_a_membership_snapshot() { + use super::MeshRelayMode; + + // The bug this fixes: on a relay that does not enforce membership, no + // kind:13534 snapshot ever lists ordinary users, so requiring one made + // every serving peer permanently undiscoverable — each node sat at + // "1 NODE" with an empty peer list forever. + let endpoint = test_endpoint_token(); + let events = vec![signed_reporter_target( + &"a".repeat(64), + "model-open", + &endpoint, + )]; + + let closed = super::availability_from_events_for_mode( + events.clone(), + MeshRelayMode::ClosedMembershipEnforced, + ); + assert!( + closed.serve_targets.is_empty(), + "an enforcing relay must still wait for its roster" + ); + + let open = super::availability_from_events_for_mode(events, MeshRelayMode::OpenNoMembership); + assert_eq!(open.serve_targets.len(), 1); + assert_eq!(open.serve_targets[0].endpoint_addr, endpoint); +} + +#[test] +fn open_relay_ignores_a_stray_membership_snapshot() { + use super::MeshRelayMode; + + // On an open relay a kind:13534 snapshot can still exist (members added + // out of band), but it is not a trust boundary there: admission runs + // `TrustPolicy::Off` regardless. Intersecting routing with that stray + // roster would hide peers the mesh happily admits — a hybrid state where + // admission and discovery disagree. Routing must ignore it entirely. + let reporter_secret = "a".repeat(64); + let reporter_pubkey = nostr::Keys::parse(&reporter_secret) + .unwrap() + .public_key() + .to_hex(); + let endpoint = test_endpoint_token(); + let events = vec![ + // Snapshot deliberately lists someone else, NOT the reporter. + signed_membership_event(&["f".repeat(64)]), + signed_reporter_target(&reporter_secret, "model-open", &endpoint), + ]; + + let closed = super::availability_from_events_for_mode( + events.clone(), + MeshRelayMode::ClosedMembershipEnforced, + ); + assert!( + closed.serve_targets.is_empty(), + "an enforcing relay still intersects routing with the roster" + ); + + let open = super::availability_from_events_for_mode(events, MeshRelayMode::OpenNoMembership); + assert_eq!( + open.serve_targets.len(), + 1, + "an open relay accepts a non-roster reporter's status: {reporter_pubkey} was hidden" + ); + assert_eq!(open.serve_targets[0].endpoint_addr, endpoint); +} + +#[test] +fn closed_relay_without_snapshot_never_silently_opens_up() { + use super::MeshRelayMode; + + // Regression guard for the risk this change introduces. On an enforcing + // relay a missing snapshot is a transient read gap, NOT permission to + // admit everyone: if that ever resolved to the open path, one replication + // blip would drop admission enforcement on a closed relay. + let events = vec![ + signed_reporter_target(&"b".repeat(64), "model-a", &test_endpoint_token()), + signed_reporter_target(&"c".repeat(64), "model-b", &test_endpoint_token()), + ]; + + let availability = + super::availability_from_events_for_mode(events, MeshRelayMode::ClosedMembershipEnforced); + assert!(availability.serve_targets.is_empty()); + assert_eq!( + availability.reason.as_deref(), + Some("Buzz shared compute is waiting for the current member roster") + ); +} + +#[test] +fn open_relay_still_enforces_freshness_and_endpoint_binding() { + use super::MeshRelayMode; + + // Dropping the membership intersection must not drop the other + // protections: a stale note is still unroutable, and an unsigned endpoint + // substitution is still rejected. + let stale = signed_reporter_target_at( + &"d".repeat(64), + "stale-model", + &test_endpoint_token(), + 1_000, + ); + let stale_availability = + super::availability_from_events_for_mode(vec![stale], MeshRelayMode::OpenNoMembership); + assert!( + stale_availability.serve_targets.is_empty(), + "freshness still gates routing on an open relay" + ); + + let signed_endpoint = test_endpoint_token(); + let mut forged = serde_json::from_str::( + &signed_reporter_target(&"e".repeat(64), "model-x", &signed_endpoint).content, + ) + .expect("status content is json"); + // Swap in an endpoint the owner never signed for. Assert the substitution + // is real so this test can never pass vacuously by reusing the same token. + let substituted_endpoint = test_endpoint_token(); + assert_ne!(signed_endpoint, substituted_endpoint); + forged["serveTargets"][0]["endpointAddr"] = json!(substituted_endpoint); + let keys = nostr::Keys::parse(&"e".repeat(64)).unwrap(); + let substituted = super::coordinator::build_status_report_event(forged) + .expect("status builder") + .sign_with_keys(&keys) + .expect("signs"); + let forged_availability = super::availability_from_events_for_mode( + vec![substituted], + MeshRelayMode::OpenNoMembership, + ); + assert!( + forged_availability.serve_targets.is_empty(), + "endpoint binding is still verified on an open relay" + ); +} + #[test] fn serving_usage_extracts_local_and_remote_attempts() { // Captured shape from a live serving node's status payload. The local vs