diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e65157705ad..9aa23c25d5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -202,6 +202,10 @@ jobs: run: just desktop-tauri-test env: CMAKE_POLICY_VERSION_MINIMUM: "3.5" + - name: Desktop MeshLLM compatibility tests + run: cargo test --manifest-path desktop/src-tauri/Cargo.toml --features mesh-llm --lib + env: + CMAKE_POLICY_VERSION_MINIMUM: "3.5" - name: Desktop Tauri compiled-flag verification run: just desktop-tauri-test-compiled-flags env: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 564cd74e9dd..38eaea01a73 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -77,6 +77,8 @@ env: # variable to override (e.g., for forks that want to push to their own # namespace without forking this file). IMAGE_NAME: ${{ vars.GHCR_IMAGE != '' && vars.GHCR_IMAGE || 'ghcr.io/block/buzz' }} + # Forks must also route gateway caches, digests and attestations to their registry. + PUSH_GATEWAY_IMAGE: ${{ vars.GHCR_PUSH_GATEWAY_IMAGE != '' && vars.GHCR_PUSH_GATEWAY_IMAGE || 'ghcr.io/block/buzz-push-gateway' }} jobs: build: @@ -336,7 +338,7 @@ jobs: echo echo "Verify provenance:" echo '```' - echo "gh attestation verify oci://${IMAGE_NAME}@${MERGED_DIGEST} --owner block" + echo "gh attestation verify oci://${IMAGE_NAME}@${MERGED_DIGEST} --owner ${GITHUB_REPOSITORY_OWNER}" echo '```' } >> "$GITHUB_STEP_SUMMARY" @@ -387,7 +389,7 @@ jobs: id: meta uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 with: - images: ghcr.io/block/buzz-push-gateway + images: ${{ env.PUSH_GATEWAY_IMAGE }} labels: | org.opencontainers.image.title=Buzz Push Gateway org.opencontainers.image.description=Capability-gated APNs last hop for Buzz @@ -400,9 +402,9 @@ jobs: file: ./Dockerfile.push-gateway platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} - outputs: type=image,name=ghcr.io/block/buzz-push-gateway,push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} - cache-from: type=registry,ref=ghcr.io/block/buzz-push-gateway-buildcache:${{ matrix.arch }} - cache-to: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && format('type=registry,ref=ghcr.io/block/buzz-push-gateway-buildcache:{0},mode=max,compression=zstd', matrix.arch) || '' }} + outputs: type=image,name=${{ env.PUSH_GATEWAY_IMAGE }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} + cache-from: type=registry,ref=${{ env.PUSH_GATEWAY_IMAGE }}-buildcache:${{ matrix.arch }} + cache-to: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && format('type=registry,ref={0}-buildcache:{1},mode=max,compression=zstd', env.PUSH_GATEWAY_IMAGE, matrix.arch) || '' }} - name: Export digest if: github.event_name != 'pull_request' env: @@ -447,7 +449,7 @@ jobs: id: meta uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 with: - images: ghcr.io/block/buzz-push-gateway + images: ${{ env.PUSH_GATEWAY_IMAGE }} tags: | type=ref,event=branch,enable=${{ github.event_name != 'workflow_dispatch' || inputs.version == '' }} type=sha,prefix=sha-,format=short,enable=${{ github.event_name != 'workflow_dispatch' || inputs.version == '' }} @@ -461,7 +463,7 @@ jobs: run: | set -euo pipefail tags=(); while IFS= read -r tag; do [ -n "$tag" ] && tags+=("-t" "$tag"); done <<< "$META_TAGS" - digests=(); for digest in *; do digests+=("ghcr.io/block/buzz-push-gateway@sha256:${digest}"); done + digests=(); for digest in *; do digests+=("${PUSH_GATEWAY_IMAGE}@sha256:${digest}"); done docker buildx imagetools create "${tags[@]}" "${digests[@]}" first_tag=$(echo "$META_TAGS" | head -n1) digest=$(docker buildx imagetools inspect "$first_tag" --format '{{json .Manifest}}' | jq -r '.digest') @@ -469,7 +471,7 @@ jobs: - name: Attest gateway image provenance uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 with: - subject-name: ghcr.io/block/buzz-push-gateway + subject-name: ${{ env.PUSH_GATEWAY_IMAGE }} subject-digest: ${{ steps.manifest.outputs.digest }} push-to-registry: true - name: Gateway publication summary @@ -479,7 +481,7 @@ jobs: run: | set -euo pipefail { - echo "### Published \`ghcr.io/block/buzz-push-gateway\`" + echo "### Published \`${PUSH_GATEWAY_IMAGE}\`" echo printf "**Digest:** \`%s\`\n" "$GATEWAY_DIGEST" echo @@ -490,6 +492,6 @@ jobs: echo echo 'Verify provenance before deployment:' echo "\`\`\`" - printf 'gh attestation verify oci://ghcr.io/block/buzz-push-gateway@%s --owner block\n' "$GATEWAY_DIGEST" + printf 'gh attestation verify oci://%s@%s --owner %s\n' "$PUSH_GATEWAY_IMAGE" "$GATEWAY_DIGEST" "$GITHUB_REPOSITORY_OWNER" echo "\`\`\`" } >> "$GITHUB_STEP_SUMMARY" diff --git a/Cargo.lock b/Cargo.lock index 18bbd15f0c7..92695432dc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,20 +352,21 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069" +checksum = "2c713e1f14c7b82e32ea159af1c6e2f070cfadbdf23fb2512acce9af0a26f1a2" dependencies = [ - "async-utility", "futures", "futures-util", "js-sys", "tokio", + "tokio-happy-eyeballs", "tokio-rustls", "tokio-socks", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.28.0", "url", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -398,12 +399,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -585,6 +580,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64ct" version = "1.8.3" @@ -597,6 +598,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "beef" version = "0.5.2" @@ -609,7 +616,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "serde", "unicode-normalization", ] @@ -644,6 +651,21 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +dependencies = [ + "bitcoin-internals", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" version = "0.1.4" @@ -657,7 +679,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals", + "hex-conservative 1.3.0", "serde", ] @@ -812,7 +846,7 @@ dependencies = [ "hex", "httparse", "nix 0.31.3", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "rustls", "serde", @@ -846,7 +880,7 @@ dependencies = [ "clap", "deadpool-redis", "hex", - "nostr", + "nostr 0.44.7", "rustls", "serde_json", "sqlx", @@ -904,7 +938,7 @@ version = "0.1.0" dependencies = [ "buzz-core", "hex", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "serde", "serde_json", @@ -926,7 +960,7 @@ dependencies = [ "http-body-util", "k8s-openapi", "kube", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "rustls", "serde", @@ -953,7 +987,7 @@ dependencies = [ "dirs", "hex", "infer", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "reqwest 0.13.4", "rustls", @@ -986,7 +1020,7 @@ dependencies = [ "chrono", "hex", "hmac 0.13.0", - "nostr", + "nostr 0.44.7", "percent-encoding", "rand 0.10.1", "serde", @@ -1008,7 +1042,7 @@ dependencies = [ "hex", "metrics", "metrics-util", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "serde", "serde_json", @@ -1032,7 +1066,7 @@ dependencies = [ "ignore", "image", "nix 0.31.3", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "rmcp", "rustls", @@ -1065,7 +1099,7 @@ dependencies = [ "imagesize", "infer", "mp4", - "nostr", + "nostr 0.44.7", "rust-s3", "serde", "serde_json", @@ -1104,7 +1138,7 @@ dependencies = [ "clap", "futures-util", "hex", - "nostr", + "nostr 0.44.7", "serde_json", "thiserror 2.0.18", "tokio", @@ -1133,7 +1167,7 @@ dependencies = [ "chrono", "deadpool-redis", "futures-util", - "nostr", + "nostr 0.44.7", "redis", "serde", "serde_json", @@ -1159,7 +1193,7 @@ dependencies = [ "metrics", "metrics-exporter-prometheus", "minicbor", - "nostr", + "nostr 0.44.7", "p256", "proptest", "rand 0.10.1", @@ -1214,10 +1248,10 @@ dependencies = [ "metrics-exporter-prometheus", "metrics-util", "moka", - "nostr", - "opentelemetry 0.32.0", - "opentelemetry-otlp 0.32.0", - "opentelemetry_sdk 0.32.1", + "nostr 0.44.7", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry_sdk", "postcard", "pulldown-cmark", "rand 0.10.1", @@ -1255,7 +1289,7 @@ dependencies = [ "hex", "hmac 0.13.0", "iroh", - "nostr", + "nostr 0.44.7", "postcard", "proptest", "redis", @@ -1273,7 +1307,7 @@ name = "buzz-sdk" version = "0.1.0" dependencies = [ "buzz-core", - "nostr", + "nostr 0.44.7", "serde", "serde_json", "thiserror 2.0.18", @@ -1304,7 +1338,7 @@ dependencies = [ "chrono", "futures-util", "hex", - "nostr", + "nostr 0.44.7", "rand 0.10.1", "reqwest 0.13.4", "rust-s3", @@ -1353,7 +1387,7 @@ dependencies = [ "evalexpr", "hex", "moka", - "nostr", + "nostr 0.44.7", "reqwest 0.13.4", "serde", "serde_json", @@ -1369,7 +1403,7 @@ name = "buzz-ws-client" version = "0.1.0" dependencies = [ "futures-util", - "nostr", + "nostr 0.44.7", "serde_json", "thiserror 2.0.18", "tokio", @@ -1489,9 +1523,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" @@ -1817,7 +1851,7 @@ dependencies = [ "anyhow", "buzz-sdk", "futures-util", - "nostr", + "nostr 0.44.7", "serde_json", "tokio", "tokio-tungstenite 0.29.0", @@ -2068,43 +2102,16 @@ dependencies = [ "phf", ] -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - [[package]] name = "ctor" -version = "0.6.3" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e" +checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d" dependencies = [ - "ctor-proc-macro", - "dtor", + "link-section", + "linktime-proc-macro", ] -[[package]] -name = "ctor-proc-macro" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" - [[package]] name = "ctr" version = "0.9.2" @@ -2555,21 +2562,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dtor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - [[package]] name = "dunce" version = "1.0.5" @@ -2804,6 +2796,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" @@ -2831,6 +2835,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -3139,7 +3153,7 @@ dependencies = [ "log", "rustversion", "windows-link 0.2.1", - "windows-result 0.4.1", + "windows-result 0.3.4", ] [[package]] @@ -3217,7 +3231,7 @@ name = "git-credential-nostr" version = "0.1.0" dependencies = [ "base64 0.22.1", - "nostr", + "nostr 0.44.7", "serde_json", "zeroize", ] @@ -3230,7 +3244,7 @@ dependencies = [ "chrono", "hex", "libc", - "nostr", + "nostr 0.44.7", "serde_json", "zeroize", ] @@ -3293,9 +3307,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" dependencies = [ "atomic-waker", "bytes", @@ -3310,6 +3324,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbag" version = "0.1.13" @@ -3353,6 +3376,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" @@ -3368,6 +3400,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0049b265b7f201ca9ab25475b22b47fe444060126a51abe00f77d986fc5cc52e" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -3396,45 +3438,31 @@ dependencies = [ ] [[package]] -name = "hf-hub" -version = "1.0.0-rc.1" +name = "hex-conservative" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89305dc8fe34e165eaf0eb12b6e294e12381d9df9a431bcc52a5809bab4319" +checksum = "271e0d19bcb473b6675739a2b536076b24a082316cb5199ad918edce10c599e8" dependencies = [ - "base64 0.22.1", - "bon", - "bytes", - "futures", - "globset", - "hf-xet", - "hyper", - "pathdiff", - "reqwest 0.13.4", - "serde", - "serde_json", - "sha2 0.11.0", - "thiserror 2.0.18", - "tokio", - "tokio-retry", - "tokio-util", - "tracing", - "url", + "arrayvec", ] [[package]] name = "hf-xet" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430b33fa84f92796d4d263070b6c0d3ca219df7b9a0e1853ee431029b1612bcd" +checksum = "c237ef4fb0ce1962a5117f8bd8c74454b41629826a9df17d14a1840ca18f0754" dependencies = [ + "anyhow", "async-trait", "bytes", "http", "more-asserts", "serde", + "serde_json", "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "uuid", "xet-client", @@ -3739,7 +3767,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.61.2", ] [[package]] @@ -4033,9 +4061,9 @@ dependencies = [ [[package]] name = "iroh" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971" +checksum = "329552fad4e46b5ccb4439d5635216f6289eec2c46f3fde4bc732d762694b9e5" dependencies = [ "backon", "blake3", @@ -4084,9 +4112,9 @@ dependencies = [ [[package]] name = "iroh-base" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830a582cd54410dc1aa71d4786a82c3297d7b0165accd8b6dbbb3b240b48140d" +checksum = "3ccf4cde68a02ef03c0095ab2c031c6adfe508f3bc8e7c258558a027ebe64a8e" dependencies = [ "curve25519-dalek 5.0.0-rc.0", "data-encoding", @@ -4103,9 +4131,9 @@ dependencies = [ [[package]] name = "iroh-dns" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a" +checksum = "9a41224eb0140a5c519935d4073fc6b236d174080be401f9bf7da126b2ee3aa4" dependencies = [ "arc-swap", "cfg_aliases", @@ -4154,9 +4182,9 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354" +checksum = "1b5f6f07e2c9b0c4bc82e2864be09f4aea4320165d5316e668616dbc822f428a" dependencies = [ "blake3", "bytes", @@ -4172,7 +4200,7 @@ dependencies = [ "iroh-base", "iroh-dns", "iroh-metrics", - "lru 0.18.0", + "lru", "n0-error", "n0-future", "noq", @@ -4193,7 +4221,6 @@ dependencies = [ "tokio-websockets", "tracing", "url", - "vergen-gitcl", "webpki-roots 1.0.7", "ws_stream_wasm", ] @@ -4315,16 +4342,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" @@ -4525,10 +4542,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", ] @@ -4542,6 +4560,18 @@ dependencies = [ "bitflags 2.13.0", ] +[[package]] +name = "link-section" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c29a617ce3df32c08497bdc1ab6e2376e0b17948ac166a2fbe5977c5954cd9" + +[[package]] +name = "linktime-proc-macro" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e57c38c1e860fd37c604281cdfb1dd2216977fd76a50f85ba2f388ef3219616" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4627,12 +4657,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" - [[package]] name = "lru" version = "0.18.0" @@ -4779,8 +4803,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "mesh-llm-client", @@ -4789,8 +4813,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -4800,21 +4824,18 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-llm-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "crypto_box", - "ed25519-dalek", - "hex", "httparse", "iroh", "mesh-llm-identity", @@ -4824,11 +4845,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", @@ -4837,8 +4856,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -4853,8 +4872,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -4863,24 +4882,26 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", + "chrono", "clap", "crossterm 0.28.1", + "libc", "ratatui", + "serde", "serde_json", + "tracing", + "uuid", ] [[package]] name = "mesh-llm-gpu-bench" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ - "anyhow", - "cc", - "libc", "serde", "serde_json", "tracing", @@ -4888,8 +4909,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -4897,16 +4918,45 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "mesh-llm-native-runtime", ] +[[package]] +name = "mesh-llm-hf-hub" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43088a838cf0c6715c65f65a5ac99045fd6d6e90949a8a4183b8104ab791e96b" +dependencies = [ + "base64 0.22.1", + "bon", + "bytes", + "futures", + "getrandom 0.2.17", + "globset", + "hf-xet", + "hyper", + "pathdiff", + "percent-encoding", + "reqwest 0.13.4", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "tokio-retry", + "tokio-util", + "tracing", + "url", + "wasm-bindgen-futures", +] + [[package]] name = "mesh-llm-host-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "argon2", @@ -4924,12 +4974,10 @@ dependencies = [ "flate2", "futures-util", "hex", - "hf-hub", "http", "http-body-util", "httparse", "iroh", - "json5", "keyring", "libc", "mdns-sd", @@ -4939,7 +4987,9 @@ dependencies = [ "mesh-llm-config", "mesh-llm-events", "mesh-llm-guardrails", + "mesh-llm-hf-hub", "mesh-llm-identity", + "mesh-llm-log-store", "mesh-llm-native-runtime", "mesh-llm-node", "mesh-llm-plugin", @@ -4951,6 +5001,7 @@ dependencies = [ "mesh-llm-types", "mesh-llm-ui", "mesh-mixture-of-agents", + "mesh-native-serving-plugin-host", "model-artifact", "model-hf", "model-package", @@ -4958,9 +5009,9 @@ dependencies = [ "model-resolver", "nostr-sdk", "openai-frontend", - "opentelemetry 0.31.0", - "opentelemetry-otlp 0.31.1", - "opentelemetry_sdk 0.31.0", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry_sdk", "prost 0.14.3", "rand 0.10.1", "regex-lite", @@ -4972,7 +5023,6 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml", "sha2 0.10.9", "skippy-coordinator", "skippy-ffi", @@ -4992,14 +5042,16 @@ dependencies = [ "tracing-subscriber", "url", "urlencoding", + "uuid", + "windows-sys 0.61.2", "zeroize", "zip", ] [[package]] name = "mesh-llm-identity" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "argon2", "base64 0.22.1", @@ -5018,10 +5070,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mesh-llm-log-store" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +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.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "serde", @@ -5031,8 +5101,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-types", @@ -5045,8 +5115,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5062,8 +5132,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5081,8 +5151,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "hex", @@ -5092,18 +5162,30 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "mesh-llm-release-footer" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "hex", + "sha2 0.10.9", +] + [[package]] name = "mesh-llm-routing" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ + "blake3", "iroh", + "serde", + "serde_json", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5125,8 +5207,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5140,8 +5222,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5151,8 +5233,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "chrono", @@ -5160,8 +5242,12 @@ dependencies = [ "dirs", "hex", "libc", + "libloading", "mesh-llm-build-info", "mesh-llm-gpu-bench", + "mesh-llm-native-runtime", + "mesh-llm-release-footer", + "mesh-llm-runtime-install", "reqwest 0.12.28", "semver", "serde", @@ -5174,8 +5260,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "serde", @@ -5185,13 +5271,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-mixture-of-agents" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5202,6 +5288,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "mesh-native-serving-plugin-api" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" + +[[package]] +name = "mesh-native-serving-plugin-host" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "anyhow", + "libloading", + "mesh-native-serving-plugin-api", + "skippy-server", + "skippy-tokenizer", +] + [[package]] name = "metrics" version = "0.24.6" @@ -5339,8 +5442,8 @@ dependencies = [ [[package]] name = "model-artifact" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5350,32 +5453,35 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", - "hf-hub", + "libc", + "mesh-llm-hf-hub", "model-artifact", "model-ref", + "rustls", "serde", "serde_json", "sha2 0.10.9", "tokio", + "tracing", ] [[package]] name = "model-package" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "bytes", "chrono", "futures", - "hf-hub", + "mesh-llm-hf-hub", "model-hf", "model-ref", "reqwest 0.12.28", @@ -5388,16 +5494,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "model-artifact", @@ -5592,9 +5698,9 @@ dependencies = [ "libc", "mac-addr", "ndk-context", - "netlink-packet-core", - "netlink-packet-route", - "netlink-sys", + "netlink-packet-core 0.8.1", + "netlink-packet-route 0.31.0", + "netlink-sys 0.8.8", "objc2", "objc2-core-foundation", "objc2-core-wlan", @@ -5605,6 +5711,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "netdev" +version = "0.46.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4db4135b187ddae3b6813c4f9d9ac9d5181859fa6a08b4e006c63a130974ed62" +dependencies = [ + "block2", + "dispatch2", + "dlopen2", + "ipnet", + "jni 0.21.1", + "libc", + "mac-addr", + "ndk-context", + "netlink-packet-core 0.9.0", + "netlink-packet-route 0.33.0", + "netlink-sys 0.9.0", + "objc2-core-foundation", + "objc2-system-configuration", + "once_cell", + "plist", + "windows-sys 0.61.2", +] + [[package]] name = "netlink-packet-core" version = "0.8.1" @@ -5614,6 +5744,12 @@ dependencies = [ "paste", ] +[[package]] +name = "netlink-packet-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6e2daf9a8c2e21302714706860a0e6be02e03d17294ecc66b354ea7d4059dd" + [[package]] name = "netlink-packet-route" version = "0.31.0" @@ -5623,7 +5759,20 @@ dependencies = [ "bitflags 2.13.0", "libc", "log", - "netlink-packet-core", + "netlink-packet-core 0.8.1", +] + +[[package]] +name = "netlink-packet-route" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d48ffc8b73a506e1ff0878528c72668f2bfbc3d875b6be890a96be5d0d5576" +dependencies = [ + "bitflags 2.13.0", + "libc", + "log", + "netlink-packet-core 0.9.0", + "zerocopy", ] [[package]] @@ -5635,8 +5784,8 @@ dependencies = [ "bytes", "futures", "log", - "netlink-packet-core", - "netlink-sys", + "netlink-packet-core 0.8.1", + "netlink-sys 0.8.8", "thiserror 2.0.18", ] @@ -5647,17 +5796,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" dependencies = [ "bytes", - "futures-util", + "futures-util", + "libc", + "log", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c99d38e00b420df49e940fe0826e667c3dad82ac68eb4c2bbf754c1c14a83a10" +dependencies = [ + "bytes", "libc", "log", - "tokio", ] [[package]] name = "netwatch" -version = "0.19.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d9cbe01741347ef750d743d6690603f5eed8341e679fb51c8e629337aa11976" +checksum = "39da9cad5f23aa43401f09497d3b280e51b5feba115d9ffbf38ca35d6ff95e96" dependencies = [ "atomic-waker", "bytes", @@ -5669,11 +5829,12 @@ dependencies = [ "n0-error", "n0-future", "n0-watcher", - "netdev", - "netlink-packet-core", - "netlink-packet-route", + "netdev 0.45.0", + "netdev 0.46.2", + "netlink-packet-core 0.8.1", + "netlink-packet-route 0.31.0", "netlink-proto", - "netlink-sys", + "netlink-sys 0.8.8", "noq-udp", "objc2-core-foundation", "objc2-system-configuration", @@ -5748,9 +5909,9 @@ dependencies = [ [[package]] name = "noq" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89" +checksum = "f1e6c57353d26be91a242f0ec96073066c4481a03f6be874daafb18902394515" dependencies = [ "bytes", "cfg_aliases", @@ -5770,11 +5931,12 @@ dependencies = [ [[package]] name = "noq-proto" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" +checksum = "9a46813e306c29c4f86357b6ffa39a8e1c97bb274b9a296a19a56d62e4111225" dependencies = [ "aes-gcm", + "aws-lc-rs", "bytes", "derive_more", "enum-assoc", @@ -5797,9 +5959,9 @@ dependencies = [ [[package]] name = "noq-udp" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e" +checksum = "b56d621ed2c1773b5356ab39cc68c819f8d0103f7493d8661c4a5f5f8ea55f40" dependencies = [ "cfg_aliases", "libc", @@ -5815,9 +5977,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" dependencies = [ "base64 0.22.1", - "bech32", + "bech32 0.11.1", "bip39", - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "cbc", "chacha20 0.9.1", "chacha20poly1305", @@ -5833,56 +5995,71 @@ dependencies = [ ] [[package]] -name = "nostr-database" -version = "0.44.0" +name = "nostr" +version = "0.45.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" +checksum = "67bacf4195a119bc55d576ec6b54cc103723cb46fff5f907b8bdac8fd29ac4a0" dependencies = [ - "lru 0.16.4", - "nostr", - "tokio", + "base64 0.22.1", + "bech32 0.12.0", + "bip39", + "bitcoin_hashes 1.2.0", + "cbc", + "chacha20 0.9.1", + "chacha20poly1305", + "faster-hex", + "opaquerr", + "rand 0.10.1", + "secp256k1 0.30.0", + "serde", + "serde_json", + "unicode-normalization", + "universal-time", + "url", + "zeroize", ] [[package]] -name = "nostr-gossip" -version = "0.44.0" +name = "nostr-database" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6" +checksum = "4b1fdb9fcba732e32719662afad1b267e50322dbe89e506017ec13f24361bddf" dependencies = [ - "nostr", + "nostr 0.45.4", + "opaquerr", ] [[package]] -name = "nostr-relay-pool" -version = "0.44.3" +name = "nostr-gossip" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "fa07539e52a71cb91fe0d693facaa298f03fcf9edcd66a521094e18e286e2336" dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "hex", - "lru 0.16.4", - "negentropy", - "nostr", - "nostr-database", - "tokio", - "tracing", + "nostr 0.45.4", + "opaquerr", ] [[package]] name = "nostr-sdk" -version = "0.44.1" +version = "0.45.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393" +checksum = "245f8642b10feecb0a40739a886ca1850e3be4e35dc6592b806ec437403ab9c9" dependencies = [ "async-utility", - "nostr", + "async-wsocket", + "faster-hex", + "futures", + "lru", + "negentropy", + "nostr 0.45.4", "nostr-database", "nostr-gossip", - "nostr-relay-pool", + "opaquerr", + "rand 0.10.1", "tokio", + "tokio-stream", "tracing", + "universal-time", ] [[package]] @@ -6044,6 +6221,17 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" @@ -6163,21 +6351,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opaquerr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" + [[package]] name = "openai-frontend" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "axum", "futures-core", "futures-util", + "mesh-llm-events", "mesh-llm-guardrails", "serde", "serde_json", "tokio", - "tokio-stream", "tracing", + "uuid", ] [[package]] @@ -6233,20 +6428,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 2.0.18", - "tracing", -] - [[package]] name = "opentelemetry" version = "0.32.0" @@ -6263,31 +6444,15 @@ dependencies = [ [[package]] name = "opentelemetry-http" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331" dependencies = [ "async-trait", "bytes", "http", - "opentelemetry 0.31.0", - "reqwest 0.12.28", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" -dependencies = [ - "http", - "opentelemetry 0.31.0", - "opentelemetry-http", - "opentelemetry-proto 0.31.0", - "opentelemetry_sdk 0.31.0", - "prost 0.14.3", - "reqwest 0.12.28", - "thiserror 2.0.18", + "opentelemetry", + "reqwest 0.13.4", ] [[package]] @@ -6297,10 +6462,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35" dependencies = [ "http", - "opentelemetry 0.32.0", - "opentelemetry-proto 0.32.0", - "opentelemetry_sdk 0.32.1", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", "prost 0.14.3", + "reqwest 0.13.4", "thiserror 2.0.18", "tokio", "tonic", @@ -6309,49 +6476,20 @@ dependencies = [ [[package]] name = "opentelemetry-proto" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" dependencies = [ "base64 0.22.1", "const-hex", - "opentelemetry 0.31.0", - "opentelemetry_sdk 0.31.0", + "opentelemetry", + "opentelemetry_sdk", "prost 0.14.3", "serde", - "serde_json", - "tonic", - "tonic-prost", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" -dependencies = [ - "opentelemetry 0.32.0", - "opentelemetry_sdk 0.32.1", - "prost 0.14.3", "tonic", "tonic-prost", ] -[[package]] -name = "opentelemetry_sdk" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" -dependencies = [ - "futures-channel", - "futures-executor", - "futures-util", - "opentelemetry 0.31.0", - "percent-encoding", - "rand 0.9.4", - "thiserror 2.0.18", -] - [[package]] name = "opentelemetry_sdk" version = "0.32.1" @@ -6361,7 +6499,7 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", - "opentelemetry 0.32.0", + "opentelemetry", "percent-encoding", "portable-atomic", "rand 0.9.4", @@ -6770,13 +6908,13 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plist" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" +checksum = "2896bade328c13f7042a297ea5ac5b0951f6cf989dea5f32c2fd98da398195cb" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "indexmap", - "quick-xml 0.39.4", + "quick-xml 0.42.0", "serde", "time", ] @@ -6851,9 +6989,9 @@ dependencies = [ [[package]] name = "portmapper" -version = "0.19.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3713e4977408279158444a18c1a01ac9bf2e7eaf1fbfd1a19ac9cd18d90721" +checksum = "ca97242f016e090a25330613bc2382aab65eb22f9149dbe84d8f8e711d49a530" dependencies = [ "base64 0.22.1", "bytes", @@ -7295,9 +7433,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.39.4" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" dependencies = [ "memchr", ] @@ -7530,7 +7668,7 @@ dependencies = [ "hashbrown 0.17.1", "itertools", "kasuari", - "lru 0.18.0", + "lru", "palette", "serde", "strum", @@ -7773,7 +7911,6 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", - "futures-channel", "futures-core", "futures-util", "h2", @@ -7821,6 +7958,7 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", @@ -7949,12 +8087,37 @@ dependencies = [ [[package]] name = "rpassword" -version = "5.0.1" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196" dependencies = [ "libc", - "winapi", + "rtoolbox", + "windows-sys 0.61.2", +] + +[[package]] +name = "rtoolbox" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1efe12a1469752d0e6ff5ebec0b6ef4924cc5c4c71046b0ec730040535819d" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[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]] @@ -8271,13 +8434,24 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.1", + "rand 0.8.6", + "secp256k1-sys 0.10.1", +] + [[package]] name = "secp256k1" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "rand 0.9.4", "secp256k1-sys 0.11.0", ] @@ -8575,7 +8749,6 @@ dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.17", "digest 0.10.7", - "sha2-asm", ] [[package]] @@ -8589,15 +8762,6 @@ dependencies = [ "digest 0.11.3", ] -[[package]] -name = "sha2-asm" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b845214d6175804686b2bd482bcffe96651bb2d1200742b712003504a2dac1ab" -dependencies = [ - "cc", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -8720,9 +8884,9 @@ dependencies = [ [[package]] name = "simple-dns" -version = "0.11.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a75cbde1bf934313596a004973e462f9a82caa814dcf1a5f507bdf51597eeb4" +checksum = "2b6f884fa9a8d48101774bfbd3aeb81e968dd22cffd19a372da69f183db22c1a" dependencies = [ "bitflags 2.13.0", ] @@ -8741,8 +8905,8 @@ checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "skippy-cache" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "blake3", @@ -8751,40 +8915,41 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "libloading", ] [[package]] name = "skippy-metrics" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "skippy-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "prost 0.14.3", "prost-build 0.14.3", "protoc-bin-vendored", "serde", + "skippy-tokenizer", ] [[package]] name = "skippy-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "libc", @@ -8795,10 +8960,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "skippy-scheduler" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "skippy-runtime", + "thiserror 2.0.18", +] + [[package]] name = "skippy-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "ahash", "anyhow", @@ -8809,8 +8983,11 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-llm-events", + "mesh-native-serving-plugin-api", + "model-artifact", "openai-frontend", - "opentelemetry-proto 0.31.0", + "opentelemetry-proto", "serde", "serde_json", "sha2 0.10.9", @@ -8818,16 +8995,27 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-scheduler", + "skippy-tokenizer", + "skippy-topology", "socket2", "tokio", - "tokio-stream", "tonic", + "uuid", +] + +[[package]] +name = "skippy-tokenizer" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "serde", ] [[package]] name = "skippy-topology" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -8964,7 +9152,7 @@ dependencies = [ "futures-io", "futures-util", "hashbrown 0.16.1", - "hashlink", + "hashlink 0.11.0", "indexmap", "log", "memchr", @@ -9729,6 +9917,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-happy-eyeballs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8564c32dfb6f4257f8bc6edfc178a34af97520e0b7b9815500c55eb3d092f29f" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.7.0" @@ -9797,9 +9994,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -9807,7 +10004,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite 0.28.0", "webpki-roots 0.26.11", ] @@ -9847,6 +10044,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad543404f98bfc969aeb71994105c592acfc6c43323fddcd016bb208d1c65cb" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "bytes", "futures-core", @@ -9864,6 +10062,30 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio_with_wasm" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e40fbbbd95441133fe9483f522db15dbfd26dc636164ebd8f2dd28759a6aa6" +dependencies = [ + "js-sys", + "tokio", + "tokio_with_wasm_proc", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "tokio_with_wasm_proc" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01145a2c788d6aae4cd653afec1e8332534d7d783d01897cefcafe4428de992" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -10117,7 +10339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" dependencies = [ "js-sys", - "opentelemetry 0.32.0", + "opentelemetry", "smallvec", "tracing", "tracing-core", @@ -10165,9 +10387,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -10344,6 +10566,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-time" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a939edecc3c5a7b83c02e5f6b3c31d2bc69eabcc9a87ab12c6d37ee6dbc856" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -10434,43 +10662,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" @@ -10650,15 +10841,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +checksum = "ef62a3d5f7b2411119a11b6f62570dbff91d7105e011a20fb83fbf8f5761c40f" dependencies = [ - "core-foundation 0.10.1", "jni 0.22.4", "log", "ndk-context", "objc2", + "objc2-app-kit", "objc2-foundation", "url", "web-sys", @@ -11295,8 +11486,8 @@ dependencies = [ "log", "serde", "thiserror 2.0.18", - "windows 0.62.2", - "windows-core 0.62.2", + "windows 0.61.3", + "windows-core 0.61.2", ] [[package]] @@ -11357,20 +11548,18 @@ dependencies = [ [[package]] name = "xet-client" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1e496dcbe6a09017acdfaf48e1a646735e7ff5b2a49e2c7e081cca77a59bc8" +checksum = "c3b8da8cc70aa2e3c500c0400e012df82c656ab9fca47f9f939fffc5afd89aca" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "clap", "crc32fast", "futures", "http", "hyper", - "lazy_static", "more-asserts", "rand 0.10.1", "redb", @@ -11384,8 +11573,8 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-retry", + "tokio_with_wasm", "tracing", - "tracing-subscriber", "url", "urlencoding", "web-time", @@ -11395,24 +11584,21 @@ dependencies = [ [[package]] name = "xet-core-structures" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb838aa8eb67d730af301584cf003caad407487606058292a6750711b603fbee" +checksum = "73503c223783dccc864abde22115e09d12f190448a0baf58ab2c54bc709e2f99" dependencies = [ "async-trait", "base64 0.22.1", "blake3", "bytemuck", "bytes", - "clap", "countio", - "csv", "futures", "futures-util", "getrandom 0.4.3", "heapify", "itertools", - "lazy_static", "lz4_flex", "more-asserts", "rand 0.10.1", @@ -11420,7 +11606,6 @@ dependencies = [ "safe-transmute", "serde", "static_assertions", - "tempfile", "thiserror 2.0.18", "tokio", "tokio-util", @@ -11432,32 +11617,31 @@ dependencies = [ [[package]] name = "xet-data" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fd409bef621411a9d9013798540bb8036cb2678f03ab39af89a5e88034ed8c" +checksum = "c89052ec5dec2187cad30b86af92cc24fd61c4a57a795f1ff7ff5f38d49184eb" dependencies = [ "anyhow", "async-trait", "bytes", "chrono", - "clap", "gearhash", "http", "itertools", - "lazy_static", "more-asserts", "rand 0.10.1", "serde", "serde_json", - "sha2 0.10.9", + "sha2 0.11.0", "tempfile", "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "url", "uuid", - "walkdir", + "web-time", "xet-client", "xet-core-structures", "xet-runtime", @@ -11465,9 +11649,9 @@ dependencies = [ [[package]] name = "xet-runtime" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d8f121c33866f7648b737abe70d0e2dd9c0af4ffdd7219207531d0283aa63d" +checksum = "af5c60d5eed38ab4c576f4421bae835e7bd07631fb381705605529d2015c106b" dependencies = [ "anyhow", "async-trait", @@ -11481,7 +11665,6 @@ dependencies = [ "git-version", "humantime", "konst", - "lazy_static", "libc", "more-asserts", "oneshot", @@ -11495,9 +11678,11 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-util", + "tokio_with_wasm", "tracing", "tracing-appender", "tracing-subscriber", + "web-time", "whoami", "winapi", ] diff --git a/Justfile b/Justfile index c3d755ffeb2..3491d7c3fc9 100644 --- a/Justfile +++ b/Justfile @@ -285,6 +285,8 @@ test-unit: cargo nextest run -p buzz-core -p buzz-auth --lib cargo nextest run -p buzz-voice --lib cargo nextest run -p buzz-cli + # Verify untrusted relay events before they reach agent queues. + cargo nextest run -p buzz-acp # buzz-db migrator/lint tests: pure SQL-parsing unit tests (no infra). # They guard the embedded-migrator invariant (exactly the consolidated # 0001; cutover/backfill stays an operator script, not startup state) diff --git a/benchmarks/harbor-buzz-orchestra/README.md b/benchmarks/harbor-buzz-orchestra/README.md index 0358c954e7d..7251f62ea56 100644 --- a/benchmarks/harbor-buzz-orchestra/README.md +++ b/benchmarks/harbor-buzz-orchestra/README.md @@ -127,3 +127,8 @@ uv run --extra dev ruff check . Live provisioner tests require the benchmark compose stack and opt-in environment described in `testbed/tests/test_provisioner_live.py`. + +The opt-in [cold-memory retrieval regression](tasks/memory-retrieval/README.md) +uses synthetic memories seeded before agent launch. Its setup and grading differ +from the orchestration artifact tasks above. Local fixture tests do not run a +model or establish retrieval success. diff --git a/benchmarks/harbor-buzz-orchestra/src/harbor_buzz_orchestra/container_runtime.py b/benchmarks/harbor-buzz-orchestra/src/harbor_buzz_orchestra/container_runtime.py index 149a5295a7a..c908cc722b7 100644 --- a/benchmarks/harbor-buzz-orchestra/src/harbor_buzz_orchestra/container_runtime.py +++ b/benchmarks/harbor-buzz-orchestra/src/harbor_buzz_orchestra/container_runtime.py @@ -21,6 +21,7 @@ from harbor.environments.base import BaseEnvironment from .manifest import AgentClass, ExperimentManifest +from .memory_retrieval import CONDITION, INSTRUCTION, SEEDS, score_answer from .provisioning import AgentCredential, TrialHandle from .runtime import RuntimeResult @@ -111,6 +112,11 @@ async def run( manifest: ExperimentManifest, trial: TrialHandle, ) -> RuntimeResult: + memory_trial = manifest.condition == CONDITION + if memory_trial and instruction.strip() != INSTRUCTION: + raise RuntimeLaunchError( + "memory-retrieval requires its answer-free instruction" + ) classes = self._classes_by_agent_id(manifest, trial.credentials) orchestrator = next(c for c in trial.credentials if c.role == "orchestrator") workers = [c for c in trial.credentials if c.agent_id != orchestrator.agent_id] @@ -134,6 +140,8 @@ async def run( "--name", trial.user.agent_id, ) + if memory_trial: + await self._seed_memories(orchestrator, trial) for credential in trial.credentials: await self._buzz_json( credential, @@ -170,8 +178,21 @@ async def run( await self._stop_agents(environment, agents + infra) await self._collect_logs(environment, trial_dir) + if memory_trial: + reward = score_answer(final_message["content"]) + result = await environment.exec( + f"printf '%s\\n' '{reward}' > /app/memory-retrieval-reward.txt" + ) + if result.return_code != 0: + raise RuntimeLaunchError("cannot write memory retrieval reward") + return RuntimeResult( metadata={ + **( + {"memory_retrieval_reward": score_answer(final_message["content"])} + if memory_trial + else {} + ), "completion_message_id": final_message["id"], "completion_message": final_message["content"], "agent_runtime": "in-container", @@ -518,6 +539,31 @@ async def _verify_m1_output( f"and its stripped text must equal 'Hello, world!' ({detail})" ) + async def _seed_memories( + self, credential: AgentCredential, trial: TrialHandle + ) -> None: + """Write synthetic cold values under the trial agent before it starts.""" + for slug, value in SEEDS.items(): + process = await asyncio.create_subprocess_exec( + self.buzz_cli_binary, + "mem", + "set", + slug, + "-", + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + env={ + **os.environ, + "BUZZ_RELAY_URL": self._user_relay_url(trial), + "BUZZ_PRIVATE_KEY": credential.nostr_secret_key, + "BUZZ_AUTH_TAG": credential.nostr_auth_tag, + }, + ) + await process.communicate(value.encode()) + if process.returncode != 0: + raise RuntimeLaunchError(f"memory seed failed for {slug}") + async def _send( self, credential: AgentCredential, trial: TrialHandle, content: str ) -> None: diff --git a/benchmarks/harbor-buzz-orchestra/src/harbor_buzz_orchestra/memory_retrieval.py b/benchmarks/harbor-buzz-orchestra/src/harbor_buzz_orchestra/memory_retrieval.py new file mode 100644 index 00000000000..69ef38b12d3 --- /dev/null +++ b/benchmarks/harbor-buzz-orchestra/src/harbor_buzz_orchestra/memory_retrieval.py @@ -0,0 +1,29 @@ +"""Opt-in cold-memory regression adapted from block/buzz PR #6950. + +Upstream head: 1a2833f45fb68021277070507965757337c810fd. +Uses this fork's existing DONE runtime instead of the newer native-task harness. +""" + +import re + +CONDITION = "memory-retrieval" +INSTRUCTION = ( + "How many total customers did we have in April 2024? " + "Reply with DONE: followed only by the exact customer count." +) +# Harness-side only. Never upload this module or seed values into agent prompts. +SEEDS = { + "total-customers-per-month": "Monthly average total customers: 361,250.", + "customer-value-metric": "Last month's customers: 351,340. Revenue per customer: 2,400.", + "customers-metrics-spring-24": ( + "March 2024 total customers: 325,401. " + "April 2024 active customers named John: 3,710." + ), + "new-customers-april-2024": "April 2024 new customers: 21,604.", + "total-customers-metric": "April 2024 total customers: 352,345.", +} + + +def score_answer(content: str) -> float: + """Reject distractors, approximation, dumps, and unrelated numeric prose.""" + return float(re.fullmatch(r"DONE:\s*(?:352,345|352345)\s*", content) is not None) diff --git a/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/README.md b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/README.md new file mode 100644 index 00000000000..16e0483255e --- /dev/null +++ b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/README.md @@ -0,0 +1,30 @@ +# Cold-memory retrieval regression + +Adapted from [block/buzz #6950](https://github.com/block/buzz/pull/6950), head +`1a2833f45fb68021277070507965757337c810fd` (merge +`f463e726dd173146c3c2bcbf7fdba03c9790dd3a`). The fork predates upstream's native +task harness, so this task uses the existing orchestrator/worker runtime and +`DONE:` completion protocol. It does not test upstream's threaded-reply grading. + +Run with the existing benchmark command in the harness README, setting the task +path to this directory and the manifest condition to `memory-retrieval`. Use a +neutral orchestrator persona (for example: “Answer the user's task and follow its +requested response format.”), with its actual SHA256 in the manifest. Keep the +existing required worker roster entry. Use only trial-isolated identities and +an explicitly configured test relay. Do not use production agent credentials. + +Before any agent starts, the host harness writes five synthetic cold memories +using the orchestrator's `buzz mem set -` credentials. Only one contains +the requested April total. Neither the instruction nor channel setup contains +the answer or slug. The agent must discover the data via its memory CLI. Seed +values stay host-side and are never uploaded to the task filesystem or persona. + +The requested response is exactly `DONE: 352,345` (comma-free is accepted). +The runtime observes the orchestrator's own completion, grades it, and writes +the verifier reward. Wrong metrics, guesses, approximations, memory dumps, and +unrelated prose earn zero. The same grade is recorded as +`memory_retrieval_reward` in runtime metadata. A timed-out run is not a pass. +This checks observable retrieval behavior, not a tool-call trace. The fixed +synthetic number is suitable for regression use, not a contamination-resistant +leaderboard. Local Python tests validate fixtures and grading only. They do not +establish model retrieval success or improvement over the old prompt. diff --git a/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/environment/Dockerfile b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/environment/Dockerfile new file mode 100644 index 00000000000..20038c62ee9 --- /dev/null +++ b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/environment/Dockerfile @@ -0,0 +1,2 @@ +FROM python:3.12-slim-bookworm +WORKDIR /app diff --git a/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/instruction.md b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/instruction.md new file mode 100644 index 00000000000..8e5086f5f29 --- /dev/null +++ b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/instruction.md @@ -0,0 +1 @@ +How many total customers did we have in April 2024? Reply with DONE: followed only by the exact customer count. diff --git a/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/task.toml b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/task.toml new file mode 100644 index 00000000000..b1622f415ff --- /dev/null +++ b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/task.toml @@ -0,0 +1,14 @@ +schema_version = "1.3" +[task] +name = "memory-retrieval" +description = "Retrieve an exact metric from trial-seeded cold memory." +authors = [{ name = "Buzz" }] +[agent] +timeout_sec = 300.0 +[verifier] +timeout_sec = 30.0 +[environment] +network_mode = "public" +cpus = 1 +memory_mb = 1024 +storage_mb = 1024 diff --git a/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/tests/test.sh b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/tests/test.sh new file mode 100755 index 00000000000..e384eefddc0 --- /dev/null +++ b/benchmarks/harbor-buzz-orchestra/tasks/memory-retrieval/tests/test.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -eu +mkdir -p /logs/verifier +# Runtime writes the grade after observing the orchestrator's final answer. +# Missing runtime evidence fails closed. +python3 - <<'PYCODE' +from pathlib import Path +p = Path('/app/memory-retrieval-reward.txt') +reward = p.read_text().strip() if p.is_file() else '0.0' +Path('/logs/verifier/reward.txt').write_text('1.0' if reward == '1.0' else '0.0') +PYCODE diff --git a/benchmarks/harbor-buzz-orchestra/tests/test_container_runtime.py b/benchmarks/harbor-buzz-orchestra/tests/test_container_runtime.py index ebf0eb4b5d2..4a6dc9ea528 100644 --- a/benchmarks/harbor-buzz-orchestra/tests/test_container_runtime.py +++ b/benchmarks/harbor-buzz-orchestra/tests/test_container_runtime.py @@ -432,3 +432,111 @@ async def test_stop_agents_sweeps_the_uploaded_stack(tmp_path): sweeps = [cmd for cmd, _ in environment.commands if REMOTE_BIN in cmd] assert len(sweeps) == 2 assert "kill -TERM" in sweeps[0] and "kill -KILL" in sweeps[1] + + +async def test_memory_seeding_uses_agent_identity_and_stdin(tmp_path, monkeypatch): + from harbor_buzz_orchestra.memory_retrieval import SEEDS + + calls = [] + + class Process: + returncode = 0 + + async def communicate(self, data): + calls[-1][2] = data + return b"", b"" + + async def spawn(*args, **kwargs): + calls.append([args, kwargs, None]) + return Process() + + monkeypatch.setattr("asyncio.create_subprocess_exec", spawn) + agent = credential("orch-1", "orchestrator", "orch-model") + trial = trial_handle((agent,), user_relay_url="http://test-relay") + await runtime(tmp_path)._seed_memories(agent, trial) + assert len(calls) == len(SEEDS) + for (args, kwargs, data), (slug, value) in zip(calls, SEEDS.items()): + assert args == ("buzz", "mem", "set", slug, "-") + assert data == value.encode() + assert kwargs["env"]["BUZZ_PRIVATE_KEY"] == agent.nostr_secret_key + assert kwargs["env"]["BUZZ_RELAY_URL"] == "http://test-relay" + + +@pytest.mark.parametrize( + ("answer", "reward"), + [("DONE: 352,345", 1.0), ("DONE: unrelated response", 0.0)], +) +async def test_memory_trial_seeds_before_launch_and_keeps_answer_out_of_chat( + tmp_path, monkeypatch, answer, reward +): + from harbor_buzz_orchestra.memory_retrieval import CONDITION, INSTRUCTION + + manifest = write_manifest(tmp_path).model_copy(update={"condition": CONDITION}) + credentials = ( + credential("orch-1", "orchestrator", "orch-model"), + credential("worker-1", "worker", "worker-model"), + ) + rt = runtime(tmp_path) + actions = [] + + async def noop(*args, **kwargs): + pass + + async def seed(*args): + actions.append("seed") + + async def launch(**kwargs): + actions.append("launch") + + async def send(identity, trial, content): + actions.append(content) + assert "352345" not in content.replace(",", "") + + async def done(*args): + return {"id": "answer", "content": answer} + + for method in ( + "_install_stack", + "_start_forwarder", + "_buzz_json", + "_wait_for_agents_ready", + "_stop_agents", + "_collect_logs", + ): + monkeypatch.setattr(rt, method, noop) + monkeypatch.setattr(rt, "_seed_memories", seed) + monkeypatch.setattr(rt, "_launch_agent", launch) + monkeypatch.setattr(rt, "_send", send) + monkeypatch.setattr(rt, "_wait_for_done", done) + result = await rt.run( + instruction=INSTRUCTION, + environment=Environment(), + manifest=manifest, + trial=trial_handle(credentials), + ) + assert actions == ["seed", "launch", "launch", f"@orch-1 {INSTRUCTION}"] + assert result.metadata["memory_retrieval_reward"] == reward + + with pytest.raises(RuntimeLaunchError, match="answer-free"): + await rt.run( + instruction="The answer is 352,345", + environment=Environment(), + manifest=manifest, + trial=trial_handle(credentials), + ) + + +async def test_memory_seed_failure_aborts(tmp_path, monkeypatch): + class Process: + returncode = 1 + + async def communicate(self, data): + return b"", b"failure" + + async def spawn(*args, **kwargs): + return Process() + + monkeypatch.setattr("asyncio.create_subprocess_exec", spawn) + agent = credential("orch-1", "orchestrator", "orch-model") + with pytest.raises(RuntimeLaunchError, match="memory seed failed"): + await runtime(tmp_path)._seed_memories(agent, trial_handle((agent,))) diff --git a/benchmarks/harbor-buzz-orchestra/tests/test_memory_retrieval.py b/benchmarks/harbor-buzz-orchestra/tests/test_memory_retrieval.py new file mode 100644 index 00000000000..c8e5c167700 --- /dev/null +++ b/benchmarks/harbor-buzz-orchestra/tests/test_memory_retrieval.py @@ -0,0 +1,36 @@ +"""Local fixture/grader tests, not a model retrieval evaluation.""" + +from harbor_buzz_orchestra.memory_retrieval import INSTRUCTION, SEEDS, score_answer + + +def test_answer_is_only_in_one_cold_seed(): + assert "352345" not in INSTRUCTION.replace(",", "") + assert sum("352,345" in value for value in SEEDS.values()) == 1 + assert "core" not in SEEDS + assert all(slug not in INSTRUCTION for slug in SEEDS) + + +def test_grader_accepts_exact_count_only(): + assert score_answer("DONE: 352,345") == 1.0 + assert score_answer("DONE: 352345") == 1.0 + for answer in ( + "DONE: I could not find it", + "DONE: 325,401", + "DONE: about 352,345", + "DONE: revenue was 352,345", + "DONE: 352,345 or 361,250", + "352,345", + ): + assert score_answer(answer) == 0.0 + + +def test_task_instruction_and_base_prompt_do_not_leak_answer(): + from pathlib import Path + + root = Path(__file__).resolve().parents[1] + instruction = (root / "tasks/memory-retrieval/instruction.md").read_text() + prompt = (root.parents[1] / "crates/buzz-acp/src/base_prompt.md").read_text() + assert instruction.strip() == INSTRUCTION + assert "352345" not in prompt.replace(",", "") + assert "buzz mem ls" in prompt + assert "buzz mem get " in prompt diff --git a/crates/buzz-acp/README.md b/crates/buzz-acp/README.md index e6164b02dd3..67b694e94e9 100644 --- a/crates/buzz-acp/README.md +++ b/crates/buzz-acp/README.md @@ -256,6 +256,10 @@ Forum event kinds: 4. **Prompting** — When events are pending and no prompt is in flight for that channel, drains all queued events for the oldest channel into a single batched prompt via ACP `session/prompt`. 5. **Agent response** — The agent processes the prompt and uses the Buzz CLI (`send_message`, `get_messages`, etc.) to interact with Buzz. 6. **Recovery** — If the agent crashes, the harness respawns it. If the relay disconnects, the harness reconnects with a `since` filter to avoid missing events. + If the inbound queue overflows, the harness attempts replay for affected + subscriptions when capacity and relay quota permit, with at least five seconds + between attempts. Recovery depends on available relay history and the consumer + making progress; complete delivery is not guaranteed. Each channel has at most one prompt in flight. Multiple channels can be processed concurrently when agents > 1. diff --git a/crates/buzz-acp/src/base_prompt.md b/crates/buzz-acp/src/base_prompt.md index 1d85221f113..99698480eb7 100644 --- a/crates/buzz-acp/src/base_prompt.md +++ b/crates/buzz-acp/src/base_prompt.md @@ -26,6 +26,7 @@ The `buzz` CLI is your primary interface. Auth env vars: `BUZZ_RELAY_URL`, `BUZZ | `buzz issues` | `create`, `get`, `list`, `status` | | `buzz pr` | `open`, `update`, `get`, `list`, `status` | | `buzz upload` | `file` | +| `buzz mem` | `ls`, `get`, `set`, `hash`, `patch`, `rm` | Run `buzz --help` or `buzz --help` for full usage. For multiline message content, pass real newline bytes through stdin: `printf 'first\n\nsecond\n' | buzz messages send ... --content -`. Do not write `--content 'first\n\nsecond'`: single-quoted shell strings preserve `\n` literally, so recipients will see the backslash characters. `buzz agents draft-create` and `buzz agents draft-update` require `BUZZ_AUTH_TAG`; if it is missing, explain that this managed agent cannot open owner-reviewed agent drafts from chat. @@ -115,7 +116,8 @@ These paths are relative to your working directory — keep exploration there. N Your `core` memory is auto-injected into your context every turn — it holds identity, durable rules, and goals across sessions. - **Keep `core` small.** A line earns a permanent slot only if it matters across most sessions or prevents a sharp repeat mistake. Treat the 65,535-byte hard limit as a wall to stay far from, not a budget to fill — aim to keep `core` under ~10 KB (roughly your healthy baseline). -- **Durable detail goes to a cold `mem/` slug, not `core`.** Long-lived findings that don't need to be in front of you every turn belong in a `mem/` slug you read on demand — not appended to `core`. +- **Retrieve cold memory when context is missing.** Before guessing or asking the user to repeat a remembered fact, use `buzz mem ls` to discover relevant slugs and `buzz mem get ` to read them. Match the requested subject, date, and metric, then answer from the relevant memory. Cold values are not automatically injected. +- **Durable detail goes to cold memory, not `core`.** Store long-lived detail with `buzz mem set ` (or `buzz mem set -` for stdin) and read it on demand with `buzz mem get `. Follow the canonical memory hierarchy and authority rules in your workspace and `core`. - **Evict completed work.** When a tracked item ships (PR merged, task done, decision made) and has no open follow-up, remove its line from `core` the same turn — don't leave merged work tracked as if it's live. The detail already lives in its cold `mem/` slug if you need it later. - **Treat `core` as load-bearing.** Follow it unless newer explicit user instructions override it. - Cite sources with paths, links, or command outputs. No unsupported claims. diff --git a/crates/buzz-acp/src/relay.rs b/crates/buzz-acp/src/relay.rs index aea5cee0770..c85fc1fe03a 100644 --- a/crates/buzz-acp/src/relay.rs +++ b/crates/buzz-acp/src/relay.rs @@ -1011,10 +1011,8 @@ struct BgState { /// On reconnect resubscribe, `since` = min(last_seen, channel_dropped_since). /// Cleared per-channel after a successful resubscribe. channel_dropped_since: HashMap, - /// Set by the backpressure handler when the event channel is full. - /// The main loop checks this flag and triggers a proactive resubscribe - /// (without waiting for a disconnect) so dropped events are replayed. - proactive_resubscribe_needed: bool, + /// Rate/fairness bookkeeping only; replay cursors retain baseline semantics. + recovery: recovery::RecoverySchedule, /// Unix timestamp captured just before the relay connection was established. /// Used as the floor `since` for membership notification replay so events /// predating this session are never re-delivered. @@ -1084,7 +1082,7 @@ impl BgState { membership_sub_active: false, observer_control_sub_active: false, channel_dropped_since: HashMap::new(), - proactive_resubscribe_needed: false, + recovery: recovery::RecoverySchedule::default(), startup_watermark: None, subscribe_since: HashMap::new(), rate_limit_gate: None, @@ -1144,6 +1142,9 @@ impl BgState { /// Prevents stale replay on re-subscribe and avoids unbounded state growth /// for channels that are removed and never re-added. fn clear_channel_state(&mut self, channel_id: &Uuid) { + self.recovery + .last_attempt + .remove(&channel_sub_id(*channel_id)); self.last_seen.remove(channel_id); self.subscribe_since.remove(channel_id); self.channel_dropped_since.remove(channel_id); @@ -1638,82 +1639,6 @@ async fn run_background_task( let mut drain_pacing_next: Option = None; loop { - if state.proactive_resubscribe_needed { - state.proactive_resubscribe_needed = false; - info!("proactive resubscribe triggered by backpressure event loss"); - // Proactive resubscribe runs on the EXISTING socket — do NOT clear the - // rate-limit gate or pending queues. - match resubscribe_after_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &agent_pubkey_hex, - false, // existing socket — preserve gate state - ) - .await - { - ResubscribeResult::Ok => {} - ResubscribeResult::Shutdown => return, - ResubscribeResult::RetryConnection => { - warn!("proactive resubscribe had failures — triggering reconnect"); - let _ = event_tx.try_send(None); - match try_autonomous_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &keys, - &relay_url, - &agent_pubkey_hex, - &event_tx, - &observer_control_tx, - auth_tag.as_ref(), - ) - .await - { - ReconnectOutcome::Ok => { - if matches!( - drain_post_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &agent_pubkey_hex - ) - .await, - ReconnectOutcome::Shutdown - ) { - return; - } - } - ReconnectOutcome::Shutdown => return, - ReconnectOutcome::Failed => { - if matches!( - wait_for_reconnect( - &mut ws, - &mut cmd_rx, - &mut state, - &keys, - &relay_url, - &agent_pubkey_hex, - &event_tx, - &observer_control_tx, - true, - auth_tag.as_ref(), - ) - .await, - ReconnectOutcome::Shutdown - ) { - return; - } - } - } - ping_sent = false; - last_pong = Instant::now(); - connected_since = Instant::now(); - stable_logged = false; - } - } - } - // Drain pending subs, one REQ per pacing tick within the relay's // admission window. let drain_window_open = drain_pacing_next.is_none_or(|t| tokio::time::Instant::now() >= t); @@ -1794,7 +1719,11 @@ async fn run_background_task( } } + let recovery_at = recovery::ready_at(&mut state); tokio::select! { + _ = recovery::ready(&event_tx, recovery_at) => { + recovery::recover_one(&mut ws, &mut state, &event_tx, &agent_pubkey_hex).await; + } raw = ws.next() => { // Determine if the socket is lost. let socket_lost = match raw { @@ -2079,6 +2008,36 @@ async fn handle_ws_message( subscription_id, event, } => { + // Relay and storage responses are untrusted. Verify before + // any event field can affect routing, replay state, or the + // harness queues. + let event_id = event.id.to_hex(); + let event = match tokio::task::spawn_blocking(move || { + buzz_core::verify_event(&event).map(|()| event) + }) + .await + { + Ok(Ok(event)) => event, + Ok(Err(error)) => { + warn!( + subscription_id, + event_id, + error = %error, + "relay event failed NIP-01 verification — dropping" + ); + return true; + } + Err(error) => { + warn!( + subscription_id, + event_id, + error = %error, + "relay event verification task failed — dropping" + ); + return true; + } + }; + if subscription_id == OBSERVER_CONTROL_SUB_ID { match observer_control_tx.try_send(*event) { Ok(()) => {} @@ -2139,12 +2098,10 @@ async fn handle_ws_message( // replay starts early enough to re-deliver it. state.membership_dropped_since = Some(state.membership_dropped_since.map_or(ts, |d| d.min(ts))); - // Proactively trigger resubscribe without waiting for a disconnect. - state.proactive_resubscribe_needed = true; warn!( channel_id = %channel_uuid, ts, - "membership notification dropped (backpressure) — proactive resubscribe queued" + "membership notification dropped (backpressure) — targeted recovery pending" ); } Err(mpsc::error::TrySendError::Closed(_)) => return false, @@ -2180,12 +2137,10 @@ async fn handle_ws_message( .entry(channel_id) .and_modify(|d| *d = (*d).min(ts)) .or_insert(ts); - // Proactively trigger resubscribe without waiting for a disconnect. - state.proactive_resubscribe_needed = true; warn!( channel_id = %channel_id, ts, - "event channel full — dropping event for channel {channel_id} — proactive resubscribe queued" + "event channel full — dropping event for channel {channel_id} — targeted recovery pending" ); } Err(mpsc::error::TrySendError::Closed(_)) => { @@ -4004,6 +3959,11 @@ async fn wait_for_any_ok( } } +mod recovery; + +#[cfg(test)] +mod recovery_tests; + #[cfg(test)] mod tests { use super::*; @@ -4350,7 +4310,7 @@ mod tests { .expect("signing should succeed") } - async fn test_ws_pair() -> (WsStream, WebSocketStream) { + pub(super) async fn test_ws_pair() -> (WsStream, WebSocketStream) { let listener = tokio::net::TcpListener::bind("127.0.0.1:0") .await .expect("bind test websocket"); @@ -4367,7 +4327,7 @@ mod tests { (client, server.await.expect("join test websocket server")) } - async fn next_test_frame( + pub(super) async fn next_test_frame( server: &mut WebSocketStream, ) -> serde_json::Value { let message = timeout(Duration::from_secs(1), server.next()) @@ -4379,14 +4339,245 @@ mod tests { .expect("parse test websocket frame") } - fn test_channel_filter() -> ChannelFilter { + fn make_signed_channel_event(keys: &Keys, content: &str, created_at_secs: u64) -> Event { + EventBuilder::new(Kind::Custom(9), content) + .tags([]) + .custom_created_at(nostr::Timestamp::from(created_at_secs)) + .sign_with_keys(keys) + .expect("sign channel event") + } + + fn replace_event_field(event: &Event, field: &str, replacement: Value) -> Event { + let mut value = serde_json::to_value(event).expect("serialize event"); + value[field] = replacement; + serde_json::from_value(value).expect("deserialize tampered event") + } + + fn recompute_event_id(event: &Event) -> Event { + let id = nostr::EventId::new( + &event.pubkey, + &event.created_at, + &event.kind, + &event.tags, + &event.content, + ); + replace_event_field(event, "id", json!(id.to_hex())) + } + + async fn handle_test_relay_event( + ws: &mut WsStream, + event_tx: &mpsc::Sender>, + observer_control_tx: &mpsc::Sender, + state: &mut BgState, + subscription_id: &str, + event: &Event, + ) -> bool { + let keys = Keys::generate(); + let agent_pubkey_hex = keys.public_key().to_hex(); + let text = serde_json::to_string(&json!(["EVENT", subscription_id, event])) + .expect("serialize relay frame"); + handle_ws_message( + Message::Text(text.into()), + ws, + event_tx, + observer_control_tx, + state, + &keys, + "wss://relay.example.com", + &agent_pubkey_hex, + None, + ) + .await + } + + #[tokio::test] + async fn verified_channel_event_is_recorded_and_forwarded() { + let (mut client, _server) = test_ws_pair().await; + let (event_tx, mut event_rx) = mpsc::channel(4); + let (observer_control_tx, mut observer_control_rx) = mpsc::channel(4); + let mut state = BgState::new(); + let channel_id = Uuid::new_v4(); + let event = make_signed_channel_event(&Keys::generate(), "hello", 2_000); + + assert!( + handle_test_relay_event( + &mut client, + &event_tx, + &observer_control_tx, + &mut state, + &channel_sub_id(channel_id), + &event, + ) + .await + ); + + let received = event_rx.try_recv().expect("verified event was forwarded"); + let received = received.expect("event channel should not contain shutdown marker"); + assert_eq!(received.channel_id, channel_id); + assert_eq!(received.event.id, event.id); + assert_eq!(state.last_seen.get(&channel_id), Some(&2_000)); + assert!(state.seen_ids.contains(&event.id.to_hex())); + assert!(matches!( + observer_control_rx.try_recv(), + Err(mpsc::error::TryRecvError::Empty) + )); + } + + #[tokio::test] + async fn tampered_channel_events_are_dropped_before_state_or_queue_changes() { + let (mut client, _server) = test_ws_pair().await; + let (event_tx, mut event_rx) = mpsc::channel(8); + let (observer_control_tx, _observer_control_rx) = mpsc::channel(4); + let mut state = BgState::new(); + let channel_id = Uuid::new_v4(); + let owner_event = make_signed_channel_event(&Keys::generate(), "status", 2_000); + let other_event = make_signed_channel_event(&Keys::generate(), "other", 3_000); + let other = serde_json::to_value(&other_event).expect("serialize other event"); + let owner_command = recompute_event_id(&replace_event_field( + &owner_event, + "content", + json!("!shutdown"), + )); + + let cases = [ + ( + "changed content", + replace_event_field(&owner_event, "content", json!("tampered")), + ), + ("forged owner command with a matching id", owner_command), + ( + "changed event id", + replace_event_field(&owner_event, "id", other["id"].clone()), + ), + ( + "changed signature", + replace_event_field(&owner_event, "sig", other["sig"].clone()), + ), + ( + "changed author pubkey", + replace_event_field(&owner_event, "pubkey", other["pubkey"].clone()), + ), + ( + "changed tags", + replace_event_field( + &owner_event, + "tags", + json!([["h", Uuid::new_v4().to_string()]]), + ), + ), + ( + "changed timestamp", + replace_event_field(&owner_event, "created_at", json!(4_000)), + ), + ]; + + for (case, event) in cases { + assert!( + handle_test_relay_event( + &mut client, + &event_tx, + &observer_control_tx, + &mut state, + &channel_sub_id(channel_id), + &event, + ) + .await, + "{case} should not close the connection" + ); + assert!( + matches!(event_rx.try_recv(), Err(mpsc::error::TryRecvError::Empty)), + "{case} reached the harness event queue" + ); + } + + assert!(state.last_seen.is_empty()); + assert!(state.seen_ids.current.is_empty()); + assert!(state.seen_ids.previous.is_empty()); + } + + #[tokio::test] + async fn forged_membership_notification_is_dropped_before_state_or_queue_changes() { + let (mut client, _server) = test_ws_pair().await; + let (event_tx, mut event_rx) = mpsc::channel(4); + let (observer_control_tx, _observer_control_rx) = mpsc::channel(4); + let mut state = BgState::new(); + let attacker_keys = Keys::generate(); + let owner_keys = Keys::generate(); + let channel_id = Uuid::new_v4(); + let event = EventBuilder::new( + Kind::Custom(KIND_MEMBER_ADDED_NOTIFICATION as u16), + "membership changed", + ) + .tags([Tag::parse(["h", &channel_id.to_string()]).expect("h tag")]) + .custom_created_at(nostr::Timestamp::from(2_000)) + .sign_with_keys(&attacker_keys) + .expect("sign membership event"); + let forged = recompute_event_id(&replace_event_field( + &event, + "pubkey", + json!(owner_keys.public_key().to_hex()), + )); + + assert!( + handle_test_relay_event( + &mut client, + &event_tx, + &observer_control_tx, + &mut state, + MEMBERSHIP_NOTIF_SUB_ID, + &forged, + ) + .await + ); + + assert!(matches!( + event_rx.try_recv(), + Err(mpsc::error::TryRecvError::Empty) + )); + assert_eq!(state.membership_last_seen, None); + assert!(state.seen_ids.current.is_empty()); + assert!(state.seen_ids.previous.is_empty()); + } + + #[tokio::test] + async fn forged_observer_control_is_dropped_before_control_queue() { + let (mut client, _server) = test_ws_pair().await; + let (event_tx, _event_rx) = mpsc::channel(4); + let (observer_control_tx, mut observer_control_rx) = mpsc::channel(4); + let mut state = BgState::new(); + let event = make_signed_channel_event(&Keys::generate(), "control", 2_000); + let forged = recompute_event_id(&replace_event_field( + &event, + "content", + json!("tampered control"), + )); + + assert!( + handle_test_relay_event( + &mut client, + &event_tx, + &observer_control_tx, + &mut state, + OBSERVER_CONTROL_SUB_ID, + &forged, + ) + .await + ); + + assert!(matches!( + observer_control_rx.try_recv(), + Err(mpsc::error::TryRecvError::Empty) + )); + } + + pub(super) fn test_channel_filter() -> ChannelFilter { ChannelFilter { kinds: Some(vec![9]), require_mention: false, } } - fn seed_test_subscription(state: &mut BgState, channel_id: Uuid) { + pub(super) fn seed_test_subscription(state: &mut BgState, channel_id: Uuid) { apply_command_to_state( state, RelayCommand::Subscribe { diff --git a/crates/buzz-acp/src/relay/recovery.rs b/crates/buzz-acp/src/relay/recovery.rs new file mode 100644 index 00000000000..62da119a4be --- /dev/null +++ b/crates/buzz-acp/src/relay/recovery.rs @@ -0,0 +1,132 @@ +//! Overflow recovery is an attempted replay, not an EOSE/consumer receipt. +//! Keep the existing IDs and cursor retirement rules; bound when work is sent. +use super::*; + +pub(super) const RECOVERY_INTERVAL: Duration = Duration::from_secs(5); + +#[derive(Default)] +pub(super) struct RecoverySchedule { + next_attempt: Option, + pub(super) last_attempt: HashMap, +} + +/// Attempt at most one affected subscription, with space for replay to arrive. +/// Failed writes retain the loss cursor and are paced too. No EOSE is interpreted +/// as completion: overlapping requests keep their existing stable wire IDs. +pub(super) async fn recover_one( + ws: &mut WsStream, + state: &mut BgState, + event_tx: &mpsc::Sender>, + agent_pubkey_hex: &str, +) { + let now = tokio::time::Instant::now(); + if event_tx.is_closed() + || event_tx.capacity() < event_tx.max_capacity().div_ceil(2) + || state.recovery.next_attempt.is_some_and(|next| now < next) + || state.check_rate_gate().is_some() + { + return; + } + + let channel = next_channel(state); + let Some(channel) = channel else { return }; + let sub = channel.map_or_else(|| MEMBERSHIP_NOTIF_SUB_ID.to_owned(), channel_sub_id); + state.recovery.last_attempt.insert(sub.clone(), now); + info!(subscription = sub, "attempting targeted overflow replay"); + + if let Some(ch) = channel { + if let Some(filter) = state.active_filters.get(&ch).cloned() { + let since = state.channel_since(&ch); + if send_subscribe(ws, state, ch, agent_pubkey_hex, since, &filter).await { + // Baseline retirement point: REQ write, NOT proven delivery. + // New overflow after this attempt creates another pending cursor. + state.channel_dropped_since.remove(&ch); + } + } + } else { + let since = match (state.membership_dropped_since, state.membership_last_seen) { + (Some(d), Some(l)) => Some(d.min(l)), + (Some(d), None) => Some(d), + (None, Some(l)) => Some(l), + (None, None) => state.startup_watermark, + }; + if send_membership_subscribe(ws, agent_pubkey_hex, since).await { + state.membership_dropped_since = None; + } + } + // Pace from the end of a potentially backpressured write. No catch-up burst. + // The existing bounded write timeout and read/ping owner detect socket loss. + state.recovery.next_attempt = Some(tokio::time::Instant::now() + RECOVERY_INTERVAL); +} + +/// No timer or capacity waiter when another authority owns all pending loss. +/// Only actual attempts advance the cooldown; closed gates wake at expiry. +pub(super) fn ready_at(state: &mut BgState) -> Option { + next_channel(state)?; + Some( + state + .recovery + .next_attempt + .unwrap_or_else(tokio::time::Instant::now) + .max( + state + .check_rate_gate() + .unwrap_or_else(tokio::time::Instant::now), + ), + ) +} + +/// Select-local readiness, not a send or a reservation carried across reads. +/// The socket task is the sole producer. `select!` drops this future (including +/// partial permits) BEFORE handling another frame/command, so live try_send +/// never competes with a recovery reservation. Receives only add capacity. +/// Keep this future inside select!: awaiting it alone would block the reader; +/// persisting it across iterations would steal capacity from live delivery. +pub(super) async fn ready( + event_tx: &mpsc::Sender>, + at: Option, +) { + if let Some(at) = at { + if tokio::time::Instant::now() < at { + tokio::time::sleep_until(at).await; + } + // Use the channel's own race-free capacity wake, not periodic samples. + // Return ALL permits before recover_one rechecks capacity and intent. + if let Ok(permits) = event_tx + .reserve_many(event_tx.max_capacity().div_ceil(2)) + .await + { + drop(permits); + return; + } + } + // No loss or a closed receiver: no immediate-ready/error wake loop. + std::future::pending::<()>().await; +} + +fn next_channel(state: &BgState) -> Option> { + // One record per active intent, not per loss or per request generation. + // Least recently attempted prevents a repeatedly overflowing channel from + // starving other channels or membership. Missing filters fail closed. + state + .channel_dropped_since + .keys() + .filter(|ch| { + state.active_subscriptions.contains_key(ch) + && state.active_filters.contains_key(ch) + && !state.rate_limited_pending.contains_key(ch) + && !state.resubscribe_retry.contains(ch) + }) + .copied() + .map(Some) + .chain( + (state.membership_sub_active + && state.membership_dropped_since.is_some() + && !state.membership_resub_needed) + .then_some(None), + ) + .min_by_key(|ch| { + let sub = ch.map_or_else(|| MEMBERSHIP_NOTIF_SUB_ID.to_owned(), channel_sub_id); + (state.recovery.last_attempt.get(&sub).copied(), sub) + }) +} diff --git a/crates/buzz-acp/src/relay/recovery_tests.rs b/crates/buzz-acp/src/relay/recovery_tests.rs new file mode 100644 index 00000000000..4e51a0c6147 --- /dev/null +++ b/crates/buzz-acp/src/relay/recovery_tests.rs @@ -0,0 +1,388 @@ +//! Bounded synthetic WebSocket fixtures; no relay service, proxy or real agent. +use super::tests::{next_test_frame, seed_test_subscription, test_channel_filter, test_ws_pair}; +use super::*; + +fn fixture_event(channel: Uuid, n: u64, kind: u16) -> Event { + let keys = + Keys::parse("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); + EventBuilder::new(Kind::Custom(kind), format!("synthetic-{n}")) + .tags([Tag::parse(["h", &channel.to_string()]).unwrap()]) + .custom_created_at(nostr::Timestamp::from(1_000 + n)) + .sign_with_keys(&keys) + .unwrap() +} + +async fn dispatch( + client: &mut WsStream, + state: &mut BgState, + tx: &mpsc::Sender>, + frame: Value, +) { + let (control_tx, _control_rx) = mpsc::channel(1); + assert!( + handle_ws_message( + Message::Text(frame.to_string().into()), + client, + tx, + &control_tx, + state, + &Keys::generate(), + "ws://127.0.0.1:1", + "synthetic-agent", + None, + ) + .await + ); +} + +#[tokio::test] +async fn repeated_overflow_recovers_only_affected_channel_after_capacity() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let channels: Vec<_> = (0..18).map(|_| Uuid::new_v4()).collect(); + for ch in &channels { + seed_test_subscription(&mut state, *ch); + } + let ch = channels[0]; + let sub = channel_sub_id(ch); + let (tx, mut rx) = mpsc::channel(256); + // Relay history is newest-first; the oldest dropped event must survive + // a watermark already advanced by much newer successfully-enqueued events. + let events: Vec<_> = (0..320).rev().map(|n| fixture_event(ch, n, 9)).collect(); + for event in &events { + dispatch(&mut client, &mut state, &tx, json!(["EVENT", sub, event])).await; + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + } + assert_eq!(rx.len(), 256); + assert_eq!(state.channel_dropped_since[&ch], 1_000); + assert!(timeout(Duration::from_millis(30), server.next()) + .await + .is_err()); + for event in &events[..256] { + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, event.id); + } + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + let req = next_test_frame(&mut server).await; + assert_eq!(req[0], "REQ"); + assert_eq!(req[1], sub); + assert_eq!(req[2]["#h"], json!([ch.to_string()])); + assert_eq!(req[2]["kinds"], json!([9])); + assert_eq!(req[2]["since"], 995); + // Concurrent timer ticks / duplicate arrivals cannot replace the replay. + for _ in 0..40 { + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + } + assert!(timeout(Duration::from_millis(30), server.next()) + .await + .is_err()); + for event in &events { + dispatch(&mut client, &mut state, &tx, json!(["EVENT", sub, event])).await; + } + dispatch(&mut client, &mut state, &tx, json!(["EOSE", sub])).await; + assert_eq!(rx.len(), 64, "delivered IDs must remain deduplicated"); + for event in &events[256..] { + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, event.id); + } + assert_eq!(state.channel_since(&ch), Some(1_319)); + recovery::recover_one(&mut client, &mut state, &tx, "synthetic-agent").await; + assert!(timeout(Duration::from_millis(30), server.next()) + .await + .is_err()); + let live = fixture_event(ch, 400, 9); + dispatch(&mut client, &mut state, &tx, json!(["EVENT", sub, live])).await; + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, live.id); + println!("18 subscriptions; 320 newest-first arrivals; 64 losses coalesced; 0 REQ while full; 1 targeted REQ; 320 unique deliveries + live"); +} + +#[tokio::test] +async fn socket_owner_services_ping_shutdown_and_coalesces_overflow_ticks() { + let (client, mut server) = test_ws_pair().await; + let (tx, mut rx) = mpsc::channel(1); + let (control_tx, _control_rx) = mpsc::channel(1); + let (cmd_tx, cmd_rx) = mpsc::channel(64); + let task = tokio::spawn(run_background_task( + client, + VecDeque::new(), + tx, + control_tx, + cmd_rx, + Keys::generate(), + "ws://127.0.0.1:1".into(), + "synthetic-agent".into(), + None, + )); + let channels: Vec<_> = (0..18).map(|_| Uuid::new_v4()).collect(); + for ch in &channels { + cmd_tx + .send(RelayCommand::Subscribe { + channel_id: *ch, + filter: test_channel_filter(), + replay_since: Some(1_000), + }) + .await + .unwrap(); + } + let mut subscriptions = 0; + while subscriptions < 18 { + let frame = timeout(Duration::from_secs(2), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + match frame { + Message::Text(text) => { + let req: Value = serde_json::from_str(&text).unwrap(); + assert_eq!(req[0], "REQ"); + server + .send(Message::Text(json!(["EOSE", req[1]]).to_string().into())) + .await + .unwrap(); + subscriptions += 1; + } + Message::Ping(payload) => server.send(Message::Pong(payload)).await.unwrap(), + other => panic!("unexpected {other:?}"), + } + } + let sub = channel_sub_id(channels[0]); + for n in 0..40 { + server + .send(Message::Text( + json!(["EVENT", sub, fixture_event(channels[0], n, 9)]) + .to_string() + .into(), + )) + .await + .unwrap(); + } + server.send(Message::Ping(vec![42].into())).await.unwrap(); + timeout(Duration::from_secs(2), async { + loop { + match server.next().await.unwrap().unwrap() { + Message::Ping(payload) => server.send(Message::Pong(payload)).await.unwrap(), + Message::Pong(payload) => { + assert_eq!(payload.as_ref(), &[42]); + break; + } + other => panic!("no immediate all-channel recovery before ping: {other:?}"), + } + } + }) + .await + .unwrap(); + // Wait across a recovery tick with the consumer still full. + assert!(socket_frame(&mut server, Duration::from_millis(5_100)) + .await + .is_none()); + rx.recv().await.unwrap().unwrap(); + let frame = timeout(Duration::from_secs(6), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[1], sub); + assert_eq!(req[2]["since"], 996); + assert!(timeout(Duration::from_millis(100), server.next()) + .await + .is_err()); + // Sustained lag: each replay is followed by another burst, without EOSE. + // Recovery must stay paced, not permanently stall or sweep healthy channels. + for round in 1..=3 { + let started = tokio::time::Instant::now(); + for n in round * 40..(round + 1) * 40 { + server + .send(Message::Text( + json!(["EVENT", sub, fixture_event(channels[0], n, 9)]) + .to_string() + .into(), + )) + .await + .unwrap(); + } + server.send(Message::Ping(vec![43].into())).await.unwrap(); + let pong = timeout(Duration::from_secs(1), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + assert!( + matches!(pong, Message::Pong(_)), + "recovery preempted ping: {pong:?}" + ); + rx.recv().await.unwrap().unwrap(); + let frame = timeout(Duration::from_secs(6), server.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[1], sub, "healthy channels must not be swept"); + assert!( + started.elapsed() >= Duration::from_secs(4), + "unpaced repeat" + ); + } + cmd_tx.send(RelayCommand::Shutdown).await.unwrap(); + timeout(Duration::from_secs(1), task) + .await + .unwrap() + .unwrap(); + println!("socket-owner seam: 18 live REQs, 39 coalesced losses, PONG while full, zero recovery across full-capacity timer tick, four paced targeted REQs over sustained lag without EOSE, responsive shutdown"); +} + +#[tokio::test] +async fn recovery_is_fair_and_paced_even_with_new_loss_and_stale_eose() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let channels = [Uuid::new_v4(), Uuid::new_v4()]; + for ch in channels { + seed_test_subscription(&mut state, ch); + state.channel_dropped_since.insert(ch, 600); + } + state.membership_sub_active = true; + state.membership_dropped_since = Some(500); + let (tx, _rx) = mpsc::channel(1); + let mut visited = HashSet::new(); + for round in 0..9 { + timeout( + Duration::from_millis(100), + recovery::ready(&tx, recovery::ready_at(&mut state)), + ) + .await + .unwrap(); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + let req = next_test_frame(&mut server).await; + let sub = req[1].as_str().unwrap(); + if round < 3 { + assert!(visited.insert(sub.to_owned()), "starved intent"); + } + if sub == MEMBERSHIP_NOTIF_SUB_ID { + assert_eq!(req[2]["since"], 495); + state.membership_dropped_since = Some(500); + } else { + let ch = channel_id_from_sub_id(sub).unwrap(); + assert_eq!(req[2]["since"], 595); + state.channel_dropped_since.insert(ch, 600); + } + // Neither stale nor current EOSE creates completion state or erases loss. + dispatch(&mut client, &mut state, &tx, json!(["EOSE", sub])).await; + for _ in 0..30 { + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + } + assert!(timeout(Duration::from_millis(1), server.next()) + .await + .is_err()); + advance_clock(recovery::RECOVERY_INTERVAL).await; + } + for ch in channels { + state.active_subscriptions.remove(&ch); + state.clear_channel_state(&ch); + assert!(!state + .recovery + .last_attempt + .contains_key(&channel_sub_id(ch))); + } + assert_eq!(state.recovery.last_attempt.len(), 1); +} + +#[tokio::test] +async fn gate_headroom_failed_writes_and_reconnect_preserve_pending_attempts() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let ch = Uuid::new_v4(); + seed_test_subscription(&mut state, ch); + state.channel_dropped_since.insert(ch, 700); + let (tx, mut rx) = mpsc::channel(4); + for _ in 0..3 { + tx.try_send(None).unwrap(); + } + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert!(state.recovery.last_attempt.is_empty()); + rx.recv().await; + state.rate_limit_gate = Some(tokio::time::Instant::now() + Duration::from_secs(10)); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert!(state.recovery.last_attempt.is_empty()); + advance_clock(Duration::from_secs(10)).await; + // Close locally, so the actual production writer fails deterministically. + client.close(None).await.unwrap(); + server.next().await; + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert_eq!(state.channel_dropped_since[&ch], 700); + let attempted = state.recovery.last_attempt.clone(); + for _ in 0..30 { + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + } + assert_eq!(state.recovery.last_attempt, attempted); + advance_clock(recovery::RECOVERY_INTERVAL).await; + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert_ne!( + state.recovery.last_attempt, attempted, + "failed write must be retried" + ); + assert_eq!(state.channel_dropped_since[&ch], 700); + + let (mut client, mut server) = test_ws_pair().await; + let (_cmd_tx, mut cmd_rx) = mpsc::channel(1); + assert!(matches!( + resubscribe_after_reconnect(&mut client, &mut cmd_rx, &mut state, "agent", true,).await, + ResubscribeResult::Ok + )); + let req = next_test_frame(&mut server).await; + assert_eq!(req[1], channel_sub_id(ch)); + assert_eq!(req[2]["since"], 695); + assert!(!state.channel_dropped_since.contains_key(&ch)); + // This is deliberately the baseline write-retirement contract, not a receipt. +} + +async fn advance_clock(duration: Duration) { + tokio::time::pause(); + tokio::time::advance(duration).await; + tokio::time::resume(); +} + +#[tokio::test] +async fn blocked_recovery_write_is_bounded_and_retains_loss() { + let (mut client, _stalled_server) = test_ws_pair().await; + let mut state = BgState::new(); + let ch = Uuid::new_v4(); + seed_test_subscription(&mut state, ch); + // Bounded 16MB JSON request exceeds loopback TCP buffering. The server does + // not read it. This tests the real production write/timeout, not a mock sink. + state.active_filters.get_mut(&ch).unwrap().kinds = Some(vec![9; 8_000_000]); + state.channel_dropped_since.insert(ch, 700); + let (tx, _rx) = mpsc::channel(1); + let started = tokio::time::Instant::now(); + timeout( + Duration::from_secs(15), + recovery::recover_one(&mut client, &mut state, &tx, "agent"), + ) + .await + .unwrap(); + assert!(started.elapsed() >= Duration::from_secs(WS_SEND_TIMEOUT_SECS)); + assert_eq!(state.channel_dropped_since[&ch], 700); + let attempted = state.recovery.last_attempt.clone(); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + assert_eq!(state.recovery.last_attempt, attempted); +} + +// Keep the fixture responsive to independent client keepalives while checking +// recovery traffic. Wall-clock scheduling may deliver the initial ping late. +async fn socket_frame( + server: &mut WebSocketStream, + duration: Duration, +) -> Option { + let deadline = tokio::time::Instant::now() + duration; + loop { + match tokio::time::timeout_at(deadline, server.next()).await { + Err(_) => return None, + Ok(Some(Ok(Message::Ping(payload)))) => { + server.send(Message::Pong(payload)).await.unwrap(); + } + Ok(Some(Ok(frame))) => return Some(frame), + other => panic!("unexpected socket state: {other:?}"), + } + } +} + +#[path = "recovery_wake_tests.rs"] +mod wake; diff --git a/crates/buzz-acp/src/relay/recovery_wake_tests.rs b/crates/buzz-acp/src/relay/recovery_wake_tests.rs new file mode 100644 index 00000000000..de27c7d06c9 --- /dev/null +++ b/crates/buzz-acp/src/relay/recovery_wake_tests.rs @@ -0,0 +1,507 @@ +//! Capacity-wake boundaries and the independently reproduced R1 schedule. +use super::*; + +// Reviewer-authored, exact-source comparison of recurring headroom at the socket owner. +// A small periodically refilled queue is empty for most of each five-second period. +async fn review_count_until( + server: &mut WebSocketStream, + deadline: tokio::time::Instant, +) -> usize { + let mut count = 0; + while tokio::time::Instant::now() < deadline { + let remaining = deadline.saturating_duration_since(tokio::time::Instant::now()); + match socket_frame(server, remaining).await { + None => break, + Some(Message::Text(text)) => { + let frame: Value = serde_json::from_str(&text).unwrap(); + assert_eq!(frame[0], "REQ"); + count += 1; + } + other => panic!("unexpected {other:?}"), + } + } + count +} + +async fn review_barrier(server: &mut WebSocketStream) -> usize { + server.send(Message::Ping(vec![77].into())).await.unwrap(); + let mut count = 0; + loop { + match socket_frame(server, Duration::from_secs(2)).await.unwrap() { + Message::Pong(payload) => { + assert_eq!(payload.as_ref(), &[77]); + return count; + } + Message::Text(text) => { + let frame: Value = serde_json::from_str(&text).unwrap(); + assert_eq!(frame[0], "REQ"); + count += 1; + } + other => panic!("unexpected {other:?}"), + } + } +} + +#[tokio::test] +async fn review_recurring_headroom_between_ticks_gets_an_attempt() { + let (client, mut server) = test_ws_pair().await; + let (tx, mut rx) = mpsc::channel(1); + let (control_tx, _control_rx) = mpsc::channel(1); + let (cmd_tx, cmd_rx) = mpsc::channel(8); + let start = tokio::time::Instant::now(); + let task = tokio::spawn(run_background_task( + client, + VecDeque::new(), + tx, + control_tx, + cmd_rx, + Keys::generate(), + "ws://127.0.0.1:1".into(), + "agent".into(), + None, + )); + let ch = Uuid::new_v4(); + let sub = channel_sub_id(ch); + cmd_tx + .send(RelayCommand::Subscribe { + channel_id: ch, + filter: test_channel_filter(), + replay_since: Some(1000), + }) + .await + .unwrap(); + let frame = socket_frame(&mut server, Duration::from_secs(2)) + .await + .unwrap(); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[1], sub); + let lost = fixture_event(ch, 1, 9); + for event in [fixture_event(ch, 0, 9), lost.clone()] { + server + .send(Message::Text( + json!(["EVENT", sub, event]).to_string().into(), + )) + .await + .unwrap(); + } + let mut attempts = review_barrier(&mut server).await; + assert_eq!(rx.len(), 1); + rx.recv().await.unwrap().unwrap(); + for round in 1..=4 { + // Headroom until 1s before the tick; then only one live arrival, no new + // overflow. The queue stays full across the tick and drains 0.5s later. + attempts += review_count_until( + &mut server, + start + Duration::from_millis(round * 5000 - 1000), + ) + .await; + assert_eq!(rx.len(), 0); + server + .send(Message::Text( + json!(["EVENT", sub, fixture_event(ch, 10 + round, 9)]) + .to_string() + .into(), + )) + .await + .unwrap(); + attempts += review_barrier(&mut server).await; + assert_eq!(rx.len(), 1); + attempts += review_count_until( + &mut server, + start + Duration::from_millis(round * 5000 + 500), + ) + .await; + rx.recv().await.unwrap().unwrap(); + } + println!("REVIEW periodic consumer: 4 full-at-tick windows, empty >=3.5s each period, recovery_requests={attempts}"); + assert_eq!( + attempts, 1, + "recurring headroom must not strand the first attempt" + ); + // Return the missing event using the actual requested stable subscription. + server + .send(Message::Text( + json!(["EVENT", sub, lost]).to_string().into(), + )) + .await + .unwrap(); + assert_eq!(review_barrier(&mut server).await, 0); + assert_eq!(rx.recv().await.unwrap().unwrap().event.id, lost.id); + let after = review_count_until(&mut server, start + Duration::from_millis(25_500)).await; + assert_eq!( + after, 0, + "no timer churn or extra requests after successful write" + ); + println!( + "REVIEW continuous-headroom control: additional_requests={after}; missing event delivered" + ); + cmd_tx.send(RelayCommand::Shutdown).await.unwrap(); + timeout(Duration::from_secs(2), task) + .await + .unwrap() + .unwrap(); +} + +/// Same real socket owner as production, with no control of its internal state. +struct Owner { + server: WebSocketStream, + rx: mpsc::Receiver>, + cmd: mpsc::Sender, + task: tokio::task::JoinHandle<()>, + ch: Uuid, +} + +impl Owner { + async fn new(capacity: usize) -> Self { + let (client, server) = test_ws_pair().await; + let (tx, rx) = mpsc::channel(capacity); + let (control_tx, _control_rx) = mpsc::channel(1); + let (cmd, cmd_rx) = mpsc::channel(8); + let task = tokio::spawn(run_background_task( + client, + VecDeque::new(), + tx, + control_tx, + cmd_rx, + Keys::generate(), + "ws://127.0.0.1:1".into(), + "agent".into(), + None, + )); + let mut owner = Self { + server, + rx, + cmd, + task, + ch: Uuid::new_v4(), + }; + owner.subscribe().await; + owner + } + + async fn subscribe(&mut self) { + self.cmd + .send(RelayCommand::Subscribe { + channel_id: self.ch, + filter: test_channel_filter(), + replay_since: Some(1000), + }) + .await + .unwrap(); + let req = self.request(Duration::from_secs(2)).await; + assert_eq!(req[1], channel_sub_id(self.ch)); + } + + async fn event(&mut self, n: u64) { + self.server + .send(Message::Text( + json!([ + "EVENT", + channel_sub_id(self.ch), + fixture_event(self.ch, n, 9) + ]) + .to_string() + .into(), + )) + .await + .unwrap(); + } + + async fn request(&mut self, duration: Duration) -> Value { + let frame = socket_frame(&mut self.server, duration) + .await + .expect("recovery not woken"); + let req: Value = serde_json::from_str(frame.to_text().unwrap()).unwrap(); + assert_eq!(req[0], "REQ"); + req + } + + async fn shutdown(self) { + self.cmd.send(RelayCommand::Shutdown).await.unwrap(); + timeout(Duration::from_secs(1), self.task) + .await + .unwrap() + .unwrap(); + } +} + +#[tokio::test] +async fn capacity_flapping_cannot_storm_or_delay_an_allowed_attempt() { + let mut owner = Owner::new(1).await; + owner.event(0).await; + owner.event(1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); + let first = owner.request(Duration::from_millis(500)).await; + let first_at = tokio::time::Instant::now(); + assert_eq!(first[2]["since"], 996); + // New loss, then rapid full/empty transitions during the attempt cooldown. + // No socket activity or capacity transition may reset or bypass that bound. + for n in 1..=20 { + owner.event(n * 2).await; + owner.event(n * 2 + 1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); + assert!(socket_frame(&mut owner.server, Duration::from_millis(10)) + .await + .is_none()); + } + let req = owner.request(Duration::from_secs(6)).await; + assert_eq!(req[1], channel_sub_id(owner.ch)); + // Arrival timestamps approximate send completion; leave tolerance for TCP. + assert!(first_at.elapsed() >= Duration::from_millis(4_900)); + assert!(first_at.elapsed() < Duration::from_secs(6)); + assert_eq!(req[2]["since"], 998); + assert!(socket_frame(&mut owner.server, Duration::from_millis(100)) + .await + .is_none()); + owner.shutdown().await; +} + +#[tokio::test] +async fn partial_capacity_wait_does_not_steal_live_slots_and_cancels_on_unsubscribe() { + let mut owner = Owner::new(5).await; // odd capacity: threshold rounds UP to 3 + for n in 0..6 { + owner.event(n).await; + } + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); // partial reservation, insufficient for replay + assert!(socket_frame(&mut owner.server, Duration::from_millis(30)) + .await + .is_none()); + owner.event(6).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + assert_eq!( + owner.rx.len(), + 5, + "select must release partial permits BEFORE try_send" + ); + for _ in 0..2 { + owner.rx.recv().await.unwrap(); + } + assert!(socket_frame(&mut owner.server, Duration::from_millis(30)) + .await + .is_none()); + owner.rx.recv().await.unwrap(); // exactly three slots free, prompt capacity wake + let req = owner.request(Duration::from_millis(500)).await; + assert_eq!(req[2]["since"], 1000); + assert_eq!( + owner + .rx + .recv() + .await + .unwrap() + .unwrap() + .event + .created_at + .as_secs(), + 1004 + ); + assert_eq!( + owner + .rx + .recv() + .await + .unwrap() + .unwrap() + .event + .created_at + .as_secs(), + 1006 + ); + + // Wait out cooldown then create another pending loss and partial reservation. + tokio::time::sleep(recovery::RECOVERY_INTERVAL).await; + for n in 7..13 { + owner.event(n).await; + } + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.rx.recv().await.unwrap(); + assert!(socket_frame(&mut owner.server, Duration::from_millis(30)) + .await + .is_none()); + owner + .cmd + .send(RelayCommand::Unsubscribe { + channel_id: owner.ch, + }) + .await + .unwrap(); + let close = socket_frame(&mut owner.server, Duration::from_secs(1)) + .await + .unwrap(); + let close: Value = serde_json::from_str(close.to_text().unwrap()).unwrap(); + assert_eq!(close[0], "CLOSE"); + while owner.rx.try_recv().is_ok() {} + assert!(socket_frame(&mut owner.server, Duration::from_millis(100)) + .await + .is_none()); + owner.subscribe().await; + assert!(socket_frame(&mut owner.server, Duration::from_millis(100)) + .await + .is_none()); + // A re-added intent can record and recover fresh loss immediately. + for n in 20..26 { + owner.event(n).await; + } + assert_eq!(review_barrier(&mut owner.server).await, 0); + while owner.rx.try_recv().is_ok() {} + let req = owner.request(Duration::from_millis(500)).await; + assert_eq!(req[2]["since"], 1020); + owner.shutdown().await; +} + +#[tokio::test] +async fn shutdown_and_transport_loss_cancel_a_capacity_wait() { + let mut owner = Owner::new(1).await; + owner.event(0).await; + owner.event(1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + // Full queue cannot block commands or processing an actual socket close. + owner.server.close(None).await.unwrap(); + owner.shutdown().await; + let mut owner = Owner::new(1).await; + owner.event(0).await; + owner.event(1).await; + assert_eq!(review_barrier(&mut owner.server).await, 0); + owner.shutdown().await; +} + +#[tokio::test] +async fn readiness_gate_ownership_and_attempt_deadlines_are_not_polling_ticks() { + let (mut client, mut server) = test_ws_pair().await; + let mut state = BgState::new(); + let ch = Uuid::new_v4(); + seed_test_subscription(&mut state, ch); + let (tx, mut rx) = mpsc::channel(4); + assert!(recovery::ready_at(&mut state).is_none()); + state.channel_dropped_since.insert(ch, 700); + state + .rate_limited_pending + .insert(ch, tokio::time::Instant::now()); + assert!(recovery::ready_at(&mut state).is_none()); + state.rate_limited_pending.clear(); + state.resubscribe_retry.insert(ch); + assert!(recovery::ready_at(&mut state).is_none()); + state.resubscribe_retry.clear(); + state.rate_limit_gate = Some(tokio::time::Instant::now() + Duration::from_millis(80)); + let at = recovery::ready_at(&mut state); + assert_eq!(at, state.rate_limit_gate); + assert!(timeout(Duration::from_millis(20), recovery::ready(&tx, at)) + .await + .is_err()); + timeout(Duration::from_millis(200), recovery::ready(&tx, at)) + .await + .unwrap(); + recovery::recover_one(&mut client, &mut state, &tx, "agent").await; + next_test_frame(&mut server).await; + state.channel_dropped_since.insert(ch, 800); + let at = recovery::ready_at(&mut state).unwrap(); + let remaining = at.saturating_duration_since(tokio::time::Instant::now()); + assert!(remaining > Duration::from_millis(4900)); + // Neither new loss nor an intervening gate shorter than cooldown delays it. + state.rate_limit_gate = Some(tokio::time::Instant::now() + Duration::from_millis(10)); + assert_eq!(recovery::ready_at(&mut state), Some(at)); + state.rate_limit_gate = Some(at + Duration::from_secs(1)); + assert_eq!(recovery::ready_at(&mut state), state.rate_limit_gate); + advance_clock(Duration::from_secs(6)).await; + // Cancellation releases partial permits. No hidden reservation survives it. + for _ in 0..3 { + tx.try_send(None).unwrap(); + } + assert!(timeout( + Duration::from_millis(20), + recovery::ready(&tx, recovery::ready_at(&mut state)) + ) + .await + .is_err()); + assert_eq!(tx.capacity(), 1); + rx.recv().await.unwrap(); + timeout( + Duration::from_millis(100), + recovery::ready(&tx, recovery::ready_at(&mut state)), + ) + .await + .unwrap(); + assert_eq!(tx.capacity(), 2); + drop(rx); + assert!(timeout( + Duration::from_millis(20), + recovery::ready(&tx, recovery::ready_at(&mut state)) + ) + .await + .is_err()); +} + +#[tokio::test] +async fn readiness_uses_channel_wakes_without_idle_churn_or_lost_capacity() { + use std::future::Future; + use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }; + use std::task::{Context, Wake, Waker}; + #[derive(Default)] + struct Wakes(AtomicUsize); + impl Wake for Wakes { + fn wake(self: Arc) { + self.0.fetch_add(1, Ordering::SeqCst); + } + fn wake_by_ref(self: &Arc) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let wakes = Arc::new(Wakes::default()); + let waker = Waker::from(wakes.clone()); + let mut cx = Context::from_waker(&waker); + let (tx, mut rx) = mpsc::channel(4); + for _ in 0..4 { + tx.try_send(None).unwrap(); + } + let mut idle = Box::pin(recovery::ready(&tx, None)); + assert!(idle.as_mut().poll(&mut cx).is_pending()); + rx.recv().await.unwrap(); + rx.recv().await.unwrap(); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + 0, + "no loss: no capacity subscription" + ); + drop(idle); + // Capacity freed before registration cannot be lost; ready on first poll. + let now = Some(tokio::time::Instant::now()); + let mut ready = Box::pin(recovery::ready(&tx, now)); + assert!(ready.as_mut().poll(&mut cx).is_ready()); + assert_eq!(tx.capacity(), 2, "successful readiness returns all permits"); + drop(ready); + for _ in 0..2 { + tx.try_send(None).unwrap(); + } + let mut ready = Box::pin(recovery::ready(&tx, now)); + assert!(ready.as_mut().poll(&mut cx).is_pending()); + assert_eq!(wakes.0.load(Ordering::SeqCst), 0); + rx.recv().await.unwrap(); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + 0, + "below threshold: do not wake" + ); + rx.recv().await.unwrap(); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + 1, + "threshold: channel wakes its waiter" + ); + assert!(ready.as_mut().poll(&mut cx).is_ready()); + assert_eq!(tx.capacity(), 2); + drop(ready); + drop(rx); + let mut closed = Box::pin(recovery::ready(&tx, now)); + let before = wakes.0.load(Ordering::SeqCst); + assert!(closed.as_mut().poll(&mut cx).is_pending()); + assert_eq!( + wakes.0.load(Ordering::SeqCst), + before, + "closed: no self-wake loop" + ); +} diff --git a/crates/buzz-relay/Cargo.toml b/crates/buzz-relay/Cargo.toml index 41bdc3b9e9b..0e161cc2f43 100644 --- a/crates/buzz-relay/Cargo.toml +++ b/crates/buzz-relay/Cargo.toml @@ -84,8 +84,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.74.0", 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.74.0", 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-rc9", 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-rc9", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] } buzz-core = { workspace = true, features = ["test-utils"] } buzz-auth = { workspace = true, features = ["dev"] } reqwest = { workspace = true } diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index fbaa547a032..09b92ab848d 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.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -221,7 +221,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -447,20 +447,21 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069" +checksum = "2c713e1f14c7b82e32ea159af1c6e2f070cfadbdf23fb2512acce9af0a26f1a2" dependencies = [ - "async-utility", "futures", "futures-util", "js-sys", "tokio", + "tokio-happy-eyeballs", "tokio-rustls", "tokio-socks", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.28.0", "url", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -507,12 +508,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -762,6 +757,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "beef" version = "0.5.2" @@ -774,7 +775,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "serde", "unicode-normalization", ] @@ -815,7 +816,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2d6094e2a1ba3c93b5a596fe5a10d1a10c3c6e06785cde89f693a044c01aa40" dependencies = [ - "bitcoin-internals", + "bitcoin-internals 0.5.0", ] [[package]] @@ -827,6 +828,12 @@ dependencies = [ "hex-conservative 0.3.2", ] +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" version = "0.1.101" @@ -847,6 +854,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals 0.6.0", + "hex-conservative 1.3.0", + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1045,7 +1064,7 @@ dependencies = [ "chrono", "hex", "hmac 0.13.0", - "nostr", + "nostr 0.44.7", "percent-encoding", "rand 0.10.2", "serde", @@ -1099,7 +1118,7 @@ dependencies = [ "mesh-llm-sdk", "mesh-llm-system", "neteq", - "nostr", + "nostr 0.44.7", "notify-rust", "objc2", "objc2-app-kit", @@ -1167,7 +1186,7 @@ dependencies = [ "imagesize", "infer", "mp4", - "nostr", + "nostr 0.44.7", "rust-s3", "serde", "serde_json", @@ -1196,7 +1215,7 @@ name = "buzz-sdk" version = "0.1.0" dependencies = [ "buzz-core", - "nostr", + "nostr 0.44.7", "serde", "serde_json", "thiserror 2.0.18", @@ -1437,9 +1456,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" @@ -1602,7 +1621,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1840,7 +1859,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.62.2", + "windows 0.61.3", ] [[package]] @@ -2269,7 +2288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 2.0.118", + "syn 1.0.109", ] [[package]] @@ -2447,7 +2466,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2728,7 +2747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2818,6 +2837,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -3236,8 +3265,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.2.1", - "windows-result 0.4.1", + "windows-link 0.1.3", + "windows-result 0.3.4", ] [[package]] @@ -3533,9 +3562,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" dependencies = [ "atomic-waker", "bytes", @@ -3561,6 +3590,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -3619,6 +3657,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0049b265b7f201ca9ab25475b22b47fe444060126a51abe00f77d986fc5cc52e" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -3662,29 +3710,12 @@ dependencies = [ ] [[package]] -name = "hf-hub" -version = "1.0.0-rc.1" +name = "hex-conservative" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89305dc8fe34e165eaf0eb12b6e294e12381d9df9a431bcc52a5809bab4319" +checksum = "271e0d19bcb473b6675739a2b536076b24a082316cb5199ad918edce10c599e8" dependencies = [ - "base64 0.22.1", - "bon", - "bytes", - "futures", - "globset", - "hf-xet", - "hyper", - "pathdiff", - "reqwest 0.13.4", - "serde", - "serde_json", - "sha2 0.11.0", - "thiserror 2.0.18", - "tokio", - "tokio-retry", - "tokio-util", - "tracing", - "url", + "arrayvec", ] [[package]] @@ -3982,7 +4013,7 @@ dependencies = [ "tokio", "tower-service", "tracing", - "windows-registry 0.6.1", + "windows-registry 0.5.3", ] [[package]] @@ -3997,7 +4028,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.61.2", ] [[package]] @@ -4308,14 +4339,14 @@ dependencies = [ [[package]] name = "iroh" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971" +checksum = "329552fad4e46b5ccb4439d5635216f6289eec2c46f3fde4bc732d762694b9e5" dependencies = [ "backon", "blake3", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "ctutils", "data-encoding", "derive_more", @@ -4359,9 +4390,9 @@ dependencies = [ [[package]] name = "iroh-base" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830a582cd54410dc1aa71d4786a82c3297d7b0165accd8b6dbbb3b240b48140d" +checksum = "3ccf4cde68a02ef03c0095ab2c031c6adfe508f3bc8e7c258558a027ebe64a8e" dependencies = [ "curve25519-dalek 5.0.0-rc.0", "data-encoding", @@ -4378,12 +4409,12 @@ dependencies = [ [[package]] name = "iroh-dns" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a" +checksum = "9a41224eb0140a5c519935d4073fc6b236d174080be401f9bf7da126b2ee3aa4" dependencies = [ "arc-swap", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "hickory-resolver", "iroh-base", @@ -4429,13 +4460,13 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354" +checksum = "1b5f6f07e2c9b0c4bc82e2864be09f4aea4320165d5316e668616dbc822f428a" dependencies = [ "blake3", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "data-encoding", "derive_more", "getrandom 0.4.3", @@ -4447,7 +4478,7 @@ dependencies = [ "iroh-base", "iroh-dns", "iroh-metrics", - "lru 0.18.1", + "lru", "n0-error", "n0-future", "noq", @@ -4468,7 +4499,6 @@ dependencies = [ "tokio-websockets", "tracing", "url", - "vergen-gitcl", "webpki-roots 1.0.8", "ws_stream_wasm", ] @@ -4643,16 +4673,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" @@ -4929,12 +4949,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" - [[package]] name = "lru" version = "0.18.1" @@ -5132,8 +5146,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "mesh-llm-client", @@ -5142,8 +5156,8 @@ dependencies = [ [[package]] name = "mesh-llm-api-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5153,21 +5167,18 @@ dependencies = [ [[package]] name = "mesh-llm-build-info" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-llm-client" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "crypto_box", - "ed25519-dalek", - "hex", "httparse", "iroh", "mesh-llm-identity", @@ -5177,11 +5188,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", @@ -5190,8 +5199,8 @@ dependencies = [ [[package]] name = "mesh-llm-config" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5206,8 +5215,8 @@ dependencies = [ [[package]] name = "mesh-llm-embedded-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-host-runtime", @@ -5216,24 +5225,26 @@ dependencies = [ [[package]] name = "mesh-llm-events" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", + "chrono", "clap", "crossterm 0.28.1", + "libc", "ratatui", + "serde", "serde_json", + "tracing", + "uuid", ] [[package]] name = "mesh-llm-gpu-bench" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ - "anyhow", - "cc", - "libc", "serde", "serde_json", "tracing", @@ -5241,8 +5252,8 @@ dependencies = [ [[package]] name = "mesh-llm-guardrails" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -5250,16 +5261,45 @@ dependencies = [ [[package]] name = "mesh-llm-hardware-profile" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "mesh-llm-native-runtime", ] +[[package]] +name = "mesh-llm-hf-hub" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43088a838cf0c6715c65f65a5ac99045fd6d6e90949a8a4183b8104ab791e96b" +dependencies = [ + "base64 0.22.1", + "bon", + "bytes", + "futures", + "getrandom 0.2.17", + "globset", + "hf-xet", + "hyper", + "pathdiff", + "percent-encoding", + "reqwest 0.13.4", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "tokio-retry", + "tokio-util", + "tracing", + "url", + "wasm-bindgen-futures", +] + [[package]] name = "mesh-llm-host-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "argon2", @@ -5277,12 +5317,10 @@ dependencies = [ "flate2", "futures-util", "hex", - "hf-hub", "http", "http-body-util", "httparse", "iroh", - "json5", "keyring", "libc", "mdns-sd", @@ -5292,7 +5330,9 @@ dependencies = [ "mesh-llm-config", "mesh-llm-events", "mesh-llm-guardrails", + "mesh-llm-hf-hub", "mesh-llm-identity", + "mesh-llm-log-store", "mesh-llm-native-runtime", "mesh-llm-node", "mesh-llm-plugin", @@ -5304,6 +5344,7 @@ dependencies = [ "mesh-llm-types", "mesh-llm-ui", "mesh-mixture-of-agents", + "mesh-native-serving-plugin-host", "model-artifact", "model-hf", "model-package", @@ -5325,7 +5366,6 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml", "sha2 0.10.9", "skippy-coordinator", "skippy-ffi", @@ -5345,14 +5385,16 @@ dependencies = [ "tracing-subscriber", "url", "urlencoding", + "uuid", + "windows-sys 0.61.2", "zeroize", "zip 2.4.2", ] [[package]] name = "mesh-llm-identity" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "argon2", "base64 0.22.1", @@ -5371,10 +5413,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "mesh-llm-log-store" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +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.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "serde", @@ -5384,8 +5444,8 @@ dependencies = [ [[package]] name = "mesh-llm-node" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-types", @@ -5398,8 +5458,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5415,8 +5475,8 @@ dependencies = [ [[package]] name = "mesh-llm-plugin-manager" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5434,8 +5494,8 @@ dependencies = [ [[package]] name = "mesh-llm-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "hex", @@ -5445,18 +5505,30 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "mesh-llm-release-footer" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "hex", + "sha2 0.10.9", +] + [[package]] name = "mesh-llm-routing" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ + "blake3", "iroh", + "serde", + "serde_json", ] [[package]] name = "mesh-llm-runtime-install" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5478,8 +5550,8 @@ dependencies = [ [[package]] name = "mesh-llm-sdk" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "mesh-llm-api-client", @@ -5493,8 +5565,8 @@ dependencies = [ [[package]] name = "mesh-llm-skills" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "dirs", @@ -5504,8 +5576,8 @@ dependencies = [ [[package]] name = "mesh-llm-system" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "chrono", @@ -5513,8 +5585,12 @@ dependencies = [ "dirs", "hex", "libc", + "libloading 0.8.9", "mesh-llm-build-info", "mesh-llm-gpu-bench", + "mesh-llm-native-runtime", + "mesh-llm-release-footer", + "mesh-llm-runtime-install", "reqwest 0.12.28", "semver", "serde", @@ -5527,8 +5603,8 @@ dependencies = [ [[package]] name = "mesh-llm-types" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "hex", "serde", @@ -5538,13 +5614,13 @@ dependencies = [ [[package]] name = "mesh-llm-ui" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "mesh-mixture-of-agents" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "mesh-llm-guardrails", @@ -5555,6 +5631,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "mesh-native-serving-plugin-api" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" + +[[package]] +name = "mesh-native-serving-plugin-host" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "anyhow", + "libloading 0.8.9", + "mesh-native-serving-plugin-api", + "skippy-server", + "skippy-tokenizer", +] + [[package]] name = "miette" version = "7.6.0" @@ -5642,13 +5735,13 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] name = "model-artifact" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", @@ -5658,32 +5751,35 @@ dependencies = [ [[package]] name = "model-hf" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "async-trait", "chrono", "dirs", - "hf-hub", + "libc", + "mesh-llm-hf-hub", "model-artifact", "model-ref", + "rustls", "serde", "serde_json", "sha2 0.10.9", "tokio", + "tracing", ] [[package]] name = "model-package" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "bytes", "chrono", "futures", - "hf-hub", + "mesh-llm-hf-hub", "model-hf", "model-ref", "reqwest 0.12.28", @@ -5696,16 +5792,16 @@ dependencies = [ [[package]] name = "model-ref" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", ] [[package]] name = "model-resolver" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "model-artifact", @@ -5801,7 +5897,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5837,7 +5933,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", @@ -5945,9 +6041,9 @@ dependencies = [ "libc", "mac-addr", "ndk-context", - "netlink-packet-core", - "netlink-packet-route", - "netlink-sys", + "netlink-packet-core 0.8.1", + "netlink-packet-route 0.31.0", + "netlink-sys 0.8.8", "objc2", "objc2-core-foundation", "objc2-core-wlan", @@ -5958,6 +6054,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "netdev" +version = "0.46.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4db4135b187ddae3b6813c4f9d9ac9d5181859fa6a08b4e006c63a130974ed62" +dependencies = [ + "block2", + "dispatch2", + "dlopen2", + "ipnet", + "jni 0.21.1", + "libc", + "mac-addr", + "ndk-context", + "netlink-packet-core 0.9.0", + "netlink-packet-route 0.33.0", + "netlink-sys 0.9.0", + "objc2-core-foundation", + "objc2-system-configuration", + "once_cell", + "plist", + "windows-sys 0.61.2", +] + [[package]] name = "neteq" version = "0.8.3" @@ -5980,6 +6100,12 @@ dependencies = [ "paste", ] +[[package]] +name = "netlink-packet-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6e2daf9a8c2e21302714706860a0e6be02e03d17294ecc66b354ea7d4059dd" + [[package]] name = "netlink-packet-route" version = "0.31.0" @@ -5989,7 +6115,20 @@ dependencies = [ "bitflags 2.13.0", "libc", "log", - "netlink-packet-core", + "netlink-packet-core 0.8.1", +] + +[[package]] +name = "netlink-packet-route" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d48ffc8b73a506e1ff0878528c72668f2bfbc3d875b6be890a96be5d0d5576" +dependencies = [ + "bitflags 2.13.0", + "libc", + "log", + "netlink-packet-core 0.9.0", + "zerocopy", ] [[package]] @@ -6001,8 +6140,8 @@ dependencies = [ "bytes", "futures", "log", - "netlink-packet-core", - "netlink-sys", + "netlink-packet-core 0.8.1", + "netlink-sys 0.8.8", "thiserror 2.0.18", ] @@ -6019,15 +6158,26 @@ dependencies = [ "tokio", ] +[[package]] +name = "netlink-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c99d38e00b420df49e940fe0826e667c3dad82ac68eb4c2bbf754c1c14a83a10" +dependencies = [ + "bytes", + "libc", + "log", +] + [[package]] name = "netwatch" -version = "0.19.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d9cbe01741347ef750d743d6690603f5eed8341e679fb51c8e629337aa11976" +checksum = "39da9cad5f23aa43401f09497d3b280e51b5feba115d9ffbf38ca35d6ff95e96" dependencies = [ "atomic-waker", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "ipnet", "js-sys", @@ -6035,11 +6185,12 @@ dependencies = [ "n0-error", "n0-future", "n0-watcher", - "netdev", - "netlink-packet-core", - "netlink-packet-route", + "netdev 0.45.0", + "netdev 0.46.2", + "netlink-packet-core 0.8.1", + "netlink-packet-route 0.31.0", "netlink-proto", - "netlink-sys", + "netlink-sys 0.8.8", "noq-udp", "objc2-core-foundation", "objc2-system-configuration", @@ -6082,7 +6233,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", ] @@ -6095,7 +6246,7 @@ checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -6107,7 +6258,7 @@ checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags 2.13.0", "cfg-if 1.0.4", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -6132,12 +6283,12 @@ dependencies = [ [[package]] name = "noq" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89" +checksum = "f1e6c57353d26be91a242f0ec96073066c4481a03f6be874daafb18902394515" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "derive_more", "noq-proto", "noq-udp", @@ -6154,11 +6305,12 @@ dependencies = [ [[package]] name = "noq-proto" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3" +checksum = "9a46813e306c29c4f86357b6ffa39a8e1c97bb274b9a296a19a56d62e4111225" dependencies = [ "aes-gcm", + "aws-lc-rs", "bytes", "derive_more", "enum-assoc", @@ -6181,11 +6333,11 @@ dependencies = [ [[package]] name = "noq-udp" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e" +checksum = "b56d621ed2c1773b5356ab39cc68c819f8d0103f7493d8661c4a5f5f8ea55f40" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "socket2", "tracing", @@ -6199,9 +6351,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" dependencies = [ "base64 0.22.1", - "bech32", + "bech32 0.11.1", "bip39", - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "cbc", "chacha20 0.9.1", "chacha20poly1305", @@ -6209,7 +6361,7 @@ dependencies = [ "hex", "instant", "scrypt", - "secp256k1", + "secp256k1 0.29.1", "serde", "serde_json", "unicode-normalization", @@ -6217,56 +6369,71 @@ dependencies = [ ] [[package]] -name = "nostr-database" -version = "0.44.0" +name = "nostr" +version = "0.45.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" +checksum = "67bacf4195a119bc55d576ec6b54cc103723cb46fff5f907b8bdac8fd29ac4a0" dependencies = [ - "lru 0.16.4", - "nostr", - "tokio", + "base64 0.22.1", + "bech32 0.12.0", + "bip39", + "bitcoin_hashes 1.2.0", + "cbc", + "chacha20 0.9.1", + "chacha20poly1305", + "faster-hex", + "opaquerr", + "rand 0.10.2", + "secp256k1 0.30.0", + "serde", + "serde_json", + "unicode-normalization", + "universal-time", + "url", + "zeroize", ] [[package]] -name = "nostr-gossip" -version = "0.44.0" +name = "nostr-database" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6" +checksum = "4b1fdb9fcba732e32719662afad1b267e50322dbe89e506017ec13f24361bddf" dependencies = [ - "nostr", + "nostr 0.45.4", + "opaquerr", ] [[package]] -name = "nostr-relay-pool" -version = "0.44.3" +name = "nostr-gossip" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "fa07539e52a71cb91fe0d693facaa298f03fcf9edcd66a521094e18e286e2336" dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "hex", - "lru 0.16.4", - "negentropy", - "nostr", - "nostr-database", - "tokio", - "tracing", + "nostr 0.45.4", + "opaquerr", ] [[package]] name = "nostr-sdk" -version = "0.44.1" +version = "0.45.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393" +checksum = "245f8642b10feecb0a40739a886ca1850e3be4e35dc6592b806ec437403ab9c9" dependencies = [ "async-utility", - "nostr", + "async-wsocket", + "faster-hex", + "futures", + "lru", + "negentropy", + "nostr 0.45.4", "nostr-database", "nostr-gossip", - "nostr-relay-pool", + "opaquerr", + "rand 0.10.2", "tokio", + "tokio-stream", "tracing", + "universal-time", ] [[package]] @@ -6298,7 +6465,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6408,7 +6575,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 1.3.1", "proc-macro2", "quote", "syn 2.0.118", @@ -6771,6 +6938,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opaquerr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" + [[package]] name = "open" version = "5.3.6" @@ -6784,19 +6957,20 @@ dependencies = [ [[package]] name = "openai-frontend" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "async-trait", "axum", "futures-core", "futures-util", + "mesh-llm-events", "mesh-llm-guardrails", "serde", "serde_json", "tokio", - "tokio-stream", "tracing", + "uuid", ] [[package]] @@ -6854,9 +7028,9 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" dependencies = [ "futures-core", "futures-sink", @@ -6868,22 +7042,22 @@ dependencies = [ [[package]] name = "opentelemetry-http" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331" dependencies = [ "async-trait", "bytes", "http", "opentelemetry", - "reqwest 0.12.28", + "reqwest 0.13.4", ] [[package]] name = "opentelemetry-otlp" -version = "0.31.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" +checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35" dependencies = [ "http", "opentelemetry", @@ -6891,15 +7065,15 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost 0.14.4", - "reqwest 0.12.28", + "reqwest 0.13.4", "thiserror 2.0.18", ] [[package]] name = "opentelemetry-proto" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" dependencies = [ "base64 0.22.1", "const-hex", @@ -6907,22 +7081,22 @@ dependencies = [ "opentelemetry_sdk", "prost 0.14.4", "serde", - "serde_json", "tonic", "tonic-prost", ] [[package]] name = "opentelemetry_sdk" -version = "0.31.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" +checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9" dependencies = [ "futures-channel", "futures-executor", "futures-util", "opentelemetry", "percent-encoding", + "portable-atomic", "rand 0.9.4", "thiserror 2.0.18", ] @@ -6996,7 +7170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.45.0", ] [[package]] @@ -7522,9 +7696,9 @@ dependencies = [ [[package]] name = "portmapper" -version = "0.19.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3713e4977408279158444a18c1a01ac9bf2e7eaf1fbfd1a19ac9cd18d90721" +checksum = "ca97242f016e090a25330613bc2382aab65eb22f9149dbe84d8f8e711d49a530" dependencies = [ "base64 0.22.1", "bytes", @@ -7756,7 +7930,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "itertools", "log", "multimap", @@ -7776,7 +7950,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "itertools", "log", "multimap", @@ -7975,7 +8149,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", @@ -8017,12 +8191,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.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8178,7 +8352,7 @@ dependencies = [ "hashbrown 0.17.1", "itertools", "kasuari", - "lru 0.18.1", + "lru", "palette", "serde", "strum", @@ -8396,7 +8570,6 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", - "futures-channel", "futures-core", "futures-util", "h2", @@ -8612,12 +8785,23 @@ dependencies = [ [[package]] name = "rpassword" -version = "5.0.1" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196" dependencies = [ "libc", - "winapi", + "rtoolbox", + "windows-sys 0.61.2", +] + +[[package]] +name = "rtoolbox" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1efe12a1469752d0e6ff5ebec0b6ef4924cc5c4c71046b0ec730040535819d" +dependencies = [ + "libc", + "windows-sys 0.61.2", ] [[package]] @@ -8753,7 +8937,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8823,7 +9007,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9008,6 +9192,17 @@ dependencies = [ "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.101", + "rand 0.8.6", + "secp256k1-sys", +] + [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -9079,7 +9274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9549,9 +9744,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "simple-dns" -version = "0.11.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a75cbde1bf934313596a004973e462f9a82caa814dcf1a5f507bdf51597eeb4" +checksum = "2b6f884fa9a8d48101774bfbd3aeb81e968dd22cffd19a372da69f183db22c1a" dependencies = [ "bitflags 2.13.0", ] @@ -9564,8 +9759,8 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skippy-cache" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "blake3", @@ -9574,40 +9769,41 @@ dependencies = [ [[package]] name = "skippy-coordinator" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "skippy-ffi" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "libloading 0.8.9", ] [[package]] name = "skippy-metrics" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" [[package]] name = "skippy-protocol" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "prost 0.14.4", "prost-build 0.14.4", "protoc-bin-vendored", "serde", + "skippy-tokenizer", ] [[package]] name = "skippy-runtime" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "anyhow", "libc", @@ -9618,10 +9814,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "skippy-scheduler" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "skippy-runtime", + "thiserror 2.0.18", +] + [[package]] name = "skippy-server" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "ahash", "anyhow", @@ -9632,6 +9837,9 @@ dependencies = [ "clap", "futures-util", "libc", + "mesh-llm-events", + "mesh-native-serving-plugin-api", + "model-artifact", "openai-frontend", "opentelemetry-proto", "serde", @@ -9641,16 +9849,27 @@ dependencies = [ "skippy-metrics", "skippy-protocol", "skippy-runtime", + "skippy-scheduler", + "skippy-tokenizer", + "skippy-topology", "socket2", "tokio", - "tokio-stream", "tonic", + "uuid", +] + +[[package]] +name = "skippy-tokenizer" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" +dependencies = [ + "serde", ] [[package]] name = "skippy-topology" -version = "0.74.0" -source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.74.0#e60b2fe43aa05271569fbeff2a457133aef456a1" +version = "0.76.0-rc9" +source = "git+https://github.com/Mesh-LLM/mesh-llm.git?tag=v0.76.0-rc9#9f192c9d821991ba43d79bb15fd2f2f12e597571" dependencies = [ "serde", "serde_json", @@ -9686,7 +9905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -10700,10 +10919,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -10725,7 +10944,7 @@ dependencies = [ "parking_lot", "rustix 1.1.4", "signal-hook 0.3.18", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -10971,6 +11190,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-happy-eyeballs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8564c32dfb6f4257f8bc6edfc178a34af97520e0b7b9815500c55eb3d092f29f" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.7.0" @@ -11039,9 +11267,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -11049,7 +11277,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite 0.28.0", "webpki-roots 0.26.11", ] @@ -11089,6 +11317,7 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52efb639344a7c6adb8e62c6f3d2c19c001ff1b79a5041ba1c6ed42e19c6aa5" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "bytes", "futures-core", @@ -11457,7 +11686,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -11479,9 +11708,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -11558,7 +11787,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -11699,6 +11928,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-time" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a939edecc3c5a7b83c02e5f6b3c31d2bc69eabcc9a87ab12c6d37ee6dbc856" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -11815,43 +12050,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" @@ -12163,15 +12361,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +checksum = "ef62a3d5f7b2411119a11b6f62570dbff91d7105e011a20fb83fbf8f5761c40f" dependencies = [ - "core-foundation 0.10.1", "jni 0.22.4", "log", "ndk-context", "objc2", + "objc2-app-kit", "objc2-foundation", "url", "web-sys", @@ -12403,7 +12601,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -13041,8 +13239,8 @@ dependencies = [ "log", "serde", "thiserror 2.0.18", - "windows 0.62.2", - "windows-core 0.62.2", + "windows 0.61.3", + "windows-core 0.61.2", ] [[package]] diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index bbf245e29a9..fb2a414bcee 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -108,15 +108,15 @@ buzz_agent_pkg = { package = "buzz-agent", path = "../../crates/buzz-agent" } buzz_voice_pkg = { package = "buzz-voice", path = "../../crates/buzz-voice" } buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" } 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.74.0", 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.74.0", 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-rc9", 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-rc9", 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.74.0", package = "mesh-llm-client", optional = true } -mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-node", optional = true } -mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-system", optional = true } -mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-events", optional = true } +mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-client", optional = true } +mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-node", optional = true } +mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-system", optional = true } +mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-events", optional = true } base64 = "0.22" sha2 = "0.11" tar = "0.4" diff --git a/desktop/src-tauri/src/managed_agents/global_config/tests.rs b/desktop/src-tauri/src/managed_agents/global_config/tests.rs index 553596e226c..51af43de478 100644 --- a/desktop/src-tauri/src/managed_agents/global_config/tests.rs +++ b/desktop/src-tauri/src/managed_agents/global_config/tests.rs @@ -503,7 +503,7 @@ fn inherited_shared_compute_translates_to_supported_agent_transport() { ); assert_eq!( effective.env.get("BUZZ_AGENT_MODEL").map(String::as_str), - Some("auto") + Some(super::super::RELAY_MESH_VIRTUAL_MODEL_ID) ); assert_eq!( effective diff --git a/desktop/src-tauri/src/managed_agents/relay_mesh.rs b/desktop/src-tauri/src/managed_agents/relay_mesh.rs index 5c246feedc5..64a47a64205 100644 --- a/desktop/src-tauri/src/managed_agents/relay_mesh.rs +++ b/desktop/src-tauri/src/managed_agents/relay_mesh.rs @@ -1,9 +1,95 @@ pub const RELAY_MESH_API_BASE_URL: &str = "http://127.0.0.1:9337/v1"; pub const RELAY_MESH_API_KEY_PLACEHOLDER: &str = "buzz-mesh-local"; pub const RELAY_MESH_PROVIDER_ID: &str = "relay-mesh"; +/// Stored value for "let the mesh decide", kept as the user-facing word. pub const RELAY_MESH_AUTO_MODEL_ID: &str = "auto"; +/// MeshLLM's virtual model. It resolves per request: a Mixture-of-Agents +/// committee when two or more workers are reachable, and otherwise degrades to +/// a single served model rather than erroring +/// (`moa_gateway::degrade_to_single_model`). That degradation is a pre-flight +/// capacity decision, so a committee that forms and *then* loses a worker still +/// surfaces as a failed turn — MoA repairs partial results internally +/// (`repair_tool_result_answer`) before it gets that far. Buzz translates the +/// stored `auto` here rather than teaching buzz-agent anything about meshes. #[cfg(feature = "mesh-llm")] -pub const RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV: &str = "BUZZ_AGENT_PREFER_MESH_FOR_AUTO"; +pub const RELAY_MESH_VIRTUAL_MODEL_ID: &str = "mesh"; + +/// The wire name for a stored shared-compute model: `auto` (and a blank legacy +/// value) means "let the mesh decide" and becomes MeshLLM's virtual `mesh` +/// model; anything else is a model the user named and is passed through. +/// +/// The single place this mapping happens. Every consumer that has to name a +/// model to the mesh — the LLM transport env and the ACP harness — goes through +/// here, so they cannot disagree. +#[cfg(feature = "mesh-llm")] +pub fn relay_mesh_wire_model(stored: &str) -> &str { + match stored.trim() { + "" | RELAY_MESH_AUTO_MODEL_ID => RELAY_MESH_VIRTUAL_MODEL_ID, + named => named, + } +} + +/// Keep the harness model aligned with the LLM transport's wire model. +/// With no effective model, remove any stale inherited override. +pub fn apply_acp_prompt_and_model_env( + command: &mut std::process::Command, + prompt: Option<&str>, + effective_model: Option<&str>, + #[cfg(feature = "mesh-llm")] mesh_model: Option<&str>, +) { + #[cfg(feature = "mesh-llm")] + let effective_model = mesh_model.map(relay_mesh_wire_model).or(effective_model); + if let Some(prompt) = prompt { + command.env("BUZZ_ACP_SYSTEM_PROMPT", prompt); + } else { + command.env_remove("BUZZ_ACP_SYSTEM_PROMPT"); + } + if let Some(model) = effective_model { + command.env("BUZZ_ACP_MODEL", model); + } else { + command.env_remove("BUZZ_ACP_MODEL"); + } +} + +#[cfg(test)] +mod acp_model_tests { + use super::*; + + #[test] + fn acp_model_preserves_named_models_and_removes_stale_override() { + let mut command = std::process::Command::new("unused"); + for model in [Some("named-model"), None] { + apply_acp_prompt_and_model_env( + &mut command, + model, + model, + #[cfg(feature = "mesh-llm")] + None, + ); + for key in ["BUZZ_ACP_MODEL", "BUZZ_ACP_SYSTEM_PROMPT"] { + let value = command.get_envs().find(|(env_key, _)| *env_key == key); + assert_eq!( + value.map(|(_, value)| value), + Some(model.map(std::ffi::OsStr::new)) + ); + } + } + } + + #[cfg(feature = "mesh-llm")] + #[test] + fn acp_model_uses_same_mesh_wire_mapping_as_llm_transport() { + for stored in ["auto", "", "named-model"] { + let mut command = std::process::Command::new("unused"); + apply_acp_prompt_and_model_env(&mut command, None, Some("fallback"), Some(stored)); + let value = command.get_envs().find(|(key, _)| *key == "BUZZ_ACP_MODEL"); + assert_eq!( + value.and_then(|(_, value)| value), + Some(std::ffi::OsStr::new(relay_mesh_wire_model(stored))) + ); + } + } +} /// Translate the native Buzz shared compute provider into the OpenAI-compatible /// transport understood by buzz-agent. These are derived runtime details, not @@ -17,11 +103,7 @@ pub fn apply_relay_mesh_env( if provider.map(str::trim) != Some(RELAY_MESH_PROVIDER_ID) { return; } - let model = model - .map(str::trim) - .filter(|value| !value.is_empty()) - .unwrap_or(RELAY_MESH_AUTO_MODEL_ID) - .to_string(); + let model = relay_mesh_wire_model(model.unwrap_or(RELAY_MESH_AUTO_MODEL_ID)).to_string(); env.insert("BUZZ_AGENT_PROVIDER".to_string(), "openai".to_string()); env.insert("BUZZ_AGENT_MODEL".to_string(), model.clone()); env.insert( @@ -34,14 +116,6 @@ pub fn apply_relay_mesh_env( RELAY_MESH_API_KEY_PLACEHOLDER.to_string(), ); env.insert("OPENAI_COMPAT_API".to_string(), "chat".to_string()); - // Buzz owns the meaning of relay-mesh `auto`: buzz-agent dynamically uses - // mesh-llm's virtual Mixture-of-Agents model whenever the live catalog says - // at least two distinct models are available, and otherwise keeps the - // router's normal single-model `auto` behavior. - env.insert( - RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV.to_string(), - "1".to_string(), - ); // Keep the requested response inside smaller local-model context windows. // These are defaults, not policy: the effective agent/persona/global env // may deliberately choose a smaller cap or a different effort. This function @@ -128,10 +202,76 @@ mod tests { // stops gemma tool-calling; enabling thinking makes Qwen3 burn ~4x the // output budget). assert_eq!(env.get("BUZZ_AGENT_THINKING_EFFORT"), None); + } + + /// Stored `auto` is translated here, so buzz-agent receives a plain model + /// name and needs no knowledge of the mesh. MeshLLM decides per request + /// whether `mesh` becomes a committee or a single served model. + #[test] + fn stored_auto_becomes_the_virtual_mesh_model_on_the_wire() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some(RELAY_MESH_AUTO_MODEL_ID), + ); + assert_eq!( - env.get(RELAY_MESH_PREFER_MESH_FOR_AUTO_ENV) - .map(String::as_str), - Some("1") + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + assert_eq!( + env.get("OPENAI_COMPAT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + } + + /// A blank stored model is the legacy encoding of the same intent. + #[test] + fn blank_stored_model_becomes_the_virtual_mesh_model() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env(&mut env, Some(RELAY_MESH_PROVIDER_ID), Some(" ")); + + assert_eq!( + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some(RELAY_MESH_VIRTUAL_MODEL_ID) + ); + } + + /// Every consumer that names a model to the mesh goes through one helper, + /// so the LLM transport and the ACP harness cannot be told different things. + #[test] + fn wire_model_maps_auto_and_blank_but_passes_named_through() { + assert_eq!( + relay_mesh_wire_model(RELAY_MESH_AUTO_MODEL_ID), + RELAY_MESH_VIRTUAL_MODEL_ID + ); + assert_eq!(relay_mesh_wire_model(""), RELAY_MESH_VIRTUAL_MODEL_ID); + assert_eq!(relay_mesh_wire_model(" "), RELAY_MESH_VIRTUAL_MODEL_ID); + assert_eq!( + relay_mesh_wire_model("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M"), + "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M" + ); + } + + /// A named model is sent verbatim: picking one is an explicit choice to + /// bypass mesh routing, and must not be rewritten. + #[test] + fn a_named_model_is_sent_verbatim() { + let mut env = BTreeMap::new(); + apply_relay_mesh_env( + &mut env, + Some(RELAY_MESH_PROVIDER_ID), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M"), + ); + + assert_eq!( + env.get("BUZZ_AGENT_MODEL").map(String::as_str), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M") + ); + assert_eq!( + env.get("OPENAI_COMPAT_MODEL").map(String::as_str), + Some("unsloth/Qwen3-8B-GGUF:Q4_K_M") ); } diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index 7afd80d1d68..d01100a1cbd 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -747,16 +747,13 @@ pub fn spawn_agent_child( let effective_model = effective_cfg.model.value; let effective_provider = effective_cfg.provider.value; - if let Some(prompt) = &effective_prompt { - command.env("BUZZ_ACP_SYSTEM_PROMPT", prompt); - } else { - command.env_remove("BUZZ_ACP_SYSTEM_PROMPT"); - } - if let Some(model) = effective_model.as_deref() { - command.env("BUZZ_ACP_MODEL", model); - } else { - command.env_remove("BUZZ_ACP_MODEL"); - } + super::apply_acp_prompt_and_model_env( + &mut command, + effective_prompt.as_deref(), + effective_model.as_deref(), + #[cfg(feature = "mesh-llm")] + mesh_model_id.as_deref(), + ); // Session title for the harness to pass out-of-band on `session/new`. The // adapter names the session after it; it never reaches the prompt, so this // is display metadata only. The spawn-config snapshot records the same diff --git a/desktop/src-tauri/src/mesh_llm/mod.rs b/desktop/src-tauri/src/mesh_llm/mod.rs index e206c53886a..daab3c4b1c9 100644 --- a/desktop/src-tauri/src/mesh_llm/mod.rs +++ b/desktop/src-tauri/src/mesh_llm/mod.rs @@ -34,9 +34,9 @@ mod usage; pub use usage::{serving_usage_from_payload, MeshServingUsage}; mod transport_policy; +use transport_policy::{iroh_relay_mode, sdk_iroh_relay_config, validate_advertised_endpoint}; #[cfg(test)] -use transport_policy::iroh_relay_mode_from; -use transport_policy::{iroh_relay_mode, validate_advertised_endpoint, IrohRelayMode}; +use transport_policy::{iroh_relay_mode_from, IrohRelayMode}; use mesh_llm_sdk::{client, serve, EmbeddedNodeHandle, MeshDiscoveryMode, TrustPolicy}; use serde::{Deserialize, Serialize}; @@ -376,13 +376,10 @@ impl DesktopMeshRuntime { if let Some(mesh_name) = request.mesh_name.as_deref() { builder = builder.mesh_name(mesh_name); } - builder = match iroh_relay_mode()? { - IrohRelayMode::Disabled => builder.disable_iroh_relays(true), - IrohRelayMode::Default => builder.disable_iroh_relays(false), - IrohRelayMode::Custom(urls) => builder - .disable_iroh_relays(false) - .iroh_relays(urls.into_iter().map(|url| url.to_string())), - }; + let (disable_iroh_relays, iroh_relays) = sdk_iroh_relay_config(iroh_relay_mode()?); + builder = builder + .disable_iroh_relays(disable_iroh_relays) + .iroh_relays(iroh_relays); if let Some(max_vram_gb) = request.max_vram_gb { builder = builder.max_vram_gb(max_vram_gb as f64); } @@ -416,13 +413,10 @@ impl DesktopMeshRuntime { if let Some(mesh_name) = request.mesh_name.as_deref() { builder = builder.mesh_name(mesh_name); } - builder = match iroh_relay_mode()? { - IrohRelayMode::Disabled => builder.disable_iroh_relays(true), - IrohRelayMode::Default => builder.disable_iroh_relays(false), - IrohRelayMode::Custom(urls) => builder - .disable_iroh_relays(false) - .iroh_relays(urls.into_iter().map(|url| url.to_string())), - }; + let (disable_iroh_relays, iroh_relays) = sdk_iroh_relay_config(iroh_relay_mode()?); + builder = builder + .disable_iroh_relays(disable_iroh_relays) + .iroh_relays(iroh_relays); if let Some(join_token) = request.join_token.as_deref() { builder = builder.join_token(join_token); } diff --git a/desktop/src-tauri/src/mesh_llm/transport_policy.rs b/desktop/src-tauri/src/mesh_llm/transport_policy.rs index 122bffd60e4..24e5156295a 100644 --- a/desktop/src-tauri/src/mesh_llm/transport_policy.rs +++ b/desktop/src-tauri/src/mesh_llm/transport_policy.rs @@ -43,6 +43,22 @@ pub(super) fn iroh_relay_mode_from(raw: Option<&str>) -> anyhow::Result (bool, Vec) { + match mode { + IrohRelayMode::Disabled => (true, Vec::new()), + IrohRelayMode::Default => ( + false, + MESH_LLM_DEFAULT_RELAYS + .iter() + .map(|url| (*url).to_string()) + .collect(), + ), + IrohRelayMode::Custom(urls) => { + (false, urls.into_iter().map(|url| url.to_string()).collect()) + } + } +} + fn parse_configured_relay_url(raw: &str) -> anyhow::Result { let parsed = url::Url::parse(raw) .map_err(|error| anyhow::anyhow!("invalid relay URL {raw:?}: {error}"))?; @@ -205,9 +221,11 @@ fn validate_transport(transport: &TransportAddr, mode: &IrohRelayMode) -> anyhow /// mesh-llm serving node (they are not in iroh's own prod relay map). /// /// Kept in sync with `effective_relay_urls(RelayPolicy::DefaultPublic, &[])`. -const MESH_LLM_DEFAULT_RELAYS: &[&str] = &[ +pub(super) const MESH_LLM_DEFAULT_RELAYS: &[&str] = &[ "https://usw1-2.relay.michaelneale.mesh-llm.iroh.link./", "https://aps1-1.relay.michaelneale.mesh-llm.iroh.link./", + "https://euc1-1.relay.michaelneale.mesh-llm.iroh.link./", + "https://use1-1.relay.michaelneale.mesh-llm.iroh.link./", ]; /// Whether `relay` is one of mesh-llm's baked-in default public relays. @@ -327,6 +345,27 @@ mod tests { } } + #[test] + fn sdk_default_relays_are_exactly_the_trusted_default_relays() { + let (disabled, configured) = sdk_iroh_relay_config(IrohRelayMode::Default); + assert!(!disabled); + let configured = configured + .into_iter() + .map(|url| url.parse::().expect("SDK relay URL must parse")) + .collect::>(); + let trusted = MESH_LLM_DEFAULT_RELAYS + .iter() + .map(|url| { + url.parse::() + .expect("trusted relay URL must parse") + }) + .collect::>(); + assert_eq!(configured, trusted); + assert!(configured + .iter() + .all(|relay| relay_allowed(relay, &IrohRelayMode::Default))); + } + #[test] fn endpoint_with_one_good_and_one_junk_candidate_is_sanitized() { // A mesh-llm endpoint can advertise a usable relay alongside an diff --git a/docs/superpowers/plans/2026-09-09-upstream-intake.md b/docs/superpowers/plans/2026-09-09-upstream-intake.md new file mode 100644 index 00000000000..d02f817d7fe --- /dev/null +++ b/docs/superpowers/plans/2026-09-09-upstream-intake.md @@ -0,0 +1,15 @@ +# Upstream factory reliability intake + +Goal: bring selected upstream reliability fixes onto factory baseline 50d96668, resolve integration blockers and verify the installed harness. Preserve fork lifecycle protections and canonical agent policy. Rust builds run on ci1 as buzzagent. + +- [x] U1: adapt #6950 memory CLI guidance and isolated seeded-memory evaluation to the fork's DONE runtime. Fixture validation is not a live model success measurement. +- [x] U2: backport #7010 receive-boundary verification. Demonstrate tampered-content failure before fix; verify before routing, queues and replay state. +- [x] U3: backport #7325 targeted, capacity-aware replay. Retain best-effort replay semantics and existing bounded writes. +- [x] Independently verify clean b49f12df: 767 ACP tests, fmt, clippy; Python 41 tests. Build and verify candidate artifact without installing while gates fail. +- [ ] Resolve Security findings using patched h2/webbrowser and the upstream #6189 MeshLLM/Nostr migration, including mesh-enabled desktop compatibility. +- [ ] Route Docker build/cache/provenance to this fork's configured GHCR namespaces; retain build and publication gates. +- [ ] Run final scoped tests and GitHub CI on immutable final commit, independently review the delta, and integrate PR14 only with passing required checks. +- [ ] Build final immutable sprig, verify archive/manifest identity, install at safe idle boundaries, and verify all active seat processes use the expected executable. Keep rollback artifact and retired Codex disabled. +- [ ] Update reliability ledger and installation receipt with final source, checks and actual runtime state. + +U4 thread-session migration remains separate; do not enable it without its base implementation and both follow-up fixes. No product or clinical behavior changes, live test messages, or product promotion are part of this harness intake. diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 0e5946ca186..80a45a8cc8b 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -90,6 +90,9 @@ run_unit_tests() { run_test_step "buzz-cli tests" \ cargo test -p buzz-cli -- --nocapture + run_test_step "buzz-acp receive-boundary and lifecycle tests" \ + cargo test -p buzz-acp -- --nocapture + # buzz-db migrator/lint unit tests (no infra): guard the embedded-migrator # invariant (exactly the consolidated 0001; cutover/backfill stays an operator # script, not startup state) and the tenant-scoping lints. The Postgres-backed