Commit Graph
402 Commits
Author SHA1 Message Date
zeekay 4ded0d56f0 style: black --target-version py312 (unblock publish CI) 2026-05-13 13:54:58 -07:00
zeekay efa90f9978 decomplect: HIP-0300 wire surface is the only public contract
The MCP wire (what tools/list reports) ships exactly one tool per axis:
fs, exec, code, git, fetch, plan — action-routed. The per-action classes
(ReadTool, WriteTool, EditTool, …) stay importable for in-process
read-only sandboxing (swarm sub-agents) but are NOT on the wire.

PyPI 0.3.1 of hanzo-tools-fs shipped the legacy split (7 tools) while
the local 0.3.1 source had already migrated to TOOLS=[FsTool]. Same
version number, two different contracts — the source of the
'Python MCP surfaces 32 tools, Node MCP surfaces 13' divergence
that breaks contract parity. Bumping to 0.3.2 publishes the
HIP-0300 surface for the first time.

- pkg/hanzo-tools-fs 0.3.1 → 0.3.2: TOOLS=[FsTool]; legacy classes
  importable; dropped 'Edit = EditTool' alias; cleaner __init__.
- pkg/hanzo-tools-shell 0.6.3 → 0.6.4: bump only (source already
  TOOLS=_get_detected_shell_tools()).
- pkg/hanzo-tools-code 0.1.0 → 0.1.1: bump only (TOOLS=[CodeTool]).
- pkg/hanzo-tools-vcs  0.1.0 → 0.1.1: bump only (TOOLS=[GitTool]).
- pkg/hanzo-tools-net  0.1.0 → 0.1.1: bump only (TOOLS=[FetchTool]).
- pkg/hanzo-tools-plan 0.1.0 → 0.1.1: bump only (TOOLS=[PlanTool]).
- pkg/hanzo-tools-agent 0.3.1 → 0.3.2: 'Edit' alias is gone — call
  EditTool by its real name in swarm_tool.py.
- pkg/hanzo-mcp 0.15.3 → 0.15.4: gets the HIP-0300-clean wire when
  consumed via 'uvx --refresh-package hanzo-tools-fs hanzo-mcp serve'.
2026-05-13 13:51:48 -07:00
zeekay 262593f4ec hanzo-memory: real BLAKE3 + fix render_vtt trailing newline
Two byte-equivalence bugs caught by cross-runtime parity check:

1. _blake3() was falling back to BLAKE2b when hashlib lacked BLAKE3 (it
   always does on stock CPython). Wallet addresses depend on the SAME
   hash across all five brain runtimes. Required the `blake3` pip
   package; no fallback. Python addresses now match the TS canonical
   (@noble/hashes/blake3): encode_address(32x0x00) → "hanzo:UFC8qCW8...".

2. render_vtt joined lines with "\n" and appended one "" at the end,
   producing a single trailing newline. Every other runtime emits "\n\n"
   after each segment so cues are separated by a blank line per WebVTT
   spec. Switched to explicit f-string append; render_srt fixed the same
   way.

53 tests still pass.
2026-05-11 12:27:11 -07:00
zeekay ad20905f57 hanzo-memory: drop stale PARITY.md ref, export KNOWN_PROVIDERS
- Replace the dangling hanzoai/brain/PARITY.md pointer with an
  in-place cross-runtime note (Go brain + TS bot-memory).
- Add KNOWN_PROVIDERS to algorithms.__all__ so the inference
  provider whitelist participates in 'from algorithms import *'.

tests/test_algorithms.py --noconftest: 53 passed.
2026-05-11 12:11:44 -07:00
zeekay d9b2284e38 hanzo-memory: port fortemi algorithm parity to Python (53 tests)
Adds hanzo_memory.algorithms covering RRF/RSF, adaptive RRF, MMR, 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, BPE token estimator,
MRR/recall/precision/NDCG eval, Haversine spatial, HTTP Range, wallet-
style address, graph maintenance (SNN/PFNET/Louvain), document-type
registry + auto-detect, circuit breaker + retry, provider slug + runtime
config, link-type rule classifier.

