diff --git a/CLAUDE.md b/CLAUDE.md index 92216415..803ba24a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -203,23 +203,6 @@ hive-forge/ Forgejo CLI wrapper (`hive-forge` binary) attach-comment, lint). Replaces the 600-line hive-forge-tools.nix bash script (closes #280). -hive-matrix-mcp/ per-agent matrix-sdk integration (#548 phase 3). - src/main.rs `hive-matrix-daemon` binary entry — long-running - matrix-sdk Client + sync per agent; serves the - MCP bridge over /run/hive-matrix.sock; emits - hyperhive wake on incoming room events. - src/bin/mcp.rs `hive-matrix-mcp` stdio bridge claude spawns per - turn — connects to the daemon socket, forwards - each tool call (send_message, send_dm, - send_reaction, send_reply, mark_read, list_rooms, - list_room_members, read_room). No matrix-sdk dep - at this entrypoint (pure serde + tokio I/O). - src/{client,handlers,socket,timeline,wake,protocol,paths}.rs - shared library modules (matrix-sdk session - restore, request dispatch, unix socket server, - event handler → wake bridge, wire types, env - resolution). - hive-sh4re/ wire types (HostRequest/Response, AgentRequest/Response, ManagerRequest/Response, Message, Approval, HelperEvent) diff --git a/Cargo.lock b/Cargo.lock index ab9dfd4b..06da1bec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,33 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.2.17", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -103,105 +76,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - -[[package]] -name = "aquamarine" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" -dependencies = [ - "include_dir", - "itertools 0.10.5", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "archery" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -dependencies = [ - "serde", -] - -[[package]] -name = "as_variant" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbc3a507a82b17ba0d98f6ce8fd6954ea0c8152e98009d36a40d8dcc8ce078a" - -[[package]] -name = "assign" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" -dependencies = [ - "compression-codecs", - "compression-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-trait" version = "0.1.89" @@ -280,57 +154,17 @@ dependencies = [ "tracing", ] -[[package]] -name = "backon" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" -dependencies = [ - "fastrand", - "gloo-timers", - "tokio", -] - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - [[package]] name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" -dependencies = [ - "serde_core", -] - -[[package]] -name = "bitmaps" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" - -[[package]] -name = "blake3" -version = "1.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "cpufeatures 0.3.0", -] [[package]] name = "block-buffer" @@ -341,15 +175,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "bumpalo" version = "3.20.2" @@ -362,21 +187,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -[[package]] -name = "bytesize" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.62" @@ -399,30 +209,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.2.17", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", - "zeroize", -] - [[package]] name = "chrono" version = "0.4.44" @@ -430,24 +216,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", - "js-sys", "num-traits", "serde", - "wasm-bindgen", "windows-link", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - [[package]] name = "clap" version = "4.6.1" @@ -494,53 +267,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" -[[package]] -name = "compression-codecs" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" -dependencies = [ - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_panic" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" -dependencies = [ - "typewit", -] - -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -556,30 +282,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "crypto-common" version = "0.1.7" @@ -587,38 +289,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", - "rand_core 0.6.4", "typenum", ] -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "serde", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "darling" version = "0.23.0" @@ -659,82 +332,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" -[[package]] -name = "date_header" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c03c416ed1a30fbb027ef484ba6ab6f80e1eada675e1a2b92fd673c045a1f1d" - -[[package]] -name = "deadpool" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" -dependencies = [ - "deadpool-runtime", - "lazy_static", - "num_cpus", - "tokio", -] - -[[package]] -name = "deadpool-runtime" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" -dependencies = [ - "tokio", -] - -[[package]] -name = "deadpool-sqlite" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8510000b26f632483a35120c2ce280c29e1e14c2dcb27b5055dbdac276f63f58" -dependencies = [ - "deadpool", - "deadpool-sync", - "rusqlite", -] - -[[package]] -name = "deadpool-sync" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524bc3df0d57e98ecd022e21ba31166c2625e7d3e5bcc4510efaeeab4abcab04" -dependencies = [ - "deadpool-runtime", -] - -[[package]] -name = "decancer" -version = "3.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9244323129647178bf41ac861a2cdb9d9c81b9b09d3d0d1de9cd302b33b8a1d" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - [[package]] name = "digest" version = "0.10.7" @@ -743,7 +340,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] @@ -763,38 +359,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "serde", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core 0.6.4", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" - [[package]] name = "equivalent" version = "1.0.2" @@ -811,53 +375,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "eyeball" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93bd0ebf93d61d6332d3c09a96e97975968a44e19a64c947bde06e6baff383f" -dependencies = [ - "futures-core", - "readlock", - "readlock-tokio", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "eyeball-im" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e8e9d31591be508826b875d8fe6056aebcaec3281ac0e45434ff303686c566" -dependencies = [ - "futures-core", - "imbl", - "tokio", - "tracing", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -876,34 +393,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foldhash" version = "0.1.5" @@ -919,16 +414,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "futures" version = "0.3.32" @@ -1067,49 +552,6 @@ dependencies = [ "wasip3", ] -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "growable-bloom-filter" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" -dependencies = [ - "serde", - "serde_bytes", - "serde_derive", - "xxhash-rust", -] - -[[package]] -name = "h2" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -1134,42 +576,12 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - [[package]] name = "hive-ag3nt" version = "0.1.0" @@ -1226,23 +638,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "hive-matrix-mcp" -version = "0.1.0" -dependencies = [ - "anyhow", - "futures-util", - "matrix-sdk", - "reqwest", - "rmcp", - "schemars", - "serde", - "serde_json", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "hive-sh4re" version = "0.1.0" @@ -1250,36 +645,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "html5ever" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" -dependencies = [ - "log", - "mac", - "markup5ever", - "match_token", -] - [[package]] name = "http" version = "1.4.0" @@ -1290,15 +655,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-auth" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" -dependencies = [ - "memchr", -] - [[package]] name = "http-body" version = "1.0.1" @@ -1350,7 +706,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -1540,49 +895,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "imbl" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4308a675e4cfc1920f36a8f4d8fb62d5533b7da106844bd1ec51c6f1fa94a0c" -dependencies = [ - "archery", - "bitmaps", - "imbl-sized-chunks", - "rand_core 0.9.5", - "rand_xoshiro", - "serde", - "version_check", -] - -[[package]] -name = "imbl-sized-chunks" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4241005618a62f8d57b2febd02510fb96e0137304728543dfc5fd6f052c22d" -dependencies = [ - "bitmaps", -] - -[[package]] -name = "include_dir" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" -dependencies = [ - "include_dir_macros", -] - -[[package]] -name = "include_dir_macros" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "indexmap" version = "2.14.0" @@ -1595,16 +907,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -1617,24 +919,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.18" @@ -1653,50 +937,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "js_int" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d937f95470b270ce8b8950207715d71aa8e153c0d44c6684d59397ed4949160a" -dependencies = [ - "serde", -] - -[[package]] -name = "js_option" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68421373957a1593a767013698dbf206e2b221eefe97a44d98d18672ff38423c" -dependencies = [ - "serde", -] - -[[package]] -name = "konst" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97feab15b395d1860944abe6a8dd8ed9f8eadfae01750fada8427abda531d887" -dependencies = [ - "const_panic", - "konst_kernel", - "typewit", -] - -[[package]] -name = "konst_kernel" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" -dependencies = [ - "typewit", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.5.0" @@ -1738,15 +978,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -1759,43 +990,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "markup5ever" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" -dependencies = [ - "log", - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "match_token" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "matchers" version = "0.2.0" @@ -1811,179 +1005,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" -[[package]] -name = "matrix-pickle" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c34e6db65145740459f2ca56623b40cd4e6000ffae2a7d91515fa82aa935dbf" -dependencies = [ - "matrix-pickle-derive", - "thiserror 2.0.18", -] - -[[package]] -name = "matrix-pickle-derive" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a962fc9981f823f6555416dcb2ae9ae67ca412d767ee21ecab5150113ee6285b" -dependencies = [ - "proc-macro-crate", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "matrix-sdk" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfa71339f867dcada2e7f1130f858fd8892088b1a4c123dd50a99ed2399ab22" -dependencies = [ - "anymap2", - "aquamarine", - "as_variant", - "async-channel", - "async-stream", - "async-trait", - "backon", - "bytes", - "bytesize", - "cfg-if", - "event-listener", - "eyeball", - "eyeball-im", - "futures-core", - "futures-util", - "gloo-timers", - "http", - "imbl", - "indexmap", - "itertools 0.14.0", - "js_int", - "language-tags", - "matrix-sdk-base", - "matrix-sdk-common", - "matrix-sdk-sqlite", - "mime", - "mime2ext", - "oauth2", - "once_cell", - "percent-encoding", - "pin-project-lite", - "reqwest", - "ruma", - "serde", - "serde_html_form", - "serde_json", - "sha2", - "tempfile", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "url", - "urlencoding", - "vodozemac", - "zeroize", -] - -[[package]] -name = "matrix-sdk-base" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b14659a7e902ea8a821ec217f36b168fb4c79020d91b912175ac188b6c364225" -dependencies = [ - "as_variant", - "async-trait", - "bitflags", - "decancer", - "eyeball", - "eyeball-im", - "futures-util", - "growable-bloom-filter", - "matrix-sdk-common", - "matrix-sdk-store-encryption", - "once_cell", - "regex", - "ruma", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "unicode-normalization", -] - -[[package]] -name = "matrix-sdk-common" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb33a986495135e217f28cfe0918bf7d01a9800e42f4ef88afbd48e23b8cc53" -dependencies = [ - "eyeball-im", - "futures-core", - "futures-executor", - "futures-util", - "gloo-timers", - "imbl", - "ruma", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "tracing-subscriber", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "matrix-sdk-sqlite" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662a128004b553196365d88ea46917047d7350a24dfe7ce829e7b78636673604" -dependencies = [ - "as_variant", - "async-trait", - "deadpool-sqlite", - "itertools 0.14.0", - "matrix-sdk-base", - "matrix-sdk-store-encryption", - "num_cpus", - "rmp-serde", - "ruma", - "rusqlite", - "serde", - "serde_json", - "serde_path_to_error", - "thiserror 2.0.18", - "tokio", - "tracing", - "vodozemac", -] - -[[package]] -name = "matrix-sdk-store-encryption" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0aac550e685306fbbd57faae8f98af9812f19bbf0f83da7559d67cf4789679" -dependencies = [ - "base64", - "blake3", - "chacha20poly1305", - "hmac", - "pbkdf2", - "rand 0.8.6", - "rmp-serde", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.18", - "zeroize", -] - [[package]] name = "memchr" version = "2.8.0" @@ -1996,12 +1017,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime2ext" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf6f36070878c42c5233846cd3de24cf9016828fd47bc22957a687298bb21fc" - [[package]] name = "mime_guess" version = "2.0.5" @@ -2012,16 +1027,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "mio" version = "1.2.0" @@ -2033,12 +1038,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2048,12 +1047,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - [[package]] name = "num-traits" version = "0.2.19" @@ -2063,36 +1056,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "oauth2" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" -dependencies = [ - "base64", - "chrono", - "getrandom 0.2.17", - "http", - "rand 0.8.6", - "reqwest", - "serde", - "serde_json", - "serde_path_to_error", - "sha2", - "thiserror 1.0.69", - "url", -] - [[package]] name = "once_cell" version = "1.21.4" @@ -2105,134 +1068,30 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - [[package]] name = "pastey" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5a797f0e07bdf071d15742978fc3128ec6c22891c31a3a931513263904c982a" -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.6", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures 0.2.17", - "opaque-debug", - "universal-hash", -] - [[package]] name = "potential_utf" version = "0.1.5" @@ -2242,12 +1101,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2257,12 +1110,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "prettyplease" version = "0.2.37" @@ -2273,36 +1120,6 @@ dependencies = [ "syn", ] -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -2312,47 +1129,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pulldown-cmark" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" -dependencies = [ - "bitflags", - "memchr", - "pulldown-cmark-escape", - "unicase", -] - -[[package]] -name = "pulldown-cmark-escape" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" - [[package]] name = "quinn" version = "0.11.9" @@ -2367,7 +1143,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.18", + "thiserror", "tokio", "tracing", "web-time", @@ -2382,13 +1158,13 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand 0.9.4", + "rand", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.18", + "thiserror", "tinyvec", "tracing", "web-time", @@ -2429,35 +1205,14 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "rand_chacha", + "rand_core", ] [[package]] @@ -2467,16 +1222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", + "rand_core", ] [[package]] @@ -2488,39 +1234,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rand_xoshiro" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" -dependencies = [ - "rand_core 0.9.5", -] - -[[package]] -name = "readlock" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da6f291b23556edd9edaf655a0be2ad8ef8002ff5f1bca62b264f3f58b53f34" - -[[package]] -name = "readlock-tokio" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7e264f9ec4f3d112e8e2f214e8e7cb5cf3b83278f3570b7e00bfe13d3bd8ff" -dependencies = [ - "tokio", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - [[package]] name = "ref-cast" version = "1.0.25" @@ -2541,18 +1254,6 @@ dependencies = [ "syn", ] -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - [[package]] name = "regex-automata" version = "0.4.14" @@ -2581,7 +1282,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "http-body-util", @@ -2602,14 +1302,12 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls", - "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", "webpki-roots", ] @@ -2643,7 +1341,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror", "tokio", "tokio-util", "tracing", @@ -2662,201 +1360,6 @@ dependencies = [ "syn", ] -[[package]] -name = "rmp" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "rmp-serde" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" -dependencies = [ - "rmp", - "serde", -] - -[[package]] -name = "ruma" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b698b728bc3747f564a9115c83b4f2e229b52377f6a1cca2e6add9cf4a13be" -dependencies = [ - "assign", - "js_int", - "js_option", - "ruma-client-api", - "ruma-common", - "ruma-events", - "ruma-federation-api", - "ruma-html", - "ruma-signatures", - "web-time", -] - -[[package]] -name = "ruma-client-api" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b54e56c591f9ad686defb0bacbebba5c8882eb0c9f8734f6a080345b4e3dd941" -dependencies = [ - "as_variant", - "assign", - "bytes", - "date_header", - "http", - "js_int", - "js_option", - "maplit", - "ruma-common", - "ruma-events", - "serde", - "serde_html_form", - "serde_json", - "thiserror 2.0.18", - "url", - "web-time", -] - -[[package]] -name = "ruma-common" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7f59b9f7639667d0d6ae3ae242c8912e9ed061cea1fbaf72710a402e83b53e" -dependencies = [ - "as_variant", - "base64", - "bytes", - "form_urlencoded", - "getrandom 0.2.17", - "http", - "indexmap", - "js_int", - "konst", - "percent-encoding", - "rand 0.8.6", - "regex", - "ruma-identifiers-validation", - "ruma-macros", - "serde", - "serde_html_form", - "serde_json", - "thiserror 2.0.18", - "time", - "tracing", - "url", - "uuid", - "web-time", - "wildmatch", - "zeroize", -] - -[[package]] -name = "ruma-events" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34fa815769ed4fe1ef5b50aa0ba6f350317c13b5a9f1e008b014f4a3ddf14204" -dependencies = [ - "as_variant", - "indexmap", - "js_int", - "js_option", - "percent-encoding", - "pulldown-cmark", - "regex", - "ruma-common", - "ruma-identifiers-validation", - "ruma-macros", - "serde", - "serde_json", - "thiserror 2.0.18", - "tracing", - "url", - "web-time", - "wildmatch", - "zeroize", -] - -[[package]] -name = "ruma-federation-api" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbc887ba1292e48e6363b29e0dec4571b52d2b5102ebf60068105efadaa6e0a" -dependencies = [ - "headers", - "http", - "http-auth", - "httparse", - "js_int", - "memchr", - "mime", - "ruma-common", - "ruma-events", - "serde", - "serde_json", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "ruma-html" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6124d74847ea788601477c89a44485894432a806824cae93885c5825a8ae9dbc" -dependencies = [ - "as_variant", - "html5ever", - "tracing", - "wildmatch", -] - -[[package]] -name = "ruma-identifiers-validation" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a7b93ac1e571c585f8fa5cef09c07bb8a15529775fd56b9a3eac4f9233dff2" -dependencies = [ - "js_int", - "thiserror 2.0.18", -] - -[[package]] -name = "ruma-macros" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9911c7188517f28505d2d513339511d00e0f50cec5c2dde820cd0ec7e6a833" -dependencies = [ - "cfg-if", - "proc-macro-crate", - "proc-macro2", - "quote", - "ruma-identifiers-validation", - "serde", - "syn", - "toml", -] - -[[package]] -name = "ruma-signatures" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47cd146d56ae6e7a4a8d912a30dfe57c70e5bf18806fdf617527d4d4f2dd2a4" -dependencies = [ - "base64", - "ed25519-dalek", - "pkcs8", - "rand 0.8.6", - "ruma-common", - "serde_json", - "sha2", - "thiserror 2.0.18", -] - [[package]] name = "rusqlite" version = "0.37.0" @@ -2877,15 +1380,6 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "1.1.4" @@ -2972,12 +1466,6 @@ dependencies = [ "syn", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.28" @@ -2994,16 +1482,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde_core" version = "1.0.228" @@ -3035,19 +1513,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_html_form" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" -dependencies = [ - "form_urlencoded", - "indexmap", - "itoa", - "ryu", - "serde_core", -] - [[package]] name = "serde_json" version = "1.0.149" @@ -3072,15 +1537,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3100,18 +1556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures", "digest", ] @@ -3140,27 +1585,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "siphasher" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" - [[package]] name = "slab" version = "0.4.12" @@ -3183,47 +1607,12 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "string_cache" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", -] - [[package]] name = "strsim" version = "0.11.1" @@ -3280,44 +1669,13 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -3340,25 +1698,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde_core", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - [[package]] name = "tinystr" version = "0.8.3" @@ -3458,70 +1797,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "winnow 0.7.15", -] - -[[package]] -name = "toml_edit" -version = "0.25.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" -dependencies = [ - "indexmap", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.3", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow 1.0.3", -] - [[package]] name = "tower" version = "0.5.3" @@ -3544,7 +1819,6 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "async-compression", "bitflags", "bytes", "futures-core", @@ -3657,9 +1931,9 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.4", + "rand", "sha1", - "thiserror 2.0.18", + "thiserror", ] [[package]] @@ -3668,21 +1942,6 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" -[[package]] -name = "typewit" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214ca0b2191785cbc06209b9ca1861e048e39b5ba33574b3cedd58363d5bb5f6" -dependencies = [ - "typewit_proc_macros", -] - -[[package]] -name = "typewit_proc_macros" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" - [[package]] name = "unicase" version = "2.9.0" @@ -3695,31 +1954,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "untrusted" version = "0.9.0" @@ -3736,21 +1976,8 @@ dependencies = [ "idna", "percent-encoding", "serde", - "serde_derive", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -3763,17 +1990,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" -dependencies = [ - "getrandom 0.4.2", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -3792,36 +2008,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "vodozemac" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022a277687e4e8685d72b95a7ca3ccfec907daa946678e715f8badaa650883d" -dependencies = [ - "aes", - "arrayvec", - "base64", - "base64ct", - "cbc", - "chacha20poly1305", - "curve25519-dalek", - "ed25519-dalek", - "getrandom 0.2.17", - "hkdf", - "hmac", - "matrix-pickle", - "prost", - "rand 0.8.6", - "serde", - "serde_bytes", - "serde_json", - "sha2", - "subtle", - "thiserror 2.0.18", - "x25519-dalek", - "zeroize", -] - [[package]] name = "want" version = "0.3.1" @@ -3932,19 +2118,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasmparser" version = "0.244.0" @@ -3986,12 +2159,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "wildmatch" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29333c3ea1ba8b17211763463ff24ee84e41c78224c16b001cd907e663a38c68" - [[package]] name = "windows-core" version = "0.62.2" @@ -4207,24 +2374,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -4325,24 +2474,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core 0.6.4", - "serde", - "zeroize", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" - [[package]] name = "yoke" version = "0.8.2" @@ -4412,20 +2543,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "zerotrie" diff --git a/Cargo.toml b/Cargo.toml index 327d3cbe..b990949f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" -members = ["hive-ag3nt", "hive-c0re", "hive-forge", "hive-matrix-mcp", "hive-sh4re"] +members = ["hive-ag3nt", "hive-c0re", "hive-forge", "hive-sh4re"] [workspace.package] edition = "2024" @@ -46,5 +46,3 @@ tokio-stream = { version = "0.1", features = ["sync"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } -matrix-sdk = { version = "0.14", default-features = false, features = ["rustls-tls", "sqlite", "markdown"] } -futures-util = "0.3" diff --git a/flake.nix b/flake.nix index 1161ae21..f6d43a86 100644 --- a/flake.nix +++ b/flake.nix @@ -93,14 +93,8 @@ # rsvg-convert call — that whole codepath moved into the # `hyperhive-assets` derivation in #555, so the rust # derivation no longer needs the dependency. - # `sqlite` required by matrix-sdk's `sqlite` feature - # (`hive-matrix-mcp` workspace member, #548 phase 3) — the - # matrix-sdk-sqlite + rusqlite stack links against system - # libsqlite3 by default. nativeBuildInputs = [ pkgs.git - pkgs.sqlite - pkgs.pkg-config ]; } ); diff --git a/hive-matrix-mcp/Cargo.toml b/hive-matrix-mcp/Cargo.toml deleted file mode 100644 index faa3fe1a..00000000 --- a/hive-matrix-mcp/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "hive-matrix-mcp" -edition.workspace = true -version.workspace = true - -[lints] -workspace = true - -[dependencies] -anyhow.workspace = true -futures-util.workspace = true -matrix-sdk.workspace = true -reqwest.workspace = true -rmcp.workspace = true -schemars.workspace = true -serde.workspace = true -serde_json.workspace = true -tokio.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true - -# `hive-matrix-daemon` — long-running per-agent matrix-sdk Client + -# sync loop. Holds the unix socket the stdio MCP bridge talks to and -# emits hyperhive wake signals on incoming room events. -[[bin]] -name = "hive-matrix-daemon" -path = "src/main.rs" - -# `hive-matrix-mcp` — thin stdio MCP bridge spawned by claude per turn. -# Forwards every tool call to the daemon over /run/hive-matrix.sock, -# returns the daemon's response shape to claude. No matrix-sdk dep at -# this entrypoint — the heavy crate only loads when the daemon binary -# is invoked. -[[bin]] -name = "hive-matrix-mcp" -path = "src/bin/mcp.rs" diff --git a/hive-matrix-mcp/src/bin/mcp.rs b/hive-matrix-mcp/src/bin/mcp.rs deleted file mode 100644 index 427f30db..00000000 --- a/hive-matrix-mcp/src/bin/mcp.rs +++ /dev/null @@ -1,264 +0,0 @@ -//! `hive-matrix-mcp` binary — stdio MCP server claude spawns per turn. -//! Thin protocol bridge: every tool call → connect to the daemon's -//! unix socket → write a JSON request line → read the JSON response → -//! return the payload (or the error message) to claude. -//! -//! No matrix-sdk dep at this entrypoint. The daemon owns the heavy -//! Client + sync; the bridge is pure serde + tokio I/O. Means the MCP -//! binary cold-starts in milliseconds even though the daemon takes -//! seconds to bring up sync. - -use anyhow::{Context, Result}; -use rmcp::{ - ServerHandler, ServiceExt, - handler::server::wrapper::Parameters, - schemars::{self, JsonSchema}, - tool, tool_handler, tool_router, - transport::stdio, -}; -use serde::Deserialize; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; -use tokio::net::UnixStream; - -use hive_matrix_mcp::paths; -use hive_matrix_mcp::protocol::{DaemonRequest, DaemonResponse}; - -/// Send `req` to the daemon and read back the response. Each call is a -/// fresh unix-socket connection — short-lived (the daemon dispatch is -/// a single round-trip) so connection pooling would be over-engineering. -async fn round_trip(req: DaemonRequest) -> Result { - let socket = paths::daemon_socket(); - let stream = UnixStream::connect(&socket) - .await - .with_context(|| format!("connect daemon socket {}", socket.display()))?; - let (reader, mut writer) = stream.into_split(); - let mut line = serde_json::to_string(&req)?; - line.push('\n'); - writer - .write_all(line.as_bytes()) - .await - .context("write request to daemon socket")?; - writer.shutdown().await.ok(); - let mut buf = String::new(); - BufReader::new(reader) - .read_line(&mut buf) - .await - .context("read response from daemon socket")?; - serde_json::from_str(&buf).context("parse daemon response") -} - -/// Turn a `DaemonResponse` into the string claude sees as the tool -/// result. Ok payloads are pretty-printed JSON; errors get a clear -/// "matrix error: …" prefix so claude can pattern-match on it. -fn render(resp: Result) -> String { - match resp { - Ok(DaemonResponse::Ok { payload }) => { - serde_json::to_string_pretty(&payload).unwrap_or_else(|_| payload.to_string()) - } - Ok(DaemonResponse::Error { message }) => format!("matrix error: {message}"), - Err(e) => format!("matrix bridge error: {e:#}"), - } -} - -#[derive(Debug, Deserialize, JsonSchema)] -struct SendMessageArgs { - /// Matrix room id (`!abc:server`) or canonical alias (`#name:server`). - /// Aliases are server-resolved at send time. - room: String, - /// Message body. Markdown is rendered to HTML by the daemon - /// (`text_markdown`); plain text passes through unchanged. - body: String, -} - -#[derive(Debug, Deserialize, JsonSchema)] -struct SendDmArgs { - /// Matrix user id of the recipient (`@user:server`). DM room is - /// created if one doesn't already exist between this agent and - /// the recipient. - user_id: String, - body: String, -} - -#[derive(Debug, Deserialize, JsonSchema)] -struct SendReactionArgs { - room: String, - /// Full matrix event id of the message being reacted to. - event_id: String, - /// Reaction key — usually an emoji (`👍`, `❤️`) but any string - /// works per matrix spec. - key: String, -} - -#[derive(Debug, Deserialize, JsonSchema)] -struct SendReplyArgs { - room: String, - event_id: String, - body: String, -} - -#[derive(Debug, Deserialize, JsonSchema)] -struct MarkReadArgs { - room: String, - event_id: String, -} - -#[derive(Debug, Deserialize, JsonSchema)] -struct ListRoomsArgs {} - -#[derive(Debug, Deserialize, JsonSchema)] -struct ListRoomMembersArgs { - room: String, -} - -#[derive(Debug, Deserialize, JsonSchema)] -struct ReadRoomArgs { - room: String, - /// Maximum events to return (default 50, max 200). Newest first. - #[serde(default)] - limit: Option, -} - -struct MatrixBridge { - #[allow(dead_code)] - tool_router: rmcp::handler::server::router::tool::ToolRouter, -} - -impl MatrixBridge { - fn new() -> Self { - Self { - tool_router: Self::tool_router(), - } - } -} - -#[tool_router] -impl MatrixBridge { - #[tool( - description = "Post a plain-text or markdown message to a matrix room. \ - `room` is either a room id (!abc:server) or alias (#name:server). \ - Returns the new event id." - )] - async fn send_message(&self, Parameters(args): Parameters) -> String { - render(round_trip(DaemonRequest::SendMessage { - room: args.room, - body: args.body, - }).await) - } - - #[tool( - description = "Open (or reuse) a direct message room with `user_id` \ - (@user:server) and post `body` to it." - )] - async fn send_dm(&self, Parameters(args): Parameters) -> String { - render(round_trip(DaemonRequest::SendDm { - user_id: args.user_id, - body: args.body, - }).await) - } - - #[tool( - description = "React to a specific matrix event with an emoji or short \ - string `key`. Matrix-spec annotation; renders as a reaction in \ - standard clients." - )] - async fn send_reaction(&self, Parameters(args): Parameters) -> String { - render(round_trip(DaemonRequest::SendReaction { - room: args.room, - event_id: args.event_id, - key: args.key, - }).await) - } - - #[tool( - description = "Reply to a specific matrix event in a room, threaded \ - via m.in_reply_to. Returns the reply's event id." - )] - async fn send_reply(&self, Parameters(args): Parameters) -> String { - render(round_trip(DaemonRequest::SendReply { - room: args.room, - event_id: args.event_id, - body: args.body, - }).await) - } - - #[tool( - description = "Mark a specific event as read for this agent. Updates \ - the room's unread indicator + sends a read receipt other \ - participants can see." - )] - async fn mark_read(&self, Parameters(args): Parameters) -> String { - render(round_trip(DaemonRequest::MarkRead { - room: args.room, - event_id: args.event_id, - }).await) - } - - #[tool( - description = "List rooms this agent has joined. Each row has the room \ - id, canonical alias (when set), display name, and joined-member count." - )] - async fn list_rooms(&self, Parameters(_): Parameters) -> String { - render(round_trip(DaemonRequest::ListRooms).await) - } - - #[tool( - description = "List the members of a matrix room (joined-state only). \ - Each row carries the user id and resolved display name." - )] - async fn list_room_members(&self, Parameters(args): Parameters) -> String { - render(round_trip(DaemonRequest::ListRoomMembers { room: args.room }).await) - } - - #[tool( - description = "Read the most recent N events from a matrix room \ - (default 50, max 200). Returns each event's id, sender, timestamp, \ - type, and best-effort plain-text body." - )] - async fn read_room(&self, Parameters(args): Parameters) -> String { - render(round_trip(DaemonRequest::ReadRoom { - room: args.room, - limit: args.limit, - }).await) - } -} - -#[tool_handler( - instructions = "Matrix client for an agent on a hyperhive swarm. Use \ - `send_message` to post in a joined room, `send_dm` to message a \ - specific user, `send_reaction` to react with an emoji, `send_reply` \ - to thread a reply, `mark_read` to acknowledge an event. Discover \ - rooms with `list_rooms`, members with `list_room_members`, recent \ - timeline with `read_room`. Room references accept ids (!abc:server) \ - or aliases (#name:server); user references use @user:server." -)] -impl ServerHandler for MatrixBridge {} - -#[tokio::main] -async fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("warn")), - ) - .with_writer(std::io::stderr) - .init(); - - // Standalone-degraded boot: missing daemon socket → exit 0 - // cleanly so claude doesn't see an MCP startup error when matrix - // hasn't been provisioned for this agent yet. The daemon will - // appear once hive-c0re writes the token + systemd starts the - // daemon unit. - let socket = paths::daemon_socket(); - if !tokio::fs::try_exists(&socket).await.unwrap_or(false) { - tracing::warn!( - path = %socket.display(), - "matrix daemon socket absent; exiting cleanly so MCP startup doesn't fail" - ); - return Ok(()); - } - - let bridge = MatrixBridge::new(); - let service = bridge.serve(stdio()).await.context("serve MCP over stdio")?; - service.waiting().await.context("MCP service exited unexpectedly")?; - Ok(()) -} diff --git a/hive-matrix-mcp/src/client.rs b/hive-matrix-mcp/src/client.rs deleted file mode 100644 index 50bf92a3..00000000 --- a/hive-matrix-mcp/src/client.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! matrix-sdk `Client` setup for the daemon: read the per-agent access -//! token from the state-dir file `hive-c0re::matrix::ensure_user_for` -//! wrote, probe `whoami` to recover the agent's matrix `user_id` + -//! `device_id`, restore the matrix-sdk session, return the Client ready -//! to start sync. -//! -//! No OAuth dance / cross-signing setup (in contrast to damocles-daemon's -//! ccc.de connection): for the in-hive tuwunel the `registration_token` -//! UIAA flow already minted the token + user/device, hive-c0re just -//! handed us the bearer in a file. matrix-sdk's `restore_session` with -//! a constructed `MatrixSession` skips the login flow entirely. - -use std::path::Path; - -use anyhow::{Context, Result, anyhow}; -use matrix_sdk::{ - Client, SessionMeta, SessionTokens, - authentication::matrix::MatrixSession, - ruma::{OwnedDeviceId, OwnedUserId}, -}; -use serde::Deserialize; -use tokio::fs; - -/// Subset of the `/_matrix/client/v3/account/whoami` response we care -/// about. matrix-spec field names; `device_id` is optional per spec -/// (servers MAY omit it for legacy bearer scopes) but tuwunel always -/// returns it. -#[derive(Debug, Deserialize)] -struct WhoamiResponse { - user_id: String, - device_id: Option, -} - -/// Build + restore a matrix-sdk `Client` for the per-agent bearer -/// token at `token_file`. The Client points at `homeserver`, persists -/// its sqlite cache under `state_dir`, and is ready for sync once -/// returned. -/// -/// Steps: -/// 1. Read the bearer token from `token_file` (trim trailing whitespace). -/// 2. Plain reqwest GET to `/_matrix/client/v3/account/whoami` with -/// the bearer — this gives us back the matrix `user_id` + -/// `device_id` (the registration response had them but hive-c0re -/// only persisted the token; whoami is the cheapest recovery path -/// and avoids matrix-sdk's circular requirement of needing a -/// session to call whoami). -/// 3. Build the real Client with the sqlite store + `restore_session` -/// using a synthetic `MatrixSession`. -pub async fn build_and_restore( - homeserver: &str, - token_file: &Path, - state_dir: &Path, -) -> Result { - let token = fs::read_to_string(token_file) - .await - .with_context(|| format!("read matrix token from {}", token_file.display()))? - .trim() - .to_owned(); - if token.is_empty() { - return Err(anyhow!( - "matrix token at {} is empty", - token_file.display() - )); - } - - let (user_id, device_id) = whoami(homeserver, &token).await?; - - fs::create_dir_all(state_dir) - .await - .with_context(|| format!("mkdir matrix state dir {}", state_dir.display()))?; - let client = Client::builder() - .homeserver_url(homeserver) - .sqlite_store(state_dir, None) - .build() - .await - .with_context(|| format!("build matrix client for {homeserver}"))?; - let session = MatrixSession { - meta: SessionMeta { user_id, device_id }, - tokens: SessionTokens { - access_token: token, - refresh_token: None, - }, - }; - client - .restore_session(session) - .await - .context("restore matrix session")?; - tracing::info!( - user = %client.user_id().map(ToString::to_string).unwrap_or_default(), - device = %client.device_id().map(ToString::to_string).unwrap_or_default(), - "matrix session restored" - ); - Ok(client) -} - -/// Bare-reqwest whoami probe — used at startup to recover the -/// `user_id` + `device_id` the registration response carried but -/// hive-c0re didn't persist alongside the access token. Cheaper than -/// teaching the hive-c0re side to persist them, plus matches what a -/// fresh deployment with a hand-rolled token (`HIVE_MATRIX_TOKEN_FILE` -/// pointing somewhere unexpected) needs anyway. -async fn whoami(homeserver: &str, token: &str) -> Result<(OwnedUserId, OwnedDeviceId)> { - let url = format!("{homeserver}/_matrix/client/v3/account/whoami"); - let resp = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(10)) - .build() - .context("build whoami reqwest client")? - .get(&url) - .bearer_auth(token) - .send() - .await - .with_context(|| format!("GET {url}"))?; - let status = resp.status(); - if !status.is_success() { - let body = resp.text().await.unwrap_or_default(); - return Err(anyhow!("whoami GET {url} → HTTP {status}, body: {body}")); - } - let body: WhoamiResponse = resp.json().await.context("parse whoami response")?; - let user_id: OwnedUserId = body - .user_id - .parse() - .with_context(|| format!("invalid user_id in whoami: {}", body.user_id))?; - let device_id_raw = body - .device_id - .ok_or_else(|| anyhow!("whoami response missing device_id"))?; - let device_id: OwnedDeviceId = device_id_raw.into(); - Ok((user_id, device_id)) -} diff --git a/hive-matrix-mcp/src/handlers.rs b/hive-matrix-mcp/src/handlers.rs deleted file mode 100644 index 387b4726..00000000 --- a/hive-matrix-mcp/src/handlers.rs +++ /dev/null @@ -1,305 +0,0 @@ -//! Per-tool dispatch — each daemon request from the MCP bridge resolves -//! to one of these handlers. Returns a `DaemonResponse` shaped for the -//! wire protocol (the MCP bridge unwraps `Ok { payload }` and returns -//! the payload to claude; `Error { message }` becomes the tool-call -//! error message claude sees). -//! -//! Tool surface mirrors damocles-daemon's v0 set per mara on #548: -//! `send_message`, `send_dm`, `send_reaction`, `send_reply`, `mark_read`, -//! `list_rooms`, `list_room_members`, `read_room`. Plus a `ping` for the -//! MCP bridge's liveness probe. - -use matrix_sdk::{ - Client, - room::reply::{EnforceThread, Reply}, - ruma::{ - OwnedEventId, OwnedRoomId, OwnedUserId, RoomOrAliasId, - api::client::receipt::create_receipt::v3::ReceiptType, - events::{ - receipt::ReceiptThread, - reaction::ReactionEventContent, - relation::Annotation, - room::message::{MessageType, RoomMessageEventContent}, - }, - }, -}; -use serde::Serialize; - -use crate::protocol::DaemonResponse; - -/// JSON-shape for `list_rooms`: one row per joined room. -#[derive(Debug, Serialize)] -pub struct RoomInfo { - pub room_id: String, - pub canonical_alias: Option, - pub name: String, - pub member_count: u64, -} - -/// JSON-shape for `list_room_members`: one row per joined member. -#[derive(Debug, Serialize)] -pub struct MemberInfo { - pub user_id: String, - pub display_name: Option, -} - -/// JSON-shape for `read_room`: one timeline event, flattened for -/// claude consumption. -#[derive(Debug, Serialize)] -pub struct TimelineEvent { - pub event_id: String, - pub sender: String, - pub origin_server_ts: i64, - pub event_type: String, - /// Best-effort plain-text body for `m.room.message` events - /// (text / notice / emote); empty for events without a textual - /// body (state changes, reactions, etc.). - pub body: String, -} - -/// Resolve a room reference (id `!abc:server` OR alias `#name:server`) -/// to a joined `Room`. Returns an `Error` response if the room isn't -/// joined / the reference is malformed. -async fn resolve_room( - client: &Client, - reference: &str, -) -> Result { - let parsed: &RoomOrAliasId = reference.try_into().map_err(|e| { - DaemonResponse::error(format!("invalid room reference {reference}: {e}")) - })?; - let room_id: OwnedRoomId = if parsed.is_room_id() { - OwnedRoomId::try_from(reference) - .map_err(|e| DaemonResponse::error(format!("invalid room_id: {e}")))? - } else { - client - .resolve_room_alias(parsed.as_str().try_into().map_err(|e| { - DaemonResponse::error(format!("invalid alias: {e}")) - })?) - .await - .map(|r| r.room_id) - .map_err(|e| DaemonResponse::error(format!("resolve_room_alias {reference}: {e}")))? - }; - client - .get_room(&room_id) - .ok_or_else(|| DaemonResponse::error(format!("room {room_id} not joined"))) -} - -/// Best-effort plain-text body for a (non-sync) timeline event. -/// Returns "" for non-text events (state changes, reactions, -/// redactions) — claude can still see the `event_type` field to -/// disambiguate. `AnyTimelineEvent` (not `AnySync...`) because -/// `read_room` pulls events via the `/messages` endpoint which -/// returns the full-form variant. -fn extract_body(event: &matrix_sdk::ruma::events::AnyTimelineEvent) -> String { - use matrix_sdk::ruma::events::{AnyMessageLikeEvent, AnyTimelineEvent}; - match event { - AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(ev)) => { - ev.as_original().map_or_else(String::new, |orig| { - match &orig.content.msgtype { - MessageType::Text(t) => t.body.clone(), - MessageType::Notice(n) => n.body.clone(), - MessageType::Emote(e) => format!("* {}", e.body), - _ => String::new(), - } - }) - } - _ => String::new(), - } -} - -pub async fn send_message(client: &Client, room_ref: &str, body: &str) -> DaemonResponse { - let room = match resolve_room(client, room_ref).await { - Ok(r) => r, - Err(e) => return e, - }; - let content = RoomMessageEventContent::text_markdown(body); - match room.send(content).await { - Ok(resp) => DaemonResponse::ok(&serde_json::json!({ - "event_id": resp.event_id.to_string(), - "room_id": room.room_id().to_string(), - })), - Err(e) => DaemonResponse::error(format!("send to {}: {e}", room.room_id())), - } -} - -pub async fn send_dm(client: &Client, user_id: &str, body: &str) -> DaemonResponse { - let uid: OwnedUserId = match user_id.parse() { - Ok(u) => u, - Err(e) => return DaemonResponse::error(format!("invalid user_id {user_id}: {e}")), - }; - // Find existing DM or create one. - let room = client - .joined_rooms() - .into_iter() - .find(|r| { - // is_direct() is async; check direct_targets() instead which - // reads from cached state. - r.direct_targets().iter().any(|t| t.as_str() == uid.as_str()) - }); - let room = match room { - Some(r) => r, - None => match client.create_dm(&uid).await { - Ok(r) => r, - Err(e) => return DaemonResponse::error(format!("create_dm {uid}: {e}")), - }, - }; - let content = RoomMessageEventContent::text_markdown(body); - match room.send(content).await { - Ok(resp) => DaemonResponse::ok(&serde_json::json!({ - "event_id": resp.event_id.to_string(), - "room_id": room.room_id().to_string(), - "user_id": uid.to_string(), - })), - Err(e) => DaemonResponse::error(format!("send DM to {uid}: {e}")), - } -} - -pub async fn send_reaction( - client: &Client, - room_ref: &str, - event_id: &str, - key: &str, -) -> DaemonResponse { - let room = match resolve_room(client, room_ref).await { - Ok(r) => r, - Err(e) => return e, - }; - let eid: OwnedEventId = match event_id.parse() { - Ok(e) => e, - Err(e) => return DaemonResponse::error(format!("invalid event_id {event_id}: {e}")), - }; - let content = ReactionEventContent::new(Annotation::new(eid.clone(), key.to_owned())); - match room.send(content).await { - Ok(resp) => DaemonResponse::ok(&serde_json::json!({ - "event_id": resp.event_id.to_string(), - "target": eid.to_string(), - })), - Err(e) => DaemonResponse::error(format!("send reaction to {eid}: {e}")), - } -} - -pub async fn send_reply( - client: &Client, - room_ref: &str, - event_id: &str, - body: &str, -) -> DaemonResponse { - let room = match resolve_room(client, room_ref).await { - Ok(r) => r, - Err(e) => return e, - }; - let eid: OwnedEventId = match event_id.parse() { - Ok(e) => e, - Err(e) => return DaemonResponse::error(format!("invalid event_id {event_id}: {e}")), - }; - let content = RoomMessageEventContent::text_markdown(body).into(); - let reply = Reply { - event_id: eid.clone(), - enforce_thread: EnforceThread::MaybeThreaded, - }; - let reply_content = match room.make_reply_event(content, reply).await { - Ok(c) => c, - Err(e) => return DaemonResponse::error(format!("make_reply_event: {e}")), - }; - match room.send(reply_content).await { - Ok(resp) => DaemonResponse::ok(&serde_json::json!({ - "event_id": resp.event_id.to_string(), - "target": eid.to_string(), - })), - Err(e) => DaemonResponse::error(format!("send reply: {e}")), - } -} - -pub async fn mark_read(client: &Client, room_ref: &str, event_id: &str) -> DaemonResponse { - let room = match resolve_room(client, room_ref).await { - Ok(r) => r, - Err(e) => return e, - }; - let eid: OwnedEventId = match event_id.parse() { - Ok(e) => e, - Err(e) => return DaemonResponse::error(format!("invalid event_id {event_id}: {e}")), - }; - match room - .send_single_receipt(ReceiptType::Read, ReceiptThread::Unthreaded, eid.clone()) - .await - { - Ok(()) => DaemonResponse::ok(&serde_json::json!({ - "marked_read": eid.to_string(), - })), - Err(e) => DaemonResponse::error(format!("send_single_receipt: {e}")), - } -} - -pub async fn list_rooms(client: &Client) -> DaemonResponse { - let mut rooms = Vec::new(); - for room in client.joined_rooms() { - let name = match room.display_name().await { - Ok(n) => n.to_string(), - Err(_) => room.room_id().to_string(), - }; - let canonical_alias = room.canonical_alias().map(|a| a.to_string()); - rooms.push(RoomInfo { - room_id: room.room_id().to_string(), - canonical_alias, - name, - member_count: room.joined_members_count(), - }); - } - DaemonResponse::ok(&rooms) -} - -pub async fn list_room_members(client: &Client, room_ref: &str) -> DaemonResponse { - let room = match resolve_room(client, room_ref).await { - Ok(r) => r, - Err(e) => return e, - }; - let members = match room.members(matrix_sdk::RoomMemberships::JOIN).await { - Ok(m) => m, - Err(e) => return DaemonResponse::error(format!("members: {e}")), - }; - let list: Vec = members - .iter() - .map(|m| MemberInfo { - user_id: m.user_id().to_string(), - display_name: m.display_name().map(ToOwned::to_owned), - }) - .collect(); - DaemonResponse::ok(&list) -} - -pub async fn read_room(client: &Client, room_ref: &str, limit: Option) -> DaemonResponse { - use matrix_sdk::ruma::api::client::message::get_message_events; - use matrix_sdk::ruma::api::Direction; - - let room = match resolve_room(client, room_ref).await { - Ok(r) => r, - Err(e) => return e, - }; - let limit = limit.unwrap_or(50).min(200); - let mut req = get_message_events::v3::Request::new(room.room_id().to_owned(), Direction::Backward); - req.limit = matrix_sdk::ruma::UInt::try_from(limit as u64).unwrap_or(matrix_sdk::ruma::UInt::from(50u32)); - let resp = match client.send(req).await { - Ok(r) => r, - Err(e) => return DaemonResponse::error(format!("get_message_events: {e}")), - }; - let events: Vec = resp - .chunk - .iter() - .filter_map(|raw| { - let parsed = raw.deserialize().ok()?; - let event_id = parsed.event_id().to_string(); - let sender = parsed.sender().to_string(); - let origin_server_ts: i64 = parsed.origin_server_ts().0.into(); - let event_type = parsed.event_type().to_string(); - let body = extract_body(&parsed); - Some(TimelineEvent { - event_id, - sender, - origin_server_ts, - event_type, - body, - }) - }) - .collect(); - DaemonResponse::ok(&events) -} diff --git a/hive-matrix-mcp/src/lib.rs b/hive-matrix-mcp/src/lib.rs deleted file mode 100644 index 8db22f9a..00000000 --- a/hive-matrix-mcp/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! `hive-matrix-mcp` library: shared types + helpers used by both the -//! `hive-matrix-daemon` binary (long-running matrix-sdk Client) and the -//! `hive-matrix-mcp` binary (stdio MCP bridge claude spawns per turn). -//! -//! The wire protocol between the two binaries lives in [`protocol`]. -//! Path helpers (token file, daemon socket) live in [`paths`]. -//! -//! Phase 3 of #548. Architecture rationale + tool surface mirror the -//! existing `damocles-daemon` (see issue thread for details). - -pub mod client; -pub mod paths; -pub mod protocol; -pub mod wake; diff --git a/hive-matrix-mcp/src/main.rs b/hive-matrix-mcp/src/main.rs deleted file mode 100644 index 34a6b0bb..00000000 --- a/hive-matrix-mcp/src/main.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! `hive-matrix-daemon` binary — long-running matrix-sdk Client + sync -//! loop per agent. Bridges incoming room events to hyperhive wake -//! signals and serves the unix socket the stdio MCP bridge talks to. -//! -//! Lifecycle: -//! 1. Read access token from `paths::token_file()` (fail clean if absent). -//! 2. Whoami probe → recover `user_id` + `device_id` → restore -//! matrix-sdk session (no login flow). -//! 3. Install the message-event handler that fires hyperhive wakes. -//! 4. Spawn the unix socket listener for the MCP bridge. -//! 5. Run sync forever. -//! -//! Standalone-degraded boot: missing token file → exit 0 cleanly so -//! systemd's `ConditionPathExists=` doesn't have to be perfectly -//! synced with hive-c0re's token-provisioning timing. - -use anyhow::{Context, Result}; -use matrix_sdk::config::SyncSettings; - -mod client; -mod handlers; -mod paths; -mod protocol; -mod socket; -mod timeline; -mod wake; - -#[tokio::main] -async fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), - ) - .with_writer(std::io::stderr) - .init(); - - let homeserver = paths::homeserver_url(); - let token_file = paths::token_file(); - let state_dir = paths::matrix_state_dir(); - let mcp_socket = paths::daemon_socket(); - let hyperhive_socket = paths::hyperhive_socket(); - - if !tokio::fs::try_exists(&token_file).await.unwrap_or(false) { - tracing::warn!( - path = %token_file.display(), - "matrix token file absent; exiting cleanly (hive-c0re will provision \ - it on first agent registration, then systemd restarts us)" - ); - return Ok(()); - } - - tracing::info!( - homeserver, - token_file = %token_file.display(), - state_dir = %state_dir.display(), - "hive-matrix-daemon starting" - ); - - let matrix_client = client::build_and_restore(&homeserver, &token_file, &state_dir) - .await - .context("build matrix client")?; - - timeline::install_message_handler(&matrix_client, hyperhive_socket); - - // Spawn the unix socket server before sync starts so the MCP - // bridge can connect as soon as the first claude turn fires. The - // socket dispatches against the same `Client` we sync on, so any - // tool call benefits from the sync state-cache. - let socket_client = matrix_client.clone(); - let socket_listener = mcp_socket.clone(); - tokio::spawn(async move { - if let Err(e) = socket::serve(&socket_listener, socket_client).await { - tracing::error!(error = %e, "mcp socket server exited"); - } - }); - - // Sync forever; matrix-sdk handles reconnection internally. - let sync_settings = SyncSettings::default(); - matrix_client - .sync(sync_settings) - .await - .context("matrix-sdk sync loop exited")?; - - Ok(()) -} diff --git a/hive-matrix-mcp/src/paths.rs b/hive-matrix-mcp/src/paths.rs deleted file mode 100644 index 7d497108..00000000 --- a/hive-matrix-mcp/src/paths.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Per-agent filesystem paths used by both `hive-matrix-daemon` and the -//! stdio MCP bridge. -//! -//! All paths are overridable via env vars for dev / test scenarios and -//! to let the harness point the daemon at non-default locations when -//! the operator overrides `hyperhive.matrix.*` options. - -use std::path::PathBuf; - -/// Default homeserver URL when `HIVE_MATRIX_URL` isn't set. Tuwunel -/// (the local hive-matrix container) listens on `localhost:8008` by -/// default; shared host netns means every agent container resolves -/// `localhost` to the same machine. -pub const DEFAULT_HOMESERVER: &str = "http://localhost:8008"; - -/// Default unix socket path the daemon listens on inside the agent -/// container. The stdio MCP bridge `connect()`s here on every tool call. -/// `/run/hive-matrix.sock` is a tmpfs path that disappears on container -/// restart — fine, because the daemon recreates the socket on its own -/// boot. -pub const DEFAULT_DAEMON_SOCKET: &str = "/run/hive-matrix.sock"; - -/// Resolve the matrix access-token file path. Override via -/// `HIVE_MATRIX_TOKEN_FILE`; default is `/matrix-token`, -/// the path `hive-c0re::matrix::ensure_user_for` writes to on agent -/// account provisioning. -#[must_use] -pub fn token_file() -> PathBuf { - if let Some(p) = std::env::var_os("HIVE_MATRIX_TOKEN_FILE") { - return PathBuf::from(p); - } - let state_dir = std::env::var("HYPERHIVE_STATE_DIR").unwrap_or_default(); - PathBuf::from(format!("{state_dir}/matrix-token")) -} - -/// Resolve the homeserver URL. Override via `HIVE_MATRIX_URL`; default -/// is the in-container `localhost:8008` tuwunel. -#[must_use] -pub fn homeserver_url() -> String { - std::env::var("HIVE_MATRIX_URL").unwrap_or_else(|_| DEFAULT_HOMESERVER.to_owned()) -} - -/// Resolve the daemon's unix socket path. Override via -/// `HIVE_MATRIX_SOCKET`; default is `/run/hive-matrix.sock`. -#[must_use] -pub fn daemon_socket() -> PathBuf { - std::env::var_os("HIVE_MATRIX_SOCKET").map_or_else(|| PathBuf::from(DEFAULT_DAEMON_SOCKET), PathBuf::from) -} - -/// Persistent sqlite store directory for matrix-sdk's state (event -/// cache, devices, etc.). Lives under the per-agent state dir so it -/// survives container restart but gets wiped on `destroy --purge`. -#[must_use] -pub fn matrix_state_dir() -> PathBuf { - let state_dir = std::env::var("HYPERHIVE_STATE_DIR").unwrap_or_default(); - PathBuf::from(format!("{state_dir}/matrix-sdk-state")) -} - -/// Hyperhive control socket — the daemon writes wake signals here so -/// the harness drives a new claude turn on incoming matrix events. -/// Mirrors the path `forge_notify` writes to. -#[must_use] -pub fn hyperhive_socket() -> PathBuf { - std::env::var_os("HIVE_CONTROL_SOCKET").map_or_else(|| PathBuf::from("/run/hive/mcp.sock"), PathBuf::from) -} diff --git a/hive-matrix-mcp/src/protocol.rs b/hive-matrix-mcp/src/protocol.rs deleted file mode 100644 index d5982620..00000000 --- a/hive-matrix-mcp/src/protocol.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! Wire types for the daemon ↔ stdio-MCP-bridge unix socket protocol. -//! -//! Same shape as `damocles-daemon`'s `DaemonRequest`/`DaemonResponse` -//! (which this is forked from in spirit). Each request is a single -//! JSON line; each response is a single JSON line back. The stdio MCP -//! bridge holds a fresh connection per tool call — claude's tool -//! lifecycle is shorter than a persistent matrix-sdk Client wants to -//! live, so the daemon stays alive and the MCP reconnects per call. - -use serde::{Deserialize, Serialize}; - -/// Request from the stdio MCP bridge to the daemon. The MCP bridge -/// owns the on-wire shape claude sees; this enum is the internal -/// shape the daemon dispatches over. -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "method")] -pub enum DaemonRequest { - /// Post a plain-text or markdown message to a room. `room` accepts - /// either a matrix room id (`!abc:server`) or a canonical alias - /// (`#name:server`); the daemon resolves aliases server-side. - #[serde(rename = "send_message")] - SendMessage { room: String, body: String }, - - /// Open (or reuse) a DM with `user_id` and post `body`. Creates - /// the DM room if one doesn't already exist between this agent - /// and the user. - #[serde(rename = "send_dm")] - SendDm { user_id: String, body: String }, - - /// React to a specific event with an emoji `key`. Matrix-spec - /// `m.reaction` annotation. - #[serde(rename = "send_reaction")] - SendReaction { - room: String, - event_id: String, - key: String, - }, - - /// Reply to `event_id` in `room` with `body` as a threaded reply. - /// Sets the `m.in_reply_to` relation so matrix clients render the - /// thread. - #[serde(rename = "send_reply")] - SendReply { - room: String, - event_id: String, - body: String, - }, - - /// Mark `event_id` (in `room`) as read for this agent. Sends a - /// read receipt; bumps the room's "unread" indicator down on - /// matrix clients (and for other agents). - #[serde(rename = "mark_read")] - MarkRead { room: String, event_id: String }, - - /// List rooms the agent has joined. Returns each room's id + - /// canonical alias (when present) + name + member count. - #[serde(rename = "list_rooms")] - ListRooms, - - /// List the members of a room. Each entry carries the matrix - /// user id + the resolved display name (when set). - #[serde(rename = "list_room_members")] - ListRoomMembers { room: String }, - - /// Read the last `limit` events from a room's timeline. Caller - /// gets each event's id, sender, `server_ts`, type, and body (best- - /// effort plain-text extraction from `m.text` / `m.notice` etc.). - #[serde(rename = "read_room")] - ReadRoom { room: String, limit: Option }, - - /// Liveness probe — fast "are you up?" round-trip that doesn't - /// touch matrix-sdk. Not used by the in-tree stdio MCP bridge - /// (which surfaces a daemon-down condition as a normal tool-call - /// connect error); reserved for external clients that want an - /// explicit health check without doing real work. - #[serde(rename = "ping")] - Ping, -} - -/// Response shape: `ok` carries the payload (any JSON; the MCP bridge -/// passes it back to claude as the tool result), `error` carries a -/// human-readable error string. -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum DaemonResponse { - Ok { payload: serde_json::Value }, - Error { message: String }, -} - -impl DaemonResponse { - /// Convenience: build an Ok response from any serializable value. - /// Serialisation failure (impossible for the small handler structs - /// we use, but the API is generic) surfaces as a structured - /// `{"serialise_error": "..."}` payload so callers see WHY the - /// data is missing instead of a silent `null`. - pub fn ok(payload: &T) -> Self { - let payload = serde_json::to_value(payload) - .unwrap_or_else(|e| serde_json::json!({ "serialise_error": e.to_string() })); - Self::Ok { payload } - } - - /// Convenience: build an Error response from any `Display` value. - pub fn error(msg: impl std::fmt::Display) -> Self { - Self::Error { - message: msg.to_string(), - } - } -} diff --git a/hive-matrix-mcp/src/socket.rs b/hive-matrix-mcp/src/socket.rs deleted file mode 100644 index 5e0d6b9b..00000000 --- a/hive-matrix-mcp/src/socket.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Unix socket server: the daemon listens here, the stdio MCP bridge -//! `connect()`s on every tool call. One JSON request line in, one -//! JSON response line out. Connections are short-lived (per tool call) -//! so the loop is just accept → dispatch → reply → close. - -use std::path::Path; -use std::sync::Arc; - -use anyhow::{Context, Result}; -use matrix_sdk::Client; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; -use tokio::net::{UnixListener, UnixStream}; - -use crate::handlers; -use crate::protocol::{DaemonRequest, DaemonResponse}; - -/// Start listening on `socket_path` and serve forever. Removes any -/// stale socket file first (daemon restart after a non-clean shutdown -/// would otherwise hit EADDRINUSE). -pub async fn serve(socket_path: &Path, client: Client) -> Result<()> { - let _ = tokio::fs::remove_file(socket_path).await; - if let Some(parent) = socket_path.parent() { - tokio::fs::create_dir_all(parent) - .await - .with_context(|| format!("mkdir {}", parent.display()))?; - } - let listener = UnixListener::bind(socket_path) - .with_context(|| format!("bind unix socket {}", socket_path.display()))?; - tracing::info!(path = %socket_path.display(), "mcp socket listener up"); - let client = Arc::new(client); - loop { - let (stream, _) = listener - .accept() - .await - .context("accept connection on mcp socket")?; - let client = client.clone(); - tokio::spawn(async move { - if let Err(e) = handle_connection(stream, &client).await { - tracing::warn!(error = %e, "mcp socket connection error"); - } - }); - } -} - -async fn handle_connection(stream: UnixStream, client: &Client) -> Result<()> { - let (reader, mut writer) = stream.into_split(); - let mut lines = BufReader::new(reader).lines(); - while let Some(line) = lines.next_line().await? { - let response = match serde_json::from_str::(&line) { - Ok(req) => dispatch(req, client).await, - Err(e) => DaemonResponse::error(format!("parse request: {e}")), - }; - let mut json = serde_json::to_string(&response)?; - json.push('\n'); - writer.write_all(json.as_bytes()).await?; - writer.flush().await?; - } - Ok(()) -} - -async fn dispatch(req: DaemonRequest, client: &Client) -> DaemonResponse { - match req { - DaemonRequest::Ping => DaemonResponse::ok(&serde_json::json!({"ok": true})), - DaemonRequest::SendMessage { room, body } => { - handlers::send_message(client, &room, &body).await - } - DaemonRequest::SendDm { user_id, body } => handlers::send_dm(client, &user_id, &body).await, - DaemonRequest::SendReaction { - room, - event_id, - key, - } => handlers::send_reaction(client, &room, &event_id, &key).await, - DaemonRequest::SendReply { - room, - event_id, - body, - } => handlers::send_reply(client, &room, &event_id, &body).await, - DaemonRequest::MarkRead { room, event_id } => { - handlers::mark_read(client, &room, &event_id).await - } - DaemonRequest::ListRooms => handlers::list_rooms(client).await, - DaemonRequest::ListRoomMembers { room } => handlers::list_room_members(client, &room).await, - DaemonRequest::ReadRoom { room, limit } => handlers::read_room(client, &room, limit).await, - } -} diff --git a/hive-matrix-mcp/src/timeline.rs b/hive-matrix-mcp/src/timeline.rs deleted file mode 100644 index 2b695497..00000000 --- a/hive-matrix-mcp/src/timeline.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! Matrix event handlers: incoming room messages fire a hyperhive -//! wake signal so the agent's harness drives a new claude turn. -//! -//! Per mara on #548: wake body is a SHORT TEASER, not the full message -//! (msg stays unread server-side; agent fetches via `read_room`). The -//! `wake::format_wake_body` truncates to ~100 chars. -//! -//! Self-events (events sent by this agent) are filtered out so an -//! agent posting a message doesn't wake itself. - -use std::path::PathBuf; -use std::sync::Arc; - -use matrix_sdk::{ - Client, Room, RoomState, - ruma::events::room::message::{MessageType, OriginalSyncRoomMessageEvent}, -}; - -use crate::wake; - -/// Install the room-message handler on `client`. Fires on every -/// `m.room.message` event in a joined room; non-self text messages -/// trigger a wake signal to the hyperhive harness via the unix socket -/// at `hyperhive_socket`. -pub fn install_message_handler(client: &Client, hyperhive_socket: PathBuf) { - let socket = Arc::new(hyperhive_socket); - let own_user = client.user_id().map(std::borrow::ToOwned::to_owned); - client.add_event_handler({ - let socket = socket.clone(); - move |event: OriginalSyncRoomMessageEvent, room: Room, _client: Client| { - let socket = socket.clone(); - let own_user = own_user.clone(); - async move { - if room.state() != RoomState::Joined { - return; - } - // Self-event filter so the agent's own outgoing messages - // don't wake it. matches forge_notify's self-skip pattern. - if own_user.as_ref().is_some_and(|u| u == &event.sender) { - return; - } - let text = match &event.content.msgtype { - MessageType::Text(t) => t.body.clone(), - MessageType::Notice(n) => n.body.clone(), - MessageType::Emote(e) => format!("* {}", e.body), - _ => { - // Non-text content (image / file / location / etc.) — - // still wake, but with a placeholder body so the - // agent knows something landed and can read_room. - format!("[{}]", event.content.msgtype()) - } - }; - let room_label = room - .canonical_alias() - .map_or_else(|| room.room_id().to_string(), |a| a.to_string()); - let body = wake::format_wake_body(event.sender.as_str(), &room_label, &text); - if let Err(e) = wake::send_wake(&socket, &body).await { - tracing::warn!(error = %e, "failed to deliver matrix wake to hyperhive"); - } else { - tracing::debug!(room = %room.room_id(), "matrix wake delivered"); - } - } - } - }); - tracing::info!("matrix message handler installed"); -} diff --git a/hive-matrix-mcp/src/wake.rs b/hive-matrix-mcp/src/wake.rs deleted file mode 100644 index 39099157..00000000 --- a/hive-matrix-mcp/src/wake.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Wake-signal writer: notifies the hyperhive harness when an incoming -//! matrix event arrives so claude drives a new turn. -//! -//! Same wire shape as `hive-ag3nt::forge_notify`'s wake: a single JSON -//! line written to the hyperhive control socket (`/run/hive/mcp.sock` -//! by default) carrying an `AgentRequest::Wake { from, body }`. -//! The agent harness's `agent_server` parses it and treats it as a -//! `Wake` from the matrix subsystem. -//! -//! Per mara's call on #548 phase 3: the body is a SHORT TEASER, not -//! the full message — the agent then reads the unmarked event via -//! the `read_room` MCP tool. Truncation to ~100 chars keeps the wake -//! prompt focused (`forge_notify` embeds longer excerpts because the -//! agent doesn't have a follow-up read-the-original tool for forge). - -use std::path::Path; - -use anyhow::{Context, Result}; -use tokio::io::AsyncWriteExt; -use tokio::net::UnixStream; - -/// Max characters of `body` to embed in the wake payload. Shorter than -/// `forge_notify`'s 500-byte excerpt because the agent has a follow-up -/// `read_room` tool to fetch the full event. -pub const WAKE_BODY_TRUNCATE: usize = 100; - -/// Send an `AgentRequest::Wake { from: "matrix", body }` to the hyperhive -/// control socket at `socket`. Best-effort: returns Err on any plumbing -/// failure; callers log + ignore so a wake delivery hiccup doesn't tear -/// down the matrix sync loop. -pub async fn send_wake(socket: &Path, body: impl AsRef) -> Result<()> { - let payload = serde_json::json!({ - "kind": "wake", - "from": "matrix", - "body": body.as_ref(), - }); - let line = format!("{}\n", serde_json::to_string(&payload)?); - let mut stream = UnixStream::connect(socket) - .await - .with_context(|| format!("connect hyperhive socket {}", socket.display()))?; - stream - .write_all(line.as_bytes()) - .await - .with_context(|| format!("write wake to {}", socket.display()))?; - stream - .shutdown() - .await - .with_context(|| format!("shutdown write to {}", socket.display()))?; - Ok(()) -} - -/// Format a wake-message body from a matrix event's sender + room -/// canonical alias + body text. Truncates at [`WAKE_BODY_TRUNCATE`] -/// chars with an ellipsis. Shape: `[matrix] in : ` -/// matches the `forge_notify` `[issue …]` framing convention. -#[must_use] -pub fn format_wake_body(sender: &str, room: &str, text: &str) -> String { - let truncated = truncate_chars(text, WAKE_BODY_TRUNCATE); - format!("[matrix] {sender} in {room}: {truncated}") -} - -/// Truncate `s` to `max` Unicode chars, appending `…` when cut. -/// Char-based not byte-based so multi-byte content (most chat) doesn't -/// get cut mid-codepoint. -fn truncate_chars(s: &str, max: usize) -> String { - let mut end = s.len(); - for (count, (i, _)) in s.char_indices().enumerate() { - if count == max { - end = i; - break; - } - } - if end == s.len() { - s.to_owned() - } else { - format!("{}…", &s[..end]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn format_wake_body_short_passes_through() { - let body = format_wake_body("@iris:matrix.darkest.space", "#general", "hi all"); - assert_eq!(body, "[matrix] @iris:matrix.darkest.space in #general: hi all"); - } - - #[test] - fn format_wake_body_long_truncates_with_ellipsis() { - let long = "x".repeat(200); - let body = format_wake_body("@iris:m", "#x", &long); - assert!(body.contains("xxxxxxx")); - assert!(body.ends_with("…")); - // Header + truncated body should be well under the absolute - // wake-message ceiling (forge_notify uses ~600 bytes; we're - // way under that). - assert!(body.len() < 200); - } - - #[test] - fn truncate_chars_handles_multibyte() { - // `ü` is 2 bytes / 1 char. truncating to 3 chars on "üüüüüü" - // should yield "üüü…" not "üü\xc3…" (mid-codepoint). - let s = "üüüüüü"; - let t = truncate_chars(s, 3); - assert_eq!(t, "üüü…"); - } - - #[test] - fn truncate_chars_no_op_below_limit() { - let s = "hi"; - assert_eq!(truncate_chars(s, 100), "hi"); - } -} diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 468b04e2..3a8eb1f5 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -149,52 +149,6 @@ ''; }; - options.hyperhive.matrix.enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Enable per-agent matrix integration via `hive-matrix-mcp` - (#548 phase 3). When true (the default), the harness: - - - runs `hive-matrix-daemon` as a systemd unit that holds a - matrix-sdk Client + sync against the homeserver at - `HIVE_MATRIX_URL` (default `http://localhost:8008` — the - in-host tuwunel from `nix/modules/hive-matrix.nix`). The - daemon auto-skips when `/matrix-token` is missing, - and a `systemd.paths` watcher restarts it the moment - hive-c0re provisions the token (mirrors `matrix-avatar-sync` - shape from #571). - - exposes the matrix tool surface (send_message, send_dm, - send_reaction, send_reply, mark_read, list_rooms, - list_room_members, read_room) to claude via an auto-injected - `extraMcpServers.matrix` entry. Claude spawns the stdio - `hive-matrix-mcp` bridge per turn, which forwards each tool - call to the daemon over `/run/hive-matrix.sock`. - - wakes the agent on incoming room events via a short teaser - Wake signal (`[matrix] in : …`) - to the hyperhive control socket; the full event stays - unread server-side until `read_room` consumes it. - - Set to `false` for agents that should NOT have matrix tools at - all (e.g. agents on a host without `hyperhive.matrix.enable` on - the meta side). When token file is absent the daemon and MCP - both no-op cleanly anyway, so `false` is rarely necessary. - ''; - }; - - options.hyperhive.matrix.url = lib.mkOption { - type = lib.types.str; - default = "http://localhost:8008"; - example = "https://matrix.darkest.space"; - description = '' - Matrix homeserver URL the agent's `hive-matrix-daemon` connects - to. Default points at the in-host tuwunel (shared netns). - Override per-agent when an agent should talk to an external - homeserver instead (e.g. a federation-only setup or a remote - hive's tuwunel reached via a vpn). - ''; - }; - options.hyperhive.frontend.dist = lib.mkOption { type = lib.types.package; default = pkgs.hyperhive-frontend; @@ -529,19 +483,6 @@ } ]; - # Auto-inject the matrix MCP entry when matrix is enabled (#548 - # phase 3). Operator can override or disable by setting their own - # `extraMcpServers.matrix` (nix submodule merge takes the operator's - # value) or by flipping `hyperhive.matrix.enable = false`. - hyperhive.extraMcpServers = lib.mkIf config.hyperhive.matrix.enable { - matrix = lib.mkDefault { - command = "${pkgs.hyperhive}/bin/hive-matrix-mcp"; - args = [ ]; - env = { }; - allowedTools = [ "*" ]; - }; - }; - environment.etc."hyperhive/extra-mcp.json".text = builtins.toJSON config.hyperhive.extraMcpServers; # Operator-set per-agent icon (hyperhive.icon). When configured, the @@ -804,49 +745,6 @@ ''; }; - # Long-running matrix-sdk Client + sync per agent (#548 phase 3). - # Holds the unix socket the stdio `hive-matrix-mcp` bridge talks - # to, and emits hyperhive wake signals on incoming room events - # via `/run/hive/mcp.sock`. Conditional on `hyperhive.matrix.enable` - # AND token-file presence (the daemon binary itself exits 0 on - # missing token, but the path watcher below restarts it the - # moment the token lands — same first-boot-ordering pattern as - # matrix-avatar-sync.path / #571). - systemd.services.hive-matrix-daemon = lib.mkIf config.hyperhive.matrix.enable { - description = "long-running matrix-sdk Client + MCP daemon socket"; - wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; - wants = [ "network-online.target" ]; - environment = { - HIVE_MATRIX_URL = config.hyperhive.matrix.url; - RUST_LOG = "info"; - }; - serviceConfig = { - ExecStart = "${pkgs.hyperhive}/bin/hive-matrix-daemon"; - Restart = "on-failure"; - RestartSec = 5; - # /run/hive-matrix.sock + the matrix-sdk-state sqlite dir - # don't need a StateDirectory= — the socket is on tmpfs (gone - # on restart, which is correct) and the sqlite dir lives in - # the bind-mounted agent state, mode-managed by the harness. - }; - }; - - # Path-trigger sibling so hive-matrix-daemon fires the moment - # `/matrix-token` appears (#548 phase 3, mirrors the - # matrix-avatar-sync.path pattern from #571). On clean boot - # hive-c0re provisions the token AFTER agent containers come up; - # without the trigger the daemon would exit 0 quietly and the - # MCP would have no backend until next restart. With the watcher - # the daemon comes alive in the same boot cycle as provisioning. - # The glob matches every agent (manager sees its own state at - # `/agents/hm1nd/state/` via the `/agents` bind). - systemd.paths.hive-matrix-daemon = lib.mkIf config.hyperhive.matrix.enable { - description = "trigger hive-matrix-daemon when matrix-token appears"; - wantedBy = [ "multi-user.target" ]; - pathConfig.PathExistsGlob = "/agents/*/state/matrix-token"; - }; - # Path-trigger sibling so matrix-avatar-sync fires the moment # `/matrix-token` appears (#571 closes argus's first-boot # ordering nag on #567). On a clean boot hive-c0re's matrix @@ -855,12 +753,19 @@ # token didn't exist yet, and avatar would only land on the next # restart. With the path watcher the appearance of the token # triggers a re-fire of the service so the avatar is set in the - # same boot cycle as provisioning completes. The glob matches - # every agent (manager sees its own state at `/agents/hm1nd/state/` - # via the `/agents` bind). + # same boot cycle as provisioning completes. + # + # `PathExists=` fires at activation if the file is already there + # (covers re-deploys + restarts after first provisioning); the + # underlying inotify watch picks up subsequent creates (covers + # first-boot race). Manager mounts state at `/state` (legacy + # path), sub-agents at `/agents/