52 Commits
Author SHA1 Message Date
zeekay 25c6f62cff feat(rust): hanzo-shell reads exec's command and says allow, ask or deny
A pure-Rust classifier for the `Classify` seam. brush-parser (MIT) gives the bash AST
and rustpython-parser (MIT) the Python one; every simple command, redirection,
substitution and nested script (bash -c, here-documents, a script the command wrote and
runs, python -c, ssh host '...') is read in the order the shell runs them, and the policy
over paths decides what each costs. rust/shell is its own crate with a wasm-bindgen build,
so kai-gateway can replace safety.ts with it.

exec: an ask is refused until the call carries approve: true (never lifts a deny); the
interpreter must be a shell the classifier reads; the env is read beside the command;
the git tool asks the same classifier and runs with core.fsmonitor=false; ui install no
longer hands the component name to sh -c.

On 408,231 Bash commands: safety.ts flags 20.69% (15.27% unparsed); this answers
allow 88.2 / ask 9.6 / deny 2.2 (roots set), cannot read 0.22%, p99 0.6 ms.
2026-10-14 16:32:13 -07:00
zeekay fe2e8c2a02 feat(rust): hanzo-trigram reads tgrep's index, and a limit bounds the work
A search with a limit now runs in two passes: every file is weighed
(matching lines counted, definitions as 2^32) without holding a hit,
then hits are read from the heaviest files until the limit is met. The
answer is the head of the unlimited list, checked for limits from 0 to
the whole list over text, symbol, context, ties and documents. Before,
every hit of every file was built, sorted and deduplicated before the
limit applied: a pattern matching 1.28 million lines of a 112 MB tree
took 15.6 s where it now takes 0.14 s, and rg's --max-count had bounded
that case before this crate replaced it.

When there is a tgrep index at or above the searched directory the
search asks it which files can match. Candidates are matched against
current bytes, and files whose content or name changed at or after the
build are read from disk whether or not the index lists them, so the
answer is the scan's. Index::Skip scans, Index::Only refuses to; the
tests ask every query all three ways on the same tree, before and after
edits, creations, deletions and renames, and fail if the freshness walk
is disabled. hanzo-mcp index [DIR] builds one.

Measured on 112 MB / 9,026 files, cores 0-4: rg 43-55 ms, scan 75-93,
indexed 28-50. Without a watcher the index costs a metadata walk.
2026-10-14 14:41:07 -07:00
zeekay aad64666db fix(rust): a file's definitions lead its hits, so a limit cannot cut them off 2026-10-14 13:46:41 -07:00
zeekay 630ec216d3 feat(rust): tools/list in one order, and --tools to serve fewer
tools/list appended the registered tools in HashMap order, which is
different in every process: two starts of the same binary listed
different orders (checked: three runs, three orders), so a client's
prompt cache over the tool schemas could not hold between sessions. The
registered tools now follow the built-ins sorted by name.

--tools fs,code,exec lists and serves only those. A client is charged
for every schema it is shown, each turn: the whole list is 34 tools and
38 KB, about 9.5K tokens; fs, code and exec together are 4.6 KB, about
1.1K, and byte for byte the same on every start. A name the server does
not carry fails at startup and lists what it does; a call outside the
list answers Unknown tool, like any other name.

ToolsConfig's flags were never read and are left alone.
2026-10-14 13:40:31 -07:00
zeekay d717c2da73 feat(rust): exec asks a classifier before it runs a command
Classify is the contract between exec and the shell parser and
classifier: command and cwd in, a Verdict (allow, ask or deny, with
reasons) out. ExecTool::with_classifier installs one; ToolRegistry::
with_classifier does the same for the MCP server; ExecTool::verdict is
the call Dev makes to ask the same classifier without running anything.

A deny is refused before a process is spawned or registered. An ask runs:
a server cannot ask the person, and the client's own permission prompt
already stood between the model and the call. Every answer then carries
the verdict. refusal() is the one place that policy lives.