Run via pytest --noconftest tests/test_algorithms.py
See hanzoai/brain/PARITY.md for the cross-runtime contract.
2026-05-11 11:44:47 -07:00
zeekay 078c198f12 brain: port graph-links + recipes to hanzo-memory (Python parity with bot/extensions)
Adds the zero-LLM typed-link extractor + YAML recipe loader to the
existing hanzo-memory Python SDK so a single brain.db file can be
written by the TS bot and read by the Python SDK and vice versa.

  graph_links.py — Edge dataclass, extract_edges(), reconcile(),
                   slugify(). Same six edge types
                   (mentions/attended/works_at/invested_in/founded/
                   advises), same regex + role inference, same code-
                   fence stripping. Pure — no I/O, no LLM.

  recipes.py     — list_recipes() + load_recipe(name). Reads built-in
                   recipes/ dir + optional HANZO_BRAIN_RECIPES path.

  recipes/email.yaml — flagship swipe-to-reply recipe, byte-identical
                       to the TS pack so cross-runtime brains agree.

  tests/test_graph_links.py — mirror of TS suite. 12 assertions cover
                              slugify, every edge type, code-fence
                              stripping, dedup, bare slugs, reconcile,
                              and recipe load.

hanzo-memory already had pluggable backends (SQLite + sqlite-vec via
.db.factory) and an MCP server; this fills in the gbrain-shape graph +
recipe surfaces so the Python SDK reaches feature parity with the bot
extensions in TS.
2026-05-10 18:39:39 -07:00
zeekay 78e61e5c31 style: black --target-version py312 (CI format-check fix)
No semantic changes — pure black reformat across the four files touched
in 55b55146 (hanzo-mcp 0.15.3 + hanzo-tools-browser 0.5.4).
2026-05-10 17:22:07 -07:00
zeekay 55b55146a4 hanzo-mcp 0.15.3 + hanzo-tools-browser 0.5.4: ZAP TextContent normalization, mDNS-only discovery
hanzo-mcp (0.15.1 -> 0.15.3):
- _normalize_mcp_result() collapses FastMCP list[TextContent] to parsed JSON
  for ZAP MSG_RESPONSE serialization
- Closes the "TextContent not JSON serializable" bug surfaced when driving
  the Hanzo extension from agent via tools/call
- Bump hanzo-tools-browser[playwright] floor to 0.5.4

hanzo-tools-browser (0.5.2 -> 0.5.4):
- mDNS-only ZAP discovery via _hanzo._tcp.local. (HIP-0069); no port pool,
  no lockfile, OS-assigned ports advertised by hanzo-zap-mdns
- zap_server.py: refactor for unique server_id + retract on stop
- Test suite trimmed to the new wire contract

Patch-bump both packages.
2026-05-10 17:17:13 -07:00
zeekay 1636a97996 fix(hanzo-tools-browser): 0.5.2 — import wire format from zap-protocol
The custom encode/decode and message-type constants in zap_server.py
were a byte-identical duplicate of zap-protocol (~/work/zap/zap-py).
Drop the duplicates, import from canonical:

  from zap.protocol import (
      ZAP_MAGIC, HEADER_SIZE, MAX_MESSAGE_SIZE,
      MSG_HANDSHAKE, MSG_HANDSHAKE_OK,
      MSG_REQUEST, MSG_RESPONSE,
      MSG_PING, MSG_PONG,
      ZAP_PORTS as DEFAULT_ZAP_PORTS,
      encode, decode,
  )

