Commit Graph
218 Commits
Author SHA1 Message Date
hanzo-dev fe3c0eabdc release: browser-extension 1.9.26 — working login (hanzo-browser IAM client)
1.9.25 shipped the unregistered app-hanzo client_id, so the PKCE token
exchange failed invalid_client — login has been dead since v1.8.0. The
canonical registered client is hanzo-browser (universe IAM init_data;
redirectUris include https://hanzo.ai/callback). Contracts pinning the
registered client land in shared-config + cross-browser-parity tests.
v1.9.26
2026-07-03 00:00:14 -07:00
hanzo-dev e23fbe51f7 dxt: kill dead app-hanzo password-grant, canonical OIDC PKCE only (from PR #6)
Adopted from fix/dxt-login-and-ci-guards: the DXT auth tool offered a
password-grant login defaulting to the unregistered app-hanzo client —
removed; whoami/token/account read /v1/iam/oauth/userinfo, refresh
exchanges at /v1/iam/oauth/token, issuance is authorization-code+PKCE
only. dxt build script now actually builds (mcp cli → server.js) instead
of echoing success. PR #6's test-suite restorations are not taken — main
already restored those suites.
2026-07-02 23:59:10 -07:00
Hanzo AIandhanzo-dev 42bf555a4e fix(extension): login client_id app-hanzo→hanzo-browser (dead client since v1.8.0) — v1.9.25 2026-07-02 23:58:30 -07:00
hanzo-dev 8f8f8b5ae2 llm.hanzo.ai is dead — api.hanzo.ai is the one gateway; harden live HF tests
Migrate the last llm.hanzo.ai references (vscode MCP llm tool provider URL
+ gateway fallback, auth-chat e2e doc comments) to the canonical
https://api.hanzo.ai/v1 (OpenAI-compatible, IAM JWT at the gateway — see
hanzoai/ai). The browser extension chat paths already pointed at
API_BASE_URL = api.hanzo.ai.

model-hub's HuggingFace tests are real live-API integration tests, which
means they fail on HF's weather: repeated runs rate-limit and the 5s
default timeout reads as 6 test failures with nothing of ours broken.
Probe HF once at module load and describe.skipIf with the status logged
when HF itself refuses service; give live calls a 15s budget. A failure
while HF answers 200 is still a real failure. 243/243 across repeated runs.
2026-07-02 23:53:04 -07:00
hanzo-dev c783b89c35 e2e: repair popup specs stranded by the 1.9.3 UI and the tab-hosted-popup harness
Three specs still tested the pre-1.9.3 popup (#open-page-overlay FAB,
'Open Chat Panel' label) — the Extension E2E jobs have been red on them
since the FAB was killed. Repointed at the current contract
(#overlay-enabled + #open-panel, 'Open Chat').

The interaction specs (overlay show, picker tool, console tool) all
funnel through the popup's active-tab lookup, which cannot work when the
harness hosts the popup as a tab: the popup IS the active tab, so the
privileged-tab guard always takes the fallback. Each spec now sends the
exact message its button sends (page.overlay.show / tool.picker.start /
tool.console.start), explicitly targeted at the example.com tab, so the
background relay and content-script surfaces are still exercised end to
end. CI's file set (popup, popup-tools, sidebar): 30/30 locally.
2026-07-02 23:04:29 -07:00
hanzo-dev b3f718dfce release: browser-extension 1.9.25 — green pipeline end to end
Fix the two failures the 1.9.24 tag surfaced:

- test/claude-integration.test.ts bound a fixed port 3002 in beforeEach
  and raced the previous test's teardown — EADDRINUSE as an unhandled
  'error', a 10s hook timeout, then ECONNREFUSED (flaked 3 of 4 local
  runs). BrowserExtensionServer now exposes ready() (resolves on listen,
  rejects on bind failure instead of crashing the process) and a port
  getter; the test binds port 0 (OS-assigned), awaits ready(), and
  terminates the client so wss.close() reliably fires. Six consecutive
  green runs.

- CI Build and Publish packaged with the `zip` binary the arc runner
  image doesn't have (Publish red since 1.9.23); Publish also wrote the
  zips one directory above where its artifact globs and the Chrome Web
  Store upload (which additionally pointed at an unversioned filename)
  looked, and CI hardcoded the version from the root package.json.
  Package with `npx web-ext build` (already in the job for linting),
  version from the browser package.json everywhere, store upload path
  fixed, and the Linux job no longer ships a junk safari zip — the real
  bundle comes from the macOS job.

Root and browser package.json move in lockstep to 1.9.25 (root was left
at 1.9.23 by the 1.9.24 bump). npm + VS Code Marketplace already carry
1.9.24; the stores' first complete release of this line is 1.9.25.
v1.9.25
2026-07-02 21:52:58 -07:00
hanzo-dev c4424826f9 release: browser-extension 1.9.24 — one evaluate rule across Chrome+Firefox
shared/evaluable.ts is the single cross-browser rule for caller-supplied
JS: pickEvaluable accepts every code-param alias (expression/code/script/
function/js); wrapEvaluable passes bare expressions through and wraps
statement bodies in an async IIFE with the trailing value auto-returned.
Both dispatch paths consume it — browser-dispatch.ts (chrome.debugger
Runtime.evaluate, which also gains the plain 'evaluate' method alias
Firefox already accepted) and background-firefox.ts, whose previous
`return (${code})` wrap was a SyntaxError on any multi-statement body
and silently returned undefined for `expr;`. The Firefox path now always
settles promises in-page and routes rejections over the __hanzo_error
channel so callers see the real page-side error.

Tests: behavioral suite for the wrapper (evaluates wrapped output, not
string-matching); revive the three suites dead since the node bridge was
deleted — parity contracts repointed at browser-dispatch.ts, the IAM
contracts updated to the HIP-0111 canonical /v1/iam/oauth/token shape,
hub-wiring trimmed to the live background.ts surface, and the node-bridge
routing suite removed with its subject. 243 passing (was 186 + 3 dead
files).
v1.9.24
2026-07-02 21:38:18 -07:00
zeekay 8d5384c1de Merge remote-tracking branch 'origin/rip/api-callers-to-v1' 2026-07-02 13:18:34 -07:00
zeekayandClaude Opus 4.8 3633c5a6ff rip: migrate IAM /api/* callers to canonical /v1/iam/*
extension dxt server: /api/userinfo→/v1/iam/oauth/userinfo, /api/login→
/v1/iam/login. Clean break, no aliases (org rule: one way, /v1/).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 13:12:56 -07:00
355f6496fb Debrand: replace Casdoor name with Hanzo IAM in comments/docs/aliases (#5)
Co-authored-by: Hanzo AI <ai@hanzo.ai>
2026-06-30 14:59:31 -07:00
z 28c75ec6a8 docs(brand): add hero banner 2026-06-28 20:10:52 -07:00
z 22729e5218 chore(brand): dynamic hero banner 2026-06-28 20:10:51 -07:00
51f59c44ff fix(iam): migrate to canonical /v1/iam/oauth/* (HIP-0111) (#4)
Co-authored-by: Zach Kelling <z@zeekay.io>
2026-06-24 19:16:15 -07:00
Hanzo AI 1c2b9f7915 kms: clarify upstream API path note 2026-06-24 10:51:37 -07:00
zeekay 1298aea79a release: browser-extension 1.9.23 v1.9.23 2026-06-20 13:05:07 -07:00
1560cd1228 ci: run on self-hosted ARC pool (hanzo-build-linux-amd64/deploy), not GitHub-hosted (#3)
Co-authored-by: zeekay <z@hanzo.ai>
2026-06-19 20:34:40 -07:00
Antje WorringandClaude Opus 4.8 c6e3ac5533 docs: tidy LLM.md indexes; CLAUDE.md -> LLM.md symlink convention
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 09:24:21 -07:00
Antje WorringandClaude Opus 4.8 bb0cd246e8 ext 1.9.22: native-zap singleton — one native port, no host-spawn storm
connectNativeZap had no singleton guard: state.port was overwritten without closing the
prior port, and every onDisconnect scheduled another connect. Called from startup + the
3s reconnect, ports (+ their native hosts) accumulated into a host-spawn storm once the
router stopped rejecting duplicates. Now: tear down any prior port on entry; a replaced
port (state.port !== port) never reconnects. Exactly one native host per browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 00:42:36 -07:00
Antje WorringandClaude Opus 4.8 52076f2c25 ext: package.json is the single source of truth for version
build.js stamps the package.json version into every manifest (chrome/firefox/safari)
so the three can never drift. Syncs src manifests to 1.9.21.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1.9.21
2026-06-16 22:04:52 -07:00
Antje WorringandClaude Opus 4.8 03e081e83a ext 1.9.21: native ZAP is the one transport (Chrome+Firefox)
connectNativeZap (shared/native-zap.ts) registers the browser as a zapd provider
over native messaging and dispatches inbound ROUTE commands. Renames cdp-bridge.ts
-> browser-dispatch.ts; deletes the cdp-bridge-server.ts WS bridge. No ws://localhost,
no port roulette, no CDP fallback in the default boot path. Patch bump (no 2.0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 22:03:00 -07:00
Antje WorringandClaude Opus 4.8 3bb7d57a9f ext 1.9.20: re-enable legacy CDP/MCP transport as working fallback
ZAP discovery is unreliable for local hanzo-mcp: the server binds ephemeral
ports (not the probed [9999..9995]) and mDNS needs the unshipped
ai.hanzo.zap_mdns native helper. Re-enable ENABLE_LEGACY_TRANSPORTS so the
extension connects to the running CDP bridge (ws://localhost:9223/cdp +
HTTP :9224) for driving a logged-in Chrome profile. Builds on the 1.9.16-1.9.19
dispatch/CSP fixes. Bumps chrome+firefox manifests + package.json to 1.9.20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1.9.20
2026-06-15 14:07:15 -07:00
zeekay 2ad3d464b4 extension(browser): v1.9.19 — decomplected click + cross-window tab switch
hanzo.click: removed framework-specific hacks (Drupal AJAX lookup, jQuery
fallback). Those don't generalize — every new framework would need its
own hack. Replaced with a clean realistic-event-sequence dispatch:

  mouseover → mouseenter → mousemove → mousedown → focus → mouseup → click

with proper clientX/Y/button/buttons/composed. Events still have
isTrusted=false (a WebExtension can't change that), so frameworks that
explicitly check isTrusted (Drupal AJAX behaviors, some React libs)
will still reject — those need the v1.10.0 BiDi backend on the Python
bridge to produce real trusted browser input via Firefox's
--remote-debugging-port WebDriver BiDi endpoint.

Target.activateTarget: now ALSO calls windows.update(focused:true) so
cross-Firefox-window tab switching actually brings the OS window to
the foreground (not just the tab within its current window).

hanzo.switchTab: new Layer-3 ergonomic alias. Same implementation as
Target.activateTarget but with a human-readable name. Recommended for
everyday tab switching in scripts and the MCP browser_tool wrapper.

Architectural note: hanzo.click is the synthetic path (best-effort for
sites that don't check isTrusted). When the v1.10.0 Python bridge BiDi
backend lands, calls to hanzo.click will auto-route to Input.bidi
.dispatchMouseEvent for trusted browser-input events. That is the
proper decomplected design — backend per trustedness level, ergonomic
alias on top. No per-framework hacks in the extension.
v1.9.19
2026-06-10 13:10:16 -07:00
zeekay 5e270c061d extension(browser): v1.9.18 — propagate page-side throws, fix textarea fill
Two bugs caught while driving the SEC TCR form:

1) runFunc silently swallowed page-side errors.
   scripting.executeScript returns [{result, error, frameId}] per frame.
   We were mapping to r?.result which dropped the `error` field — when a
   page-side function threw, callers saw null instead of an error.

   Fix: keep the full InjectionResult shape and throw on first.error.

2) hanzo.fill called HTMLInputElement's value setter on a TEXTAREA.
   That throws TypeError "Illegal invocation" silently (now visible
   thanks to fix #1). The chained `|| HTMLTextAreaElement.prototype...`
   never executed because the first ?.set was truthy.

   Fix: per-tag dispatch — pick the matching prototype's native setter
   (INPUT/TEXTAREA/SELECT each get their own). Wrapped in try/catch with
   plain `el.value = value` as last resort.

Verified on www.sec.gov/forms/tcr-external-form:
  - hanzo.fill on textarea[name^="in_your_own_words"] now sets the value
  - hanzo.clear on same textarea now works
v1.9.18
2026-06-10 11:47:16 -07:00
zeekay bb6ed472be extension(browser): v1.9.17 — fix HTTP-action dispatch fallthrough
handleExtensionRequest() was returning "Unknown extension action" for
every Page.* / DOM.* / Input.* / hanzo.* method added in 1.9.16. The
handler had only routed monitor.*/audit.* and fell off the end for
everything else.

Fix: fall back to executeMethod (the canonical CDP-shaped dispatcher)
before returning Unknown. Strictly additive — all previously-working
actions continue through their existing branches.

Verified locally on www.sec.gov/forms/tcr-external-form: hanzo.listForm
returns 183 elements with full label association; hanzo.scroll +
hanzo.clickByText + hanzo.fillByLabel are now routable.

Next: a v1.10.x multi-backend Python bridge that adds WebDriver BiDi
(Firefox 129+), CDP (Chrome 124+), and safaridriver (Safari 17+) as
secondary backends for network interception, log streams, and other
capabilities the WebExtension scripting API cannot provide. Same wire
surface, prefix-based routing (BiDi.* / CDP.* / WD.*).
v1.9.17
2026-06-10 11:29:59 -07:00
zeekay 610367e6fd extension(browser): v1.9.16 — CSP-safe DOM ops + CDP-shaped surface
Decomplected three orthogonal layers:
  L3: hanzo.* ergonomic aliases
  L2: CDP-shape canonical primitives (Page.* DOM.* Input.* ...)
  L1: runFunc(tabId, fn, args) → scripting.executeScript({world: MAIN, func: ref})

Every DOM op now passes a real function reference to scripting.executeScript
instead of constructing Function(codeStr)(). The Function() path is blocked
by strict page CSP on sec.gov / github.com / any default-src 'self' SPA.
Direct function refs are exempt from page CSP per the WebExtension spec, so
the new path works everywhere.

Added (all CSP-safe):
  • hanzo.clickByText  fillByLabel  findByText  listForm  submitForm
  • hanzo.press        scroll        waitForText  waitForMutation
  • hanzo.uploadFile   dialogAccept  observe/observeRead/observeStop
  • Input.dispatchMouseEvent  Input.scrollWheel
  • DOM.querySelector  DOM.scrollIntoView  DOM.focus
  • Page.printToPDF

Rewrote with runFunc (CSP-safe):
  • hanzo.click  fill  check  uncheck  clear  getText  getHTML
  • hanzo.querySelectorAll

Capabilities handshake bumped to advertise the new method names.
Driver-side mapping in hanzo-tools-browser/browser_tool.py updated with
~100 snake_case action → wire-method entries (Python MCP surface).

API reference: dist/browser-extension/firefox/API.md
v1.9.16
2026-06-10 11:21:29 -07:00
Hanzo AI d268fa9298 cleanup: remove AI-slop summary / status / plan / report files
Reverts violations of the durable rule that current-state docs belong
in LLM.md and history belongs in git log. Removed files were session
handoffs, agent-style "complete success" / "1000%" reports, dated
audit dumps, and stub NOTES.
2026-06-07 13:34:07 -07:00
Hanzo AI 44fccae452 chore: drop stale hanzo-auth.ts.bak
Fossilized copy left over from an earlier rewrite of packages/tools/src/auth.
The live hanzo-auth.ts is the source of truth.
2026-06-03 11:26:39 -07:00
Antje WorringandClaude Opus 4.7 d002cc136c ZAP wire fixes + WebGPU on-demand HF model loader
Fixes the browser ↔ hanzo-mcp ZAP path end to end:

- shared/zap.ts: vendor ZAP_PORTS constant and restore localhost
  port-probe fallback in discoverZapServers when the mDNS native
  helper isn't available. Sequential probe (stops on first hit) so
  the Chrome console stays quiet on the happy path.
- src/manifest.json: add deterministic ``key`` (extension ID
  ``biingenefmanpecedoafkfajbnlgdmbl``) + ``nativeMessaging``
  permission so the ``ai.hanzo.zap_mdns`` helper can be invoked.
- background.ts: configurable HF model source for the WebGPU
  runtime via chrome.storage (``hanzo_model_url`` or
  ``hanzo_model_hf={repo,file,revision}``); falls back through HF →
  bundled artifact → remote providers.
- webgpu-ai.ts: cache-first fetch backed by the service-worker
  Cache API + progress logs, ``WebGPUAI.huggingFaceURL`` helper, and
  ``WebGPUAI.evictCache`` for rotating model artefacts. Drop-in for
  the existing single-blob loader; lays groundwork for sparse-load
  MoE work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:24:31 -07:00
Hanzo AI 4aebce7014 extension(vscode): drop HANZO_IAM_ env-var prefix, use canonical IAM_
Aligns the standalone MCP server's auth path with the canonical IAM_
env contract enforced by hanzo-iam 1.30.0 (python-sdk 3cb6a34) — there
is exactly one prefix, IAM_, with no upstream-brand alias chain.

- HANZO_IAM_ENDPOINT       → IAM_ENDPOINT
- HANZO_IAM_LOGIN_URL      → IAM_LOGIN_URL
- HANZO_IAM_CLIENT_ID      → IAM_CLIENT_ID
- HANZO_IAM_CLIENT_SECRET  → IAM_CLIENT_SECRET

Updates docs/BUILD.md and docs/MCP_INSTALLATION.md to match.
2026-05-14 23:40:22 -07:00
Antje Worring 2287b6a707 extension(browser): gate legacy MCP+CDP WebSockets behind feature flag
Module-load WS connections to ws://localhost:3001/browser-extension
(legacy MCP) and ws://localhost:9223/cdp (legacy CDP bridge) spam
ERR_CONNECTION_REFUSED on every reload when no local hanzo-mcp / CDP
server is running.

ZAP mDNS discovery (startZapDiscoveryLoop / discoverZapServers, added
in 1.9.13–1.9.15) is now the canonical transport, so the legacy paths
are dead-on-load by default.

Adds ENABLE_LEGACY_TRANSPORTS = false (top of background.ts) and gates:
- connectToMCP() body (early-return when off)
- The module-load call site for connectToMCP()
- The module-load call site for cdpBridge.startWebSocketServer(cdpPort)

Flip the flag to true when developing against a local WS server.
ZAP path is untouched.
2026-05-14 15:16:53 -07:00
Hanzo AI 8caa647106 ext 1.9.15: cdp-bridge routing tests + mDNS-only ZAP discovery contract
Browser extension 1.9.14 -> 1.9.15.

- Add cdp-bridge-routing.test.ts: result-unwrapping, awaitPromise, multi-tab fan-out
- Add cross-browser-parity.test.ts: IAM URL, ZAP wire, sidebar/overlay surface, inspect shortcut
- Add execute-script.test.ts: page-world injection contract
- Add webextension-polyfill.test.ts: Chrome vs Firefox parity for chrome.* / browser.*
- Update shared-zap.test.ts to the new mDNS-only contract (DEFAULT_ZAP_PORTS removed;
  HANZO_SERVICE_TYPE = _hanzo._tcp.local. is the canonical discovery entry)
- background.ts / background-firefox.ts: tab_id routing path for ZAP tools/call
- shared/zap.ts: HANZO_SERVICE_TYPE constant, drop legacy port-probe list
- manifest version bump to 1.9.15 (Chrome + Firefox)
- .gitignore: *.xpi / *.crx build artifacts

Driving Porkbun + CF dashboards end-to-end this session surfaced the tab_id
routing path; the new tests pin the contract so it doesn't silently regress.

Tests: 250 pass / 0 fail (vitest run).
Patch-bump.
v1.9.15
2026-05-10 17:17:45 -07:00
Hanzo Dev 07b9bf6239 fix(extension): 1.9.14 — drop data_collection_permissions (Firefox manifest warning)
Firefox 151 warns on `data_collection_permissions` — that key is not
yet in Firefox's accepted MV3 manifest schema. The warning was
sufficient to block clean loading of native-messaging permissions in
some contexts (visible in about:debugging as "Reading manifest:
Warning processing data_collection_permissions"), which broke our
mDNS discovery via runtime.sendNativeMessage with an opaque
"unexpected error".

Removed the field. No data-collection semantics depend on it on the
Firefox side; Chrome MV3 still ignores unknown keys.
2026-05-09 14:09:10 -07:00
Hanzo Dev 5932768548 fix(extension): 1.9.13 — mDNS-only discovery via _hanzo._tcp.local.
Per HIP-0069 — extension discovers MCPs via mDNS only, no port-probe
fallback. The legacy [9999..9995] DEFAULT_ZAP_PORTS array is gone;
non-compliant deployments (no mDNS responder, no native-messaging
helper installed) fail loudly so the operator can fix the install
rather than the extension silently picking the wrong server.

shared/zap.ts:
- Replaced DEFAULT_ZAP_PORTS export with HANZO_SERVICE_TYPE constant
  ('_hanzo._tcp.local.')
- discoverZapServers: mDNS-only, retries every 10s when the helper
  is unavailable or the browse comes back empty
- discoverViaMdns: throws on missing native-messaging API instead of
  swallowing — surfaces the install gap

background.ts:
- Removed DEFAULT_ZAP_PORTS / SHARED_ZAP_PORTS imports + zapPorts
  storage key from getPortConfig
- Both discovery callsites pass undefined for ports (mDNS routes them)
2026-05-08 13:24:13 -07:00
Hanzo AI aabc15a382 fix(extension): 1.9.12 — content-script port keep-alive + correct package metadata
The 1.9.11 alarms-based wake-up worked but felt fragile — the background
was still idling out between alarms. Switching primary keep-alive to
the canonical MV3 Port pattern: every content script in every loaded
http(s) tab opens a `zap-keepalive` chrome.runtime port back to the
background and pings it every 10s. As long as ANY tab holds a port,
the background event-page (Firefox) / service worker (Chrome) stays
loaded indefinitely. The WebSocket survives without polling.

Alarms remain as belt-and-suspenders for the no-tab edge case (fresh
browser window, only privileged pages open).

Package metadata corrected: monorepo root is now `@hanzo/extension`
with description "Hanzo AI Extension" — preparing the surface for
publishing the bits people actually want (zap shared module,
gui-primitives shim, mDNS package).
2026-05-08 12:25:19 -07:00
Hanzo AI 6ca23de63a fix(browser-extension): 1.9.11 — alarms-driven keep-alive across MV3 idle
Root cause of the "connect once, never reconnect" pattern: Firefox MV3
treats `"background.scripts"` as event pages and Chrome MV3 service
workers idle out after ~30s. Once the background unloads, every
setTimeout (including our connectZap retry) silently dies. The first
connect happens on initial load; once that WS closes the script
suspends and never comes back.

Fix: register a `chrome.alarms` / `browser.alarms` 30s alarm. Alarm
events wake the worker/page even when suspended. The handler is a
no-op when the ZAP connection is healthy and triggers
discoverZapServers() when it isn't — closing the gap that the
WebSocket reconnect chain alone could not.

Adds `alarms` permission to both Firefox and Chrome manifests.
2026-05-08 12:22:22 -07:00
Hanzo AI 179a5bc718 fix(browser-extension): 1.9.10 — drop application-level heartbeat
ZAP is a long-lived WebSocket. The OS delivers FIN/RST on real
disconnects, which fires ws.onclose → the reconnect chain shipped in
1.9.7. The 20s/8s PING/PONG dance added in 1.9.9 was both unnecessary
and broken: it overwrote ws.onmessage on every tick, stacking closures
indefinitely until the socket closed.

Reverted. The transport stays simple: connect, stream, on close
reconnect. No polling, no wrapper chain.
2026-05-08 12:13:36 -07:00
Hanzo AI 8677e55f30 fix(browser-extension): 1.9.9 — heartbeat keepalive + connection robustness
shared/zap.ts:
  Every successful connectZap now arms a 20-second PING heartbeat. If no
  PONG arrives within 8s the socket is force-closed so the existing
  reconnect chain (1.9.7) takes over. This catches the "half-open"
  failure mode where neither side gets ws.onclose — typical when the
  agent process is killed -9, a laptop sleeps mid-WS, or NAT keep-alive
  drops a stale connection between LAN hops.

  ws.onclose now also clears the heartbeat interval, so we never PING a
  socket whose owner is in the middle of being torn down.

Combined with 1.9.7's onerror retry and 1.9.8's MAIN→ISOLATED CSP
fallback + 30s evaluate timeout, the 1.9.x line is rock-solid for
low-latency tab driving across MCP restarts, page CSP, and any
hand-off between agents.
2026-05-08 12:10:26 -07:00
Hanzo AI 719cd51919 fix(browser-extension): 1.9.8 — Firefox driving, hardened evaluate path
executeScript now tries world: 'MAIN' first (so callers reading
page-internal state still work) and silently falls back to ISOLATED on
the recognisable CSP-blocked-Function() error string. This rescues every
strict 'script-src self' page (banking sites, Porkbun account pages,
GitHub, Google Docs) without forcing the caller to know which world it
needs.

Default Runtime.evaluate timeout bumped from 10s → 30s. Page-walks
against jQuery-rendered domain-management UIs with 1.5k+ DOM nodes
routinely need more than 10s for the first selector pass; caller can
still override via params.timeout for short queries.

Together with 1.9.7's reconnect-chain fix the extension can now drive
any Firefox tab end-to-end: stays glued to its ZAP server through
restarts, evaluates JS regardless of page CSP, and exposes the result
on a clean shape (1.9.2's null-coercion + exception-details wiring).
2026-05-08 11:51:47 -07:00
Hanzo AI ea747cff45 fix(browser-extension): 1.9.7 — connectZap retry chain survives bind-miss
connectZap previously chained a 3s reconnect on ws.onclose only. When the
WebSocket failed BEFORE a successful handshake (typical case: agent
process restarting and rebinding port 9999), Firefox hit ws.onerror →
resolve(null) → the reconnect chain died after a single attempt.

Now both onclose AND onerror schedule the same idempotent retry (guarded
by a one-shot flag so we never double-fire). Firefox stays glued to its
ZAP server across MCP restarts, server upgrades, and any transient bind
window — exactly what we need for low-latency tab driving.
2026-05-08 11:38:10 -07:00
Hanzo AI 3200927f8c fix(browser-extension): 1.9.6 — native mDNS discovery + version footer
Discovery (shared/zap.ts):
  discoverZapServers now tries mDNS via the native-messaging helper
  `ai.hanzo.zap_mdns` first (op=browse, 1.5s timeout), falling back to
  the legacy `[9999..9995]` port-probe only when the helper isn't
  installed or returns no services. With the helper in place we get
  collision-free LAN-wide discovery — the same agent can be found
  whether it's on this host or another machine on the network.

  See ~/work/zap/mdns/ for the helper + manifest:
    python/zap_mdns.py    publish() / browse() (zeroconf-backed, no-op
                          fallback when zeroconf isn't installed)
    python/omni.py        roles: mcp / browser / host / gateway over
                          shared `_hanzo._tcp.local.` service-type
    ext/helper.py         stdio native-messaging host
    ext/native-messaging-host.json   manifest for Firefox NMH path

Manifest:
  Adds `nativeMessaging` permission so the helper can be reached.

Popup:
  Footer now displays "Hanzo AI v1.9.6" (read from manifest at runtime
  so the chip always tracks the actual loaded build).
2026-05-08 11:34:37 -07:00
Hanzo AI 39cf5950a5 fix(browser-extension): 1.9.5 — drop @hanzo/ui, switch to @hanzo/gui
The extension no longer depends on @hanzo/ui. The single React consumer
(chat-widget.tsx) now imports from `@hanzo/gui`, which the build aliases
to a small local primitives shim at `src/gui-primitives.tsx`. The shim is
strictly monochrome plain DOM (Button, Textarea, Input, Card*, XStack,
YStack, Text) — sized for content-script bundles. When the full tamagui-
based @hanzo/gui adoption ships in a feature branch, swap the alias
target in build.js and remove the shim.

Why the local shim and not the upstream @hanzo/gui (npm) right now:
- the upstream is tamagui-flavoured: react-native-web + @hanzogui/*
  packages + the static compiler — far too heavy for the per-page
  content-script + background bundles we ship today;
- @hanzo/brand is strict monochrome anyway, so primitives carry no
  colour tokens — sibling CSS (popup.css/sidebar.css) does the styling
  with the white-luminance scale committed in 1.9.4.

Surface change: every `@hanzo/ui/primitives-common` import is now
`@hanzo/gui`, build.js no longer probes a sibling @hanzo/ui repo, and
`primitives-common-fallback.tsx` is replaced by the canonical
`gui-primitives.tsx`. Bundle drops ~200KB across firefox + chrome from
not pulling the @hanzo/ui dist.

Also includes the in-flight ZAP-bridge / Firefox auth / sidebar
auto-mount tweaks that were already in working-tree.
2026-05-08 11:05:21 -07:00
Hanzo AI 1781862fb1 fix(browser-extension): 1.9.4 — monochrome brand pass + on-page chat config
@hanzo/brand is strictly monochrome (primaryColor #FFFFFF, no chromatic
accents). Stripped every blue/violet/emerald/amber/red literal across
the in-page overlay, sidebar, and popup; replaced with a luminance scale
keyed off white. Status states (success/warn/error) now distinguished
by opacity, not hue.

On-page overlay (content-script):
- Replaces the brittle `looksLikePromiseResult` blue button with a white
  primary action (#fff bg / #0a0a0b text) per brand guidelines.
- Composer is now sticky-bottom: messages flex/scroll, composer
  flex-shrink: 0, panel min-height: 0 lets the layout collapse cleanly.
- Adds a pin-mode toggle in the header. `mode=push` reflows page content
  (sets html.hanzo-overlay-push-{side} → margin-{side}: var(width));
  `mode=overlay` floats over the page (default).
- Adds setMode / setWidth / setEnabled message handlers and persists all
  three in chrome.storage.sync alongside the existing overlaySide.
- Picks up overlayWidth (compact 320 / default 420 / wide 560) via
  --hanzo-overlay-width CSS var, used by both the panel itself and the
  push-mode html margin.

Popup:
- "Open Chat" honours overlayEnabled — falls back to standalone tab
  when disabled (or when active tab is privileged).
- Adds On-page chat panel checkbox + Width picker (Compact/Default/Wide)
  alongside the existing Pin Left/Right.

CSS / brand cleanup:
- sidebar.css usage bars (.bar-blue/.bar-violet/...) → white opacity scale.
- model-hub badges (.badge-cloud/.badge-local/.badge-hub) → mono-luminance.
- inspector-result element-{tag,id,class} → mono-luminance instead of
  blue/violet/green syntax tints.
- --success / --warning / --error in both stylesheets → white shades.
2026-05-08 10:50:20 -07:00
Hanzo AI cfd53c8142 fix(browser-extension): 1.9.3 — kill on-page FAB, edge-pinned sidebar, native FF sidebar
- Remove the round "AI" FAB launcher from the in-page overlay. It rendered
  as a 54px circle bottom-right and looked out of place; once toggled on
  it was sticky because hide() never reset data-enabled. Removed the
  button + CSS + listener entirely.
- Reposition the overlay as a full-height edge-pinned sidebar panel
  (top:0; bottom:0; right:0; width:min(420px,100vw)) instead of the old
  floating bottom-right card. Slides in via translateX rather than
  translateY+scale so it reads as a sidebar.
- Add data-side="left|right" with persisted overlaySide setting in
  chrome.storage.sync. Popup gains a Pin Left | Pin Right picker that
  flips the side on the fly and stores the preference for next page load.
- Unify the popup's two old buttons (Open Chat Panel + Toggle On-Page
  Overlay — both did the same thing in different words) into a single
  Open Chat button.
- Register sidebar_action in manifest-firefox.json so Firefox users who
  prefer the OS-native sidebar can reach the same chat surface via
  View → Sidebar → Hanzo AI.
2026-05-08 10:04:38 -07:00
Hanzo AI 4c5a0bb064 fix(browser-extension): patch-bump to 1.9.2 + perfect FF evaluate path
Revert 2.0.0 to 1.9.2 — internal refactor doesn't warrant a major bump.

Firefox MV3 evaluate fixes (1.9.2):
- Detect privileged URLs (about:, moz-extension:, view-source:, resource:,
  file:, chrome:) up front; raise an actionable error instead of letting
  scripting.executeScript silently return zero frames.
- runInPage now throws when results is empty OR results[0] is undefined
  (the latter signals a frame errored outside the wrapped try/catch —
  almost always page CSP blocking Function() in MAIN world). Old behavior
  returned undefined which JSON-stripped to a confusing {result:{}}.
- Runtime.evaluate always returns {result:{type,value}, value} with
  value coerced from undefined to null and a CDP-style exceptionDetails
  surfaced when evaluation errored.
- Replace the looksLikePromiseResult heuristic with explicit
  awaitPromise / await_promise parameter handling. The previous heuristic
  re-evaluated any empty-object result, masking real {} values.
2026-05-08 09:53:56 -07:00
Hanzo AI 11862defbc feat(browser-extension): ZAP-native, kill node bridge from critical path (2.0.0)
The Python hanzo-mcp now hosts the ZAP server directly. This extension
extends the shared ZAP module so Python servers can dispatch browser
RPCs back to the extension over the same socket — closing the loop on
the 2-process architecture.

- shared/zap.ts: handle inbound MSG_REQUEST and respond with
  MSG_RESPONSE. New setZapRequestHandler(mgr, fn) installs the
  dispatcher.
- background.ts (Chrome): wire cdpBridge.dispatchMethod as the inbound
  ZAP handler.
- background-firefox.ts: expose dispatchMethod and wire it as the
  inbound ZAP handler. Reorder so discoverZapServers fires AFTER the
  handler is installed.
- cdp-bridge.ts: add public dispatchMethod() helper for ZAP-server
  initiated requests.
- cdp-bridge-server.ts: deprecation header. No longer in the critical
  path; kept only for legacy non-ZAP clients.
- mcp/src/zap-server.ts: fix wire-constant mismatches with
  shared/zap.ts (MSG_RESPONSE was 0x12, must be 0x11; PING/PONG were
  0xf0/0xf1, must be 0xFE/0xFF).

Tests: 250 vitest cases pass (was 247, +3 for setZapRequestHandler).
shared-zap.test.ts now covers 31 cases (was 28).

Manifest version 2.0.0 reflects the architecture pivot; wire format
itself is unchanged from 1.9.x (still
[0x5A 0x41 0x50 0x01][type:1][length:4 BE][JSON]).
2026-05-07 21:33:02 -07:00
Hanzo AI 962bc8ce26 feat(bridge): Firefox-first default + dynamic browser switching
The CDP bridge now supports multiple connected browsers concurrently
(it always did; this fixes the routing) and prefers Firefox when no
explicit target is specified. Order: firefox > safari > edge > chrome.

User explicitly said personal browser is Firefox — Chrome was
silently winning whenever both extensions were active because the
bridge picked the first-registered client. Now Chrome only wins
when it's the only one connected.

- resolveClient(): walks the DEFAULT_BROWSER_PREFERENCE list to find
  the first connected match. Last-resort fallback is the
  first-registered client.
- defaultBrowser config: persisted to ~/.hanzo/extension/config.json
  so the preference survives bridge restarts. Read on construct.
- New 'set_default_browser' / 'use_browser' actions: agent can flip
  the default at runtime without restarting the bridge.
- list_browsers now returns defaultBrowser + preferenceOrder so
  callers can see what would be picked.

The agent can now talk to multiple browsers simultaneously by passing
'browser' as a per-call target ('firefox', 'chrome', 'safari', or a
specific instance ID like 'firefox-2'). Without a target, the
configured default (or Firefox by preference) wins.
v1.9.0
2026-05-07 21:04:08 -07:00
Hanzo AI 0cc7d4972f feat(browser): right-anchored chat overlay everywhere + Chrome awaitPromise
- popup.ts: 'Open Chat Panel' now opens the in-page right-anchored
  overlay instead of the browser-native sidebar. Firefox's sidebar_action
  always rendered on the user's preferred side (default left) wrapped in
  the native sidebar-icon strip — both unwanted. Chrome's sidePanel was
  inconsistent with the overlay's behaviour. Single right-anchored
  surface across every browser.
- manifest-firefox.json: drop sidebar_action entirely. No more native
  Firefox sidebar; the floating right-side overlay (already at
  right:20px;bottom:20px in content-script.ts) is the only chat surface.
- cdp-bridge.ts: add awaitPromise:true to Runtime.evaluate so async
  IIFEs (the standard pattern for fetch+credentials:include in MCP
  evaluate calls) resolve to the actual value instead of returning {}.

Bumps 1.8.7 → 1.8.8.
v1.8.8
2026-05-07 20:30:14 -07:00
Hanzo AI 43a81de0d0 fix(auth): /v1/iam/login/oauth/access_token instead of /oauth/token
The Hanzo gateway exposes Casdoor's
at  (verified via probe — POST
returns 401 JSON with proper error semantics). The bare /oauth/token
listed in iam.hanzo.ai/.well-known/openid-configuration returns 404
in prod — the gateway has no rewrite for it.

Token exchange + refresh now hit the working path and use proper
application/x-www-form-urlencoded body per RFC 6749. JSON bodies were
silently rejected by Casdoor's token endpoint (it only accepts form-
encoded for the OAuth path).

Bumps 1.8.6 → 1.8.7 in root + browser package.json + both manifests.
v1.8.7
2026-05-07 20:08:29 -07:00
Hanzo AI 6503fa614d fix(browser): Firefox MV3 evaluate + screenshot
Firefox MV3 removed `browser.tabs.executeScript` (the string-based
MV2 API the bridge was still calling). Every evaluate from a Firefox
client returned `{}` because the call rejected silently. Replaced
with `browser.scripting.executeScript({ target, world: 'MAIN', func,
args })`, with the legacy `tabs.executeScript` retained as a fallback
for older Firefox builds.

Also fixed runInPage's wrappedCode: when injected via Function(body)()
the body must `return` explicitly. The previous IIFE-as-expression
trampoline discarded the result and the caller saw {} for every
evaluation.

Screenshot fix: Firefox's `tabs.captureVisibleTab(opts)` is interpreted
as `(windowId)` when called with a single object — the polymorphic
signature accepts only `(windowId, opts)`. Now resolves the target
tab's windowId, briefly activates the tab if needed, captures, then
restores focus. Also returns `{data, format, size}` so the Python
side has all three fields it expects.

Bumps 1.8.5 → 1.8.6 in root + browser package.json and both manifests.
v1.8.6
2026-05-07 19:54:18 -07:00
Hanzo AI 494cb3a38c release: v1.8.5 — fix root package.json so workflow produces v1.8.5-named assets v1.8.5 2026-05-07 18:33:35 -07:00