With none installed, which is every build until the classifier module
lands, exec answers byte for byte as before and its input schema is
unchanged: the verdict is an output field, so the TypeScript runtime's
schema stays identical.
2026-10-14 13:37:51 -07:00
zeekay e9e6096ce4 feat(rust): fs search_text and code search_symbol on tgrep, in process
hanzo-trigram is the one search engine behind both actions: tgrep-core
(the hanzoai fork, MIT, pinned by commit, feature docs) linked as a
library, so a search is a call, not an rg spawn. It is its own crate so
Dev can link it without this crate's browser and parser dependencies;
hanzo_mcp::trigram re-exports it.

Results are ranked (matching lines a file, or definitions before uses
for a symbol), then path, then line, so a page is an offset into one
fixed list and the cursor really pages: before, rg stopped at limit and
a cursor repeated the first page. .gitignore, hidden and binary files
are skipped as rg skips them. A glob that names a document type (*.pdf,
*.docx, *.zip) reads documents too, and a hit inside one carries the
archive members and its page, slide, paragraph or cell.

fs and code keep their names, schemas and answer shapes; entry and place
appear on a document hit only. search_symbol now reads definitions by
the engine's declaration patterns, not the first-keyword guess.

Linking tgrep-core brings libsqlite3-sys (its managed-index code), which
needs a newer cc than the tree-sitter grammars allow, so Cargo resolves
tree-sitter-{c,cpp,javascript,python,rust,typescript} to their previous
patch releases. 241 library tests and the code tool's pass on them.
Gating managed/ behind a feature in the fork removes both.
2026-10-14 13:37:01 -07:00
zeekay deaf801810 chore(release): hanzo-mcp 1.1.29
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Successful in 1m10s
Hanzo CI/CD / test-amd64 (push) Successful in 2m43s
Hanzo CI/CD / cicd (push) Successful in 6m22s
CI/CD / cicd (push) Successful in 9m12s
2026-10-04 16:28:32 -07:00
zeekay e3067ebc8e feat(rust): llm limits, catalog search, served and paid-by, plan refusals 2026-10-04 16:28:31 -07:00
zooqueenandHanzo Dev 5d256affa1 Write the release checksum in the form sha256sum -c reads
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Successful in 42s
Hanzo CI/CD / test-amd64 (push) Successful in 7m10s
Hanzo CI/CD / cicd (push) Successful in 2m40s
CI/CD / cicd (push) Successful in 9m55s
rust-v1.1.26 and rust-v1.1.27 shipped .sha256 sidecars holding a bare
hash. install.sh, which hanzo.sh runs for `curl hanzo.sh/mcp | sh`,
verifies with `sha256sum -c`, which needs "<hash>  <name>", so every
Linux install of those releases stopped at "checksum MISMATCH" and
installed nothing. The sidecar is sha256sum's own line again, as it was
through rust-v1.1.23.

Release 1.1.28.

Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 14:01:20 -07:00
zooqueenandHanzo Dev eb0f29fc85 Give the Rust runtime kai_decide and Enso through llm
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Successful in 59s
Hanzo CI/CD / test-amd64 (push) Successful in 3m22s
Hanzo CI/CD / cicd (push) Successful in 2m11s
CI/CD / cicd (push) Successful in 5m37s
The Rust hanzo-mcp could not reach Kai at all, and its llm tool defaulted
to an upstream model id, sent no routing bound, dropped the completion id
and the model that served, and returned a 402 or 429 as ok with empty
content because the client never read the status.

- kai_decide: POST /v1/decisions with the TypeScript tool's name, schema
  and shape checks (choice, score, noul; 1 to 100 questions).
- llm query: model enso-auto unless named, "auto" for Enso's pick;
  max_cost and max_latency_ms ride as X-Max-Cost and X-Max-Latency-Ms;
  the answer carries id and the model from X-Routed-Model or the body.
- llm models: one row per model with family, class and the catalog's
  per-million prices; family narrows it.
- llm feedback: POST /v1/ai/feedback with request_id, signal, rating.
- HanzoApi::call holds an answer to its status: a non-2xx, or an
  envelope that says error, is an error with the server's sentence.