Single source of truth for the wire — any change (MSG_STREAM, frame
versioning, future Cap'n Proto migration) lands once and propagates.

zap-protocol added as a dependency. Verified at install:
  ZAP encode unified: True
  ZAP decode unified: True
2026-05-09 14:48:36 -07:00
zeekay 640977d94b feat(zap-server): auto-publish via hanzo-zap-mdns + retract on stop
Every ZapServer.start() now publishes its bound port + agent_label as a
`_hanzo-zap._tcp.local.` mDNS record (best-effort — silently skipped
when `hanzo-zap-mdns` isn't installed). Browser extensions and any
LAN-wide agent can discover the server via `hanzo_zap_mdns.browse()`
without hard-coded ports or fragile lockfile registries.

Publish runs on a worker thread (asyncio.to_thread) so zeroconf's
internal multicast-socket setup doesn't block our event loop and
trigger EventLoopBlocked on zeroconf >= 0.140.

stop() retracts the announcement before closing the WS.
2026-05-08 12:10:26 -07:00
zeekay 1ee67a2e13 fix(zap-server): race-free client cleanup when client_id is reused
When an extension reuses its client_id across consecutive ZAP connections
(probe followed by connectZap, both with mgr.state.extensionId), the
older websocket's finally clause was unconditionally popping the
client_id from _clients — deleting the new connection's freshly-installed
registration. The extension would then immediately disconnect because the
server-side state no longer had it, hammering reconnect loops.

Now the finally clause only removes the entry when it still points at
the closing websocket. Verified end-to-end: Firefox 1.9.x ZAP-native
connection holds steady; tabs/screenshot/evaluate round-trip in
11/73/5 ms.
2026-05-08 09:54:12 -07:00
zeekay 0dc6c65566 feat(tools-browser): ZAP-native 2-process architecture (0.5.0)
Each hanzo-mcp now hosts a ZAP server directly on the lowest free port
from [9999..9995], using a POSIX flock for cross-MCP arbitration. The
browser extension discovers and connects to this server without any
node-side bridge in the critical path.

- New hanzo_tools/browser/zap_server.py: wire format, server, lifecycle,
  client registry, leases, cluster-visibility config registry.
- _extension_command tries ZAP first (sub-1ms median round-trip on
  loopback), falls back to legacy HTTP bridge on :9224 only when no
  ZAP-connected client matches. Pinnable via BROWSER_TRANSPORT.
- New tool actions: list_mcp_instances, claim_browser, release_browser
  for sub-agent coordination across multiple parallel MCPs.
- Legacy CDP HTTP bridge no longer auto-starts (opt in with
  HANZO_CDP_BRIDGE_ENABLED=1). It remains available for non-ZAP MCP
  clients.

Tests: 30 new unit tests cover wire format, port arbitration,
multi-MCP coexistence, lease semantics, and cluster registry.
Bench shows 0.35ms median round-trip on local loopback.

Total tests: 4 baseline -> 35 passing (30 zap_server + 1 bench + 4 imports).
2026-05-07 21:32:43 -07:00
zeekay 3e34d5e624 feat(tools-browser): set_default_browser / use_browser actions for v1.9.0 bridge
The extension's bridge server (cdp-bridge-server.ts v1.9.0) added two
new actions for dynamic browser switching:

  set_default_browser — persist a default-target browser
  use_browser        — alias of set_default_browser

Both accept the browser name (firefox / chrome / safari / edge or a
specific instance ID like 'firefox-2') as the 'browser' field. The
bridge writes the choice to ~/.hanzo/extension/config.json so it
survives bridge restarts.

This commit:
- Adds the two action names to the Action Literal so the MCP tool
  accepts them.
- Adds them to extension_actions so they route through the bridge
  rather than falling to Playwright.
- The 'target_browser' parameter (already exposed) carries the
  browser name through to the bridge as 'browser', which is exactly
  what set_default_browser expects.

Bumps hanzo-tools-browser 0.4.5 → 0.4.6.
2026-05-07 21:07:22 -07:00
zeekay 376df8dd41 feat(tools-browser): add tab_id / client_id / target_browser params
Mirror the v1.8.3 extension bridge fix on the python side. The MCP
'browser' tool now exposes:

- tab_id — accepts the targetId returned by 'tabs' (e.g. "tab-1888868904")
  or a numeric tab id. Forwards as tabId so every action operates on the
  specified tab instead of the OS-active tab. Critical when the user has
  many windows open and the wrong one is in focus.
- client_id — pin to a specific connected extension instance (returned
  in 'status'). Use with 'list_browsers' to address one Chrome instance
  vs another Firefox instance bridged to the same hanzo bridge.
- target_browser — 'chrome' | 'firefox' per call. Per-call override of
  the global backend so a single MCP session can address Chrome and
  Firefox at different moments.

Also expose two action names that the bridge already supports:
  select_tab — bring a specific tab to focus (Target.activateTarget)
  list_browsers — enumerate every connected provider with details

The "tab-NNN" prefix from getTargets is automatically stripped and the
numeric id forwarded; the bridge's parseTabId() handles both formats.

Tested against extension v1.8.3 which lands the matching bridge changes.
2026-05-07 14:20:54 -07:00
zeekay e51dc8c00e chore: untrack node_modules, improve .gitignore 2026-04-18 17:15:22 -07:00
zeekay 3402e86c92 fix: ruff lint auto-fixes across hanzo-mcp and tests 2026-04-01 20:48:58 -07:00
zeekay 28641e7d73 fix: CTO review — version targets, slash command parity, dataclass default
1. pyproject.toml: ruff/mypy target py38→py312, remove 3.8-3.11 classifiers
2. command_suggestions.py: add /branch /commit /commit-push-pr /diff /stash /worktree
3. command_suggestions.py: fix aliases default None → Optional[List[str]]
2026-04-01 13:36:10 -07:00
zeekay a48f4a0c69 feat: hanzo-dev Python TUI + LSP + hooks + sandbox + vim + git + multi-auth + full parity
Rename hanzo-repl → hanzo-dev to match @hanzo/dev (Rust/TS).

New packages:
- hanzo-lsp: async LSP client (diagnostics, go-to-def, references, context enrichment)
- hanzo-hooks: pre/post tool use hooks (shell scripts, exit-code deny)
- hanzo-sandbox: container detection, filesystem isolation, Linux unshare

hanzo-dev TUI:
- Vim keybindings (Normal/Insert/Visual/Command modes, 37 tests)
- Git slash commands (/branch /commit /worktree /diff /stash, 13 tests)
- All slash commands at parity: /model /permissions /clear /resume /memory
  /init /export /session /plan /solve /code /auto /fast /remote-control
- Multi-provider auth: Anthropic OAuth, OpenAI device code, Hanzo PKCE, auto-detect

Auth:
- login_with_anthropic() — PKCE via console.hanzo.ai
- login_with_openai() — device code flow via auth.openai.com
- login_auto() — env var detection + saved credential fallback
- OAuthCredentialStore supports provider keys (hanzo/anthropic/openai)

Parity test suite (38 tests): SSE, MCP normalization, permissions, compaction,
token usage, PKCE RFC vector, config, backoff, session roundtrip, hooks.

348 tests total, 0 failures across Python + Rust + Universe E2E.
2026-04-01 10:41:46 -07:00
zeekay 200cef23b6 security: fix 3 HIGH + 3 MEDIUM findings from red team review
- H-1: guard negative abs_off in obj_bytes() (buffer read bypass)
- H-2: remove CERT_NONE from ZapClient TLS (MITM protection)
- H-3: add MAX_MESSAGE_SIZE check in ZapServer (OOM DoS)
- M-5: log exceptions instead of bare pass in server
- L-1: validate ZAP version in Message.parse()
- L-2: wrap req_id at u32 boundary in CloudClient
2026-03-31 20:57:16 -07:00
zeekay 1a3af83a0e feat: add CloudClient and rewrite wire protocol for Rust compatibility
- Add CloudClient for luxfi/zap binary protocol (TCP + auto-TLS)
- Rewrite wire.py Builder/ObjectBuilder to match Rust single-buffer architecture
- Fix response field offsets (CLOUD_RESP_BODY=4, CLOUD_RESP_ERROR=12)
- Use signed i32 relative offsets from absolute positions
- 28 unit tests + cross-language integration test passing
2026-03-29 15:13:32 -07:00
zeekay 3bd53b1f98 fix: lint — ruff format and fix for hanzo-tools-ui and hanzo-mcp 2026-03-27 20:00:21 -07:00
zeekay 8d7c928572 feat: add RAG search via Hanzo Cloud (search-docs + chat-docs)
Replace custom fastembed/sqlite-vec approach with Hanzo Cloud search
infrastructure. UI tool now has:
- ask: RAG chat about components via cloud-api.hanzo.ai/api/chat-docs
- semantic_search: hybrid fulltext+vector via cloud-api.hanzo.ai/api/search-docs
- rebuild_index: push components to Hanzo Cloud search index
- index_status: check search index stats

Uses publishable key (pk-hanzo-ui-search-2026) for reads, admin key for writes.
Same infrastructure as docs.hanzo.ai search.
2026-03-27 19:31:16 -07:00
zeekay d5e6520396 fix: registry client uses static JSON paths for CF Pages compatibility
Update RegistryClient to fetch from /api/registry/components.json and
/api/registry/components/{name}.json (pre-built static files), with
fallback to Next.js API routes. Works on CF Pages, GitHub Pages, and
server mode. Add lux registry URL (ui.lux.finance).
2026-03-27 19:15:46 -07:00
zeekay 07105edc8d feat: add UI registry server and tiered backend (local → registry → github)
Add FastAPI registry server that pre-fetches all component data from
hanzoai/ui on GitHub, caches in memory, refreshes every 15min. Serves
instant cached responses. Deploy at ui.hanzo.ai.

- registry/cache.py: eager full-cache with concurrent GitHub fetching
- registry/server.py: FastAPI endpoints for components, blocks, search
- registry/client.py: RegistryClient matching GitHubAPIClient interface
- Dockerfile for containerized deployment
- UiTool now uses 3-tier fallback: local disk → registry → GitHub API
- Bump hanzo-tools-ui to 0.2.0, server deps optional (fastapi, uvicorn)
2026-03-27 19:06:38 -07:00
zeekay b71b054f11 feat: enable ui tool in hanzo-mcp with local-first component reading
Add hanzo-tools-ui to workspace members and hanzo-mcp dependencies so
the ui tool is actually discoverable at runtime. Add LocalUIClient that
reads components directly from ~/work/hanzo/ui when available, falling
back to GitHub API. Adds list_packages and read_file actions. Fixes
GitHub config components_path to match actual repo layout (pkg/ui/primitives).
2026-03-27 18:50:09 -07:00
zeekay 54fbc30f44 fix: depend on hanzo-flow (hyphenated PyPI name) 2026-03-25 04:02:13 -07:00
zeekay 313e983295 fix: flow CLI binary is 'flow' not 'hanzo-flow' 2026-03-25 04:01:09 -07:00
zeekay c46ccf56a3 fix: update hanzo-flow entry point to flow.launcher (internal rename) 2026-03-25 02:39:47 -07:00
zeekay fb8cdaca1c feat: add hanzo-flow package (re-exports hanzoflow CLI as hanzo-flow) 2026-03-25 01:23:47 -07:00
zeekay f5f1a90ca3 chore: update SDK client, memory docs, and tests 2026-03-24 18:41:29 -07:00
zeekay e0a1c5f74a feat(memory): add namespace/key support + BlueRedChannel coordination class
- SQLite backend: namespace, key, tags, TTL, append support
- PluginMemoryService: full parity with TypeScript/Rust (list, stats, clear, tag, history, export/import)
- BlueRedChannel class for blue-red agent coordination protocol
- 30/30 tests passing
2026-03-24 18:03:30 -07:00
zeekay c5fb6e8d84 chore: update hanzo-kms package 2026-03-23 10:26:07 -07:00
zeekay db9245595d fix(api): add User-Agent header to APIs.guru fetch to fix CI 403
APIs.guru CDN blocks requests with Python's default User-Agent
(Python-urllib/3.x) from datacenter IPs, causing the weekly
Generate API Providers workflow to fail with HTTP 403.
2026-03-22 20:03:09 -07:00
zeekay f22f249fbd chore: update dependencies to latest 2026-03-21 11:45:05 -07:00
zeekay 6943d1a316 chore: update dependencies to latest 2026-03-21 11:01:05 -07:00
zeekay cc639684f9 feat(hanzo-tasks): Python SDK for durable task execution
New hanzo-tasks package wrapping temporalio SDK with Hanzo conventions:
- Client: connect, submit, get_result, cancel, signal, query
- Worker: register workflows/activities, poll and execute
- Pre-built workflows: AgentTaskWorkflow, PipelineWorkflow, FanOutWorkflow
- Pluggable agent executor via set_agent_executor()
- 28 tests passing
2026-03-19 10:29:31 -07:00
zeekay 695006518e rebrand: fix insights host URL and remove redundant env var fallback 2026-03-13 18:03:09 -07:00
zeekay 1e3f9d7752 rebrand: PostHog→Insights in MCP analytics 2026-03-13 16:52:56 -07:00
zeekay e30fa43710 refactor(hanzo-mcp): clean up ZAP server bridge 2026-03-13 16:52:56 -07:00
zeekay 205cc38299 test(zap,paas): add protocol and tool test suites
ZAP: 58 tests covering types, wire format encoding, server dispatch,
and full client-server integration over TCP (handshake, tool calls,
batch, ping, multiple clients, large payloads).

PaaS: 21 tests covering action routing, parameter validation, JWT
decode, auth fallbacks, deployment listing, and error propagation.
2026-03-13 16:52:56 -07:00
zeekay b0af04eef1 fix(curl): accept json parameter in MCP tool registration
Field(alias="json") doesn't translate to Python kwargs — FastMCP
passes the schema name directly. Rename json_body to json in the
registered function signature.
2026-03-13 16:52:55 -07:00
zeekay 6d1a5d059f feat(hanzo-zap): add ZAP SDK package to monorepo (0.6.1)
Zero-copy Agent Protocol SDK — client, server, types.
Patch bump from PyPI 0.6.0 to bring into monorepo workspace.
2026-03-12 18:30:18 -07:00
zeekay 67c908de84 fix(zap): replace websockets with native ZAP TCP transport
- Rewrite zap_server.py to use asyncio TCP (not websockets library)
- ZAP server now speaks native binary protocol over raw TCP
- Wire format: [magic:4][type:1][length:4 BE][JSON payload]
- Remove websockets from ide optional dependency
- Fix type annotations: any -> Any, dict -> dict[str, Any]
- Add proper frame reading with asyncio.StreamReader.readexactly()
- Support MAX_MESSAGE_SIZE (16MB) limit per ZAP spec
2026-03-12 16:23:41 -07:00
zeekay 6d89c28368 chore(hanzo-mcp): bump version to 0.15.0
HIP-0300 bidirectional action sync release:
- Full action parity across Python/TS/Rust implementations
- 13 unified tools with complete action coverage
2026-03-12 15:39:28 -07:00
zeekay 2c909f60dc fix: ruff format MCP files + update required tool count to 21 2026-03-12 01:40:46 -07:00
zeekay 338e2c7c88 fix: resolve mypy errors and update shell tool count in tests
- Fix type shadowing in forgiving_edit.py (needle_lines int/list conflict)
- Add return type annotations to prompts functions
- Fix change_type indexing for None case in utils.py
- Fix commit.message bytes/str handling in utils.py
- Add proper dict typing for tree entries in utils.py
- Update shell tool count from 8 to 9 in test assertions
2026-03-12 01:30:57 -07:00
zeekay 372213a2b9 fix: resolve all ruff lint errors (F401, F841, E741, I001)
Remove unused imports, fix unsorted import blocks, rename ambiguous
variable, and remove unused local variable assignment.
2026-03-12 01:12:30 -07:00
zeekay cf5f8a0ba8 fix(hanzo-mcp): update fallback version to 0.14.0, add watchdog to dev deps
- server.py: fallback version 0.12.5 → 0.14.0 (matches pyproject.toml)
- pyproject.toml: add watchdog>=3.0.0 to dev extras (required by dev_server.py)
2026-03-11 16:57:20 -07:00
zeekay 4d78af29b8 feat(hanzo-mcp): 100% MCP/ZAP protocol parity with method pass-through v0.14.0
- Add handle_method parameter to ZapServer for full MCP protocol parity
- Server routes resources/list, resources/read, prompts/list, prompts/get through ZAP
- All MCP methods work identically over ZAP binary WebSocket transport
- Matches Node @hanzo/mcp v2.4.0 ZAP parity implementation
2026-03-11 15:33:00 -07:00
zeekay 70b2b9c73e fix(zap): dual-protocol decode, fix tool extraction, persist auth token
- Decode supports both MCP ZAP and hanzo/dev wire formats
- Fix _start_zap_server to use _tool_manager._tools (not .items())
- Persist auth token to ~/.hanzo/mcp_token instead of random per-run
2026-03-11 13:26:46 -07:00