Release 1.1.27.

Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 13:46:12 -07:00
zeekay b406b542b5 Take zapd 1.1.5 from crates.io
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Successful in 37s
Hanzo CI/CD / cicd (push) Successful in 1m24s
CI/CD / cicd (push) Successful in 3m26s
Hanzo CI/CD / test-amd64 (push) Successful in 1m59s
It is published there now, so the git pin goes.
2026-09-30 21:36:34 -07:00
zeekay ea08388793 Open and fill the release with the release action
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Successful in 41s
Hanzo CI/CD / test-amd64 (push) Successful in 1m46s
Hanzo CI/CD / cicd (push) Successful in 1m23s
CI/CD / cicd (push) Successful in 3m12s
The runner image carries no gh CLI, so the open job died on its first
command and nothing was built. The release action talks to the API
itself, and the toolchain action installs Rust and the target rather
than trusting the image to have them. hanzo-mcp 1.1.26.
2026-09-30 21:23:47 -07:00
zeekay 1ef1ce34a5 Add npx, uvx and jq to the Rust runtime
npx and uvx build Python's argv (package, whitespace-split args, npx -y,
uvx --python) and run it as an exec with a two-minute auto-background. jq
passes its filter as one argv word, as Python's jq tool does.
2026-09-30 15:17:29 -07:00
zeekay ed2a23156d Let presence arrive before WELCOME in the native host test
A node registering between ours entering the registry and our WELCOME
being queued is announced to us first; the extension ignores it, and so
does the test now.
2026-09-30 15:17:25 -07:00
zeekay 0bb22d833e Keep a backgrounded Rust exec's output and exit
A command past its timeout had its pipes dropped with the wait, so it lost
its output (or died of SIGPIPE) and stayed running forever in ps. Its
output now drains to a log that logs reads, and its exit is recorded; a
command that finishes in time returns inline and leaves no log.
2026-09-30 15:17:17 -07:00
zeekay 03b7fd3edf Give the Rust fs tool Python's contract
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
Hanzo CI/CD / test-amd64 (push) Successful in 1m46s
publish / publish (push) Failing after 1m10s
Hanzo CI/CD / cicd (push) Successful in 1m53s
CI/CD / cicd (push) Successful in 3m43s
uri (path accepted), the unified envelope, sha256 hashes, write that only
creates, apply_patch guarded by the hash a read returned, list with depth,
pattern and cursor, search_text, and patch taking input. The search alias
tool goes: fs action=search_text is the one search.
2026-09-30 15:03:49 -07:00
zeekay 6dd2293590 Serve the extension's native messaging host from the Rust binary
Hanzo CI/CD / test (linux-amd64) (push) Skipped
Hanzo CI/CD / test (linux-arm64) (push) Skipped
Hanzo CI/CD / test-advisory (push) Skipped
publish / publish (push) Failing after 1m15s
Hanzo CI/CD / test-amd64 (push) Successful in 1m56s
Hanzo CI/CD / cicd (push) Successful in 2m33s
CI/CD / cicd (push) Successful in 4m33s
Run as hanzo-zap-host, hanzo-mcp relays ai.hanzo.zap messages to the ZAP
router's socket, as Python's native_host.py does, so a machine with only the
Rust server still lets Chrome and Firefox join without a door or pairing. Each
start registers it with the installed browsers, leaving any manifest whose
program exists alone.
2026-09-30 14:59:17 -07:00
zeekay d9f8034726 Route the Rust browser and cdp tools to the extension over the embedded ZAP router
hanzo-mcp links zapd v1.1.5, stands for this login's router at startup and
takes the seat mcp/hanzo-<pid>, as Python hanzo-mcp does. browser takes
Python's action names and parameters (the core typed, the rest in args) and
sends each action with a wire method to the registered browser node; headless
Playwright runs only when none is registered. cdp sends raw methods to the
same node. hanzo-mcp pair prints the door's pairing code. The integration test
embeds the router on a private XDG_RUNTIME_DIR with a fake extension.
2026-09-30 12:52:05 -07:00
zeekayandHanzo Dev a03f7e6820 The graph is on the surface, and hanzo reaches it
The catalog is regenerated from cloud (plugin/gen-mcp-catalog): the graph tool
now carries all thirteen operations, answer, communities, diff, erase, extract,
ingest and path among them, and the Rust crate's copy is synced from it.

The hanzo tool sent {resource, action, ...} to a subsystem handler that reads
{op, input}, so every call from the default surface reached the fleet with no
op. It now sends {op: action, input: args}, the shape the Rust runtime sends,
lists a resource's actions when none is named, and offers only subsystems.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-23 05:37:21 -07:00
zeekay 3b694c1f9e fix(rust): include cloud catalog in published MCP crate 2026-09-06 02:56:12 -07:00
zeekay 94d8398f1a Drop the last /api/ address, and make the mode tests take turns
The registry fallback asked ui.hanzo.ai/api/registry, which 301s to a 404 — so a
dead address stood in for a second chance nobody had. The primary serves; the
fallback is gone rather than moved, because /v1/registry is not served either and
pointing a client at an address the estate does not answer is the same defect
with a better prefix.

The mode tests raced a process-wide singleton: one activates minimal while
another activates guido and reads it back, and cargo runs them in threads of one
process. Three runs gave ok, ok, FAILED. They share a lock now.
2026-08-30 16:15:03 -07:00
zeekay 0a5f63f352 Read the same catalog from Rust
The runtimes are said to mirror one another, and this one listed ten services
against the fleet's 114. Two of the ten had been left behind, and the commerce
base was api.hanzo.ai/api/v1 — an /api/ prefix nothing serves.

Nine routers over four hosts become one dispatch to the single endpoint, and the
service list becomes a read of the file the TypeScript runtime already embeds.

The aliases are removed rather than repointed: three of the four named the old
surface, so they mapped a caller off the API. Spelling is still normalised.
2026-08-30 16:05:35 -07:00
zooqueenandHanzo Dev cf98e8777a auth: the two key shapes are pk- and sk-, and one sentence says so
Cloud's APIKeyPrefixes admits pk- and sk- only (auth_identity.go, 4b030b77).
Every tool here still told the user to set an hk- key, which cloud resolves to
no principal — following the message produced a key that could never
authenticate.

The message is now a single const beside the resolver that mints it, so the
seven tools that report it cannot drift apart again. Resolution order is
untouched: HANZO_API_KEY, then ~/.hanzo/config.json .apiKey.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-08 14:59:50 -07:00
zeekay b1281cd582 initialize is a negotiation, so answer the revision the client asked for
The server replied "2024-11-05" to every initialize, whatever the client asked
for. `hanzo dev` asks for 2025-06-18 and refuses a server that answers anything
else, so it reported `MCP server hanzo failed to start` — in an image where the
binary was on PATH, `dev mcp list` showed it registered, and a hand-written
handshake got 29 tools back. Everything that looked at the server passed;
nothing that PAIRED it with an agent worked.

Negotiation is the whole fix: echo the requested revision when it is one we
serve, answer our newest when it is not and let the client decide. The three
listed revisions are the ones this server can actually serve — initialize,
tools/list and tools/call are unchanged across them, and what the later ones add
is optional and unadvertised. A client pinned to the original revision keeps
getting it, which is the point of negotiating rather than moving the constant.
2026-08-07 08:03:05 -07:00
zooqueenandHanzo Dev b3c34fa7c3 feat(lsp): repo answers from /v1/code/lsp, file stays local
One tool, two planes, picked by data the caller already has: `file` names the
file, `repo` (a git.hanzo.ai slug, optional `rev`) says which world it lives
in. Without `repo` nothing changes — the same language server on the same
tree. With it, the question goes to the indexed corpus behind /v1/code/lsp,
which reaches across a repo's dependencies without checking anything out.

Actions map onto ops: locate carries a relation (definition|reference|type|
implementation) because "where is X" is one question with four answers, not
four routes; hover, symbols, diagnostics and complete stand on their own. The
body is {repo, rev?, path, line, character, relation?} in LSP's own frame —
0-based line, UTF-16 character — shifted from the tool's 1-based `line` at the
same boundary the local plane shifts it.

Credentials and base URL come from the one HanzoApi client every cloud-backed
tool already composes; there is no second client and no second lsp tool. The
actions a plane cannot serve say so up front rather than spawning a server or
calling out — rename/code_action/organize_imports/status need a working tree,
type/implementation/symbols are the index's to answer.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 03:14:53 -07:00
zeekayandHanzo Dev c36a1eb1c8 rust/tests: repair the two suites that stopped compiling at HIP-0300
test_shell_tools and test_think_tools have not built since the shell->exec
consolidation, so `cargo test` was red and 46 tests were silently not running.
Nothing here is a new assertion — each change follows the tool's live contract,
read off the published 1.1.22 binary rather than guessed:

  shell   ShellTool -> ExecTool, ProcToolArgs -> ExecToolArgs. Pure rename;
          all 16 pass untouched.
  think   ThinkTool::execute returns Value, not String, and HIP-0300 moved the
          payload under "data". Help reports the tool as "tool", not "name".

Tests whose subject no longer exists are deleted, not rewritten to pass:
critic/review with focus, code_snippets, file_paths and work_description (11);
the timestamp and examples keys (2); action-less auto-detect, which now falls
through to help (1).

browser: playwright_available -> driver_running, its successor since the tool
moved to a CDP driver. That file compiled, so this one was failing, not skipped.

351 pass, 0 fail: lib 223, browser 28, fs 27, search 28, memory 22, shell 16,
think 5, research 2. tests/ is excluded from the package, so 1.1.22's contents
are unchanged and no version bump is warranted.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 14:23:33 -07:00
zeekayandHanzo Dev 57cf89b892 rust: take back hanzo-mcp on crates.io at 1.1.22
The name has changed hands four times across three codebases:
  1.1.10, 1.1.11  rmcp client shim
  0.1.0,  1.1.21  rust-sdk re-export facade (ships zero tools)
  1.1.12          this repo, the only lineage with working tools

1.1.21 is what cargo add resolves today: no binary, and its
ToolRegistry::with_defaults() returns [] with the server feature on.
Publish this tree instead, above it, so the name carries the server.

Description said 'blockchain'; there is no blockchain tool. It now
names the 15 tools the registry actually serves.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 14:06:58 -07:00
zooqueenandHanzo Dev d0e89b9bf7 feat(rust): research — the /v1/ask answer engine as one tool
Deep research is a MODE of the one door (POST /v1/ask, mode=research), not a
second endpoint and not a client-side loop: the plan → search → read → rank →
synthesize → cite pass runs server-side where it is bounded and billed. The
tool sends `{q,mode}` with `Accept: text/event-stream` and folds the stream
into one cited answer plus the sources behind it.

HanzoApi gains `events()` — POST over SSE — so streaming lives with the other
credentials-and-base-URL knowledge instead of beside a tool, and `frames()`
decodes a body into its `data:` payloads. Both are the shared `@hanzo/ai`
SearchEvent contract: data-only JSON that self-describes via `type`, with the
terminal `[DONE]` a marker rather than an event. Bearer attachment collapses
into one `auth()` now that two request paths need it.

`research` sits beside `web_search` and `web_read` because they are ONE web
capability at three depths — a snippet, a page, a report — so the existing
`ToolsConfig::web_search` flag governs all three and no second flag appears.
A mode this door does not open (`search`, `news`) is refused rather than
silently answered: a wrong answer is worse than an error.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-04 22:50:50 -07:00
zeekay bd0048dbcf chore(rust): release 1.1.15
1.1.14 published only 4 of 5 targets: the linux-amd64 job gates the
matrix on the host test run, and three tests failed on the ARC runner.

The search tools shell out to `rg` at runtime, which the runner image
does not carry, so test_search and test_unified_search asserted against
a missing binary. Install ripgrep rather than skip them — it is a real
runtime dependency and the coverage is real.

test_screen_size_action reads the display through X11 and cannot pass
headless, same as the already-skipped test_position_action. Skip it by
name so the rest of computer_tool still gates.

Reproduced both causes locally by running the test binary with `rg`
off PATH and DISPLAY unset: identical panics. With ripgrep present and
both display tests skipped, 214 pass / 0 fail.
2026-07-18 23:51:05 -07:00
zeekay f15c3bc806 chore(rust): release 1.1.14
1.1.13 never published: the release build failed on a cross-repo
include_str! and produced no artifacts. Rather than move a pushed tag,
take the next patch.
2026-07-18 20:41:27 -07:00
zeekay 363ebca369 fix(rust): vendor the email recipe into the crate
builtin_email_recipe_yaml include_str!'d
../../../../bot/extensions/recipes-brain/recipes/email.yaml — a path
that climbs out of this repo into a sibling checkout of hanzoai/bot.
That resolves only on a workstation with both repos side by side, so
every CI target failed to compile the lib:

  couldn't read `src/brain/../../../../bot/.../email.yaml`

Copy the file to rust/recipes/email.yaml (byte-identical, sha256
f4577ec92d696a426d8ec230af55747e81a505c79f9d02266f9e2e0538ed38ae) and
include it from there. This is also what the module doc already claimed:
recipes live at <crate_dir>/recipes/*.yaml.

Verified by building all five targets from a copy containing only this
crate's tracked files, with no sibling repo present.
2026-07-18 20:41:21 -07:00
zeekay f55120fcc4 chore(rust): release 1.1.13
Patch bump from 1.1.12, the last published version of this crate.
Cargo.toml and Cargo.lock both carry 1.1.13 so the release builds
under --locked; a stale lock package entry is what broke the last
hanzoai/cli release.
2026-07-18 20:28:03 -07:00
zeekay 10f3de707f fix(rust): unblock cross-compilation of the hanzo-mcp binary
reqwest ran on default features, which selects native-tls and pulls
openssl-sys. openssl-sys needs per-arch OpenSSL headers, so every
non-host target failed in its build script before compiling a line of
our code. Move to rustls, which is pure Rust and needs no C headers.
Same 0.11 line, only the TLS backend changes.

GetDC and ReleaseDC live in winapi::um::winuser, not wingdi; only
GetPixel is a GDI call. The windows target had never been compiled, so
the wrong module went unnoticed. Enable the wingdi, windef and
minwindef features the file actually uses.

All five targets now build from one amd64 host via cargo-zigbuild.
2026-07-18 20:27:56 -07:00
zeekay 66427c608e feat(rust-mcp): 100% default MCP surface parity + stdio drop-in
stdio/--project-dir transport (drop-in for hanzo code); +9 tools (config, llm+think-wiring, hanzo 10-service router, ui, agent+zen, lsp, refactor, fs mv/mkdir/rm, system); full browser (persistent session + ~90 Playwright actions + CDP bridge); code serialize. 29 tools registered, crate compiles. No Python/uvx needed.
2026-07-18 18:29:17 -07:00
zeekay c8c8130578 mcp brain: BLAKE3 (sync with node canonical)
Swapped sha2 stand-in for blake3 crate. Wallet address output now byte-
equivalent across all five brain runtimes:

  encode_address([0u8;32], None)
  → hanzo:UFC8qCW8LRUmpfyRq2qnAvYi11cqftY3b

38 brain tests pass.
2026-05-11 12:36:30 -07:00
zeekay aa19dc8c32 mcp/rust: sync brain::algorithms to node canonical
- Update doc comment to point at hanzoai/node canonical home and drop
  the stale `hanzoai/brain/PARITY.md` reference (PARITY contract is now
  superseded by the byte-equivalent mirror policy).
- Drop spurious `mut` on the `flush` closure inside `estimate_tokens`
  to match node — closure mutates through &mut params, no rebind.

Diff vs `hanzoai/node/hanzo-libs/hanzo-brain/src/algorithms.rs` is now
limited to the three expected divergences:
  * doc comment (different host context)
  * SearchHit: imported from super::store here, locally defined in node
  * test-module use vs comment for the same reason

38 brain::algorithms tests passing.
2026-05-11 12:12:19 -07:00
zeekay 2dff5ecbc6 mcp/rust: port fortemi algorithm parity to brain crate (38 tests)
Adds hanzo_mcp::brain::algorithms covering RRF/RSF, adaptive RRF, MMR,
chunk dedup, Unicode script detection, FTS helpers (CJK bigrams, emoji
trigrams, websearch_to_tsquery, FTS5 MATCH), embed registry + MRL
truncation, UUIDv7 temporal bounds, WebVTT/SRT/RTTM captions, token
estimator, MRR/recall/precision/NDCG, Haversine, HTTP Range, wallet-
style address, graph maintenance (SNN/PFNET/Louvain), provider slug +
runtime config + link-type classifier.

Adds sha2 = "0.10" workspace dependency (BLAKE3 stand-in for the Rust
runtime — the wallet address contract uses the same bytes-to-base58check
shape across all four runtimes).

See hanzoai/brain/PARITY.md for the cross-runtime contract.
2026-05-11 11:44:57 -07:00
zeekay 26dcd3920b brain: clarify zapdb is the canonical native store — zap-proto/db, not luxfi/database 2026-05-10 19:50:15 -07:00
zeekay 83df953177 brain: Rust port of the Hanzo Brain — parity with TS + Python
Adds rust/src/brain/ module with three components mirroring the TS
(bot/extensions/{memory,graph-links,recipes-brain}) and Python
(python-sdk/pkg/hanzo-memory/{graph_links,recipes}.py) implementations:

  graph_links.rs — Edge struct + EdgeType enum, extract_edges(),
                   reconcile(), slugify(). Same six edge types, same
                   regex + role inference, same code-fence stripping
                   as the other two runtimes. Pure — no I/O, no LLM.

  store.rs       — Pluggable BrainStore trait (async). Same surface as
                   TS BrainStore + Python BaseVectorDB so a single
                   ~/.hanzo/brain/brain.db is read/written by all three.

  recipes.rs     — YAML recipe loader. Reads HANZO_HOME/recipes and
                   HANZO_BRAIN_RECIPES; ships the email recipe at
                   compile-time via include_str! from the canonical
                   bot/extensions/recipes-brain/recipes/email.yaml.

Tests: 6/6 pass via `cargo test --lib brain` covering slugify, every
edge type, code-fence stripping, reconcile, recipe parse.

Dep added: serde_yaml = "0.9".
2026-05-10 19:29:37 -07:00
zeekay f9e2c61f5b feat(memory): add cross-runtime parity for blue-red agent coordination
- TypeScript: add update, manage, facts, summarize, help actions + id/scope/content aliases
- Rust: add file persistence (~/.hanzo/memory.json), namespace/key/TTL support, kb action
- Both runtimes now share the same JSON format for cross-agent memory coordination
- Enables blue-red paired security review protocol via shared namespace
2026-03-24 18:03:26 -07:00
zeekay 4654285b5e feat: self-contained ZAP protocol in MCP, clean up Rust main
- Rewrite zap-server.ts with inline ZAP encode/decode (no @hanzo/zap dep)
- Clean up Rust main.rs: remove dead hanzo-node check, use CARGO_PKG_VERSION
- Remove accidentally tracked rust/target/ from git
2026-03-12 23:17:19 -07:00
zeekay 748d49b724 fix(rust): exclude target/ from crate package 2026-03-12 18:23:33 -07:00
zeekay e323927c7c chore(rust): bump crate to 1.1.12
Patch release with HIP-0300 action parity additions.
2026-03-12 18:20:23 -07:00
zeekay aa5074ac70 chore: bump @hanzo/mcp to 2.5.0, hanzo-mcp crate to 2.5.0
HIP-0300 bidirectional action sync release:
- Full action parity across Python/TS/Rust implementations
- 13 unified tools with complete action coverage
- Rust crate metadata: repository, homepage, keywords, categories
2026-03-12 15:39:30 -07:00
zeekay 702a3dab45 feat(tools): bidirectional HIP-0300 action sync across TS/Rust/Python
Complete action parity for all 13 unified tools:

Rust additions:
- code: +9 actions (search_symbol, outline, metrics, exports, types, hierarchy, rename, grep_replace, serialize)
- fetch: +4 actions (fetch, head, search, crawl)
- git: +15 actions (add, commit, reset, stash_*, tag, remote, cherry_pick, rebase, merge, blame, bisect)
- memory: +9 actions (stats, clear, export, import, merge, tag, untag, namespaces, history)
- plan: +8 actions (list, approve, reject, status, archive, templates, duplicate, export)
- tasks: +8 actions (list, update, delete, clear, stats, search, export, import)
- think: +4 actions (reflect, plan, critique, summarize)
- mode: +2 actions (list, status)
- workspace: +1 action (schema)

TypeScript additions:
- exec: +1 action (wait)
- code: +4 actions (parse, serialize, definition, transform)
- fetch: +2 actions (search, crawl)
- git: +2 actions (apply, checkout)
2026-03-11 11:37:15 -07:00
zeekay e9a4d37d1f feat: C FFI + Go cgo bindings for Rust MCP tools, fix TS conformance
Rust FFI (rust/src/ffi.rs):
- Expose all 13 HIP-0300 tools via C-compatible API
- 7 extern "C" functions: init, call_tool, list_tools, last_error,
  free_string, destroy, version
- Persistent multi-threaded tokio runtime behind opaque handle
- panic::catch_unwind on all FFI boundaries
- Wire 5 missing tools (code, git, fetch, workspace, tasks, hanzo)
  into ToolRegistry::execute() match arms
- Build as cdylib + staticlib (libhanzo_mcp.a / .dylib)

Go bindings (go/hanzomcp/):
- Handle type with Init/CallTool/CallToolRaw/ListTools/Close
- JSON in, JSON out across cgo boundary
- runtime.SetFinalizer safety net
- 11 tests including 20-goroutine concurrent stress test
- Race detector clean

TypeScript fixes:
- tasks: handle stale todos.json with wrong schema (todos vs items)
- code: add symbols + summarize actions for Python parity
- fetch: add fetch + head actions for Python parity

Results: Rust 66/66 tests, Go 11/11 tests, TS 22/22 conformance
2026-03-11 11:37:15 -07:00
zeekay ac2000708d feat: HIP-0300 unified tool architecture (TS + Rust)
Implement 13 canonical tools organized by axis:
- Core (7): fs, exec, code, git, fetch, workspace, ui
- Optional (6): think, memory, hanzo, plan, tasks, mode

Key changes:
- Add src/tools/unified/ with action-routed tool implementations
- Add Rust native tools: exec, git, fetch, code, hanzo, tasks, workspace, computer
- Refactor tool index to default to unified surface (legacy available via enableLegacy)
- Simplify tests to match new architecture (337 tests, 15 suites pass)
- Update LLM.md with HIP-0300 architecture documentation
- Remove legacy shell_tool.rs and ui_tool/ (replaced by exec_tool.rs and computer_tool/)
- Stop tracking rust/target/.rustc_info.json
2026-03-11 11:37:15 -07:00
zeekay 8fa47c2503 fix(rust): expose built-in tools and align parity metadata 2026-03-11 11:37:15 -07:00
zeekay 028c53ed6d chore: sync uncommitted changes 2026-02-13 22:15:53 -08:00
zeekay 7195df0566 feat: Add award-winning landing page example using unified ui tool
- Created complete landing page with @hanzo/ui components
- Demonstrates ui tool's ability to compose complex UIs
- Includes hero, features, testimonials, pricing, and CTA sections
- Production-ready code with animations and dark theme
- Validates dev agent can use ui tool effectively
- Comprehensive testing suite for all UI tool methods
- Matrix test report showing 80% success rate
2025-09-19 00:08:55 -05:00