325 Commits
Author SHA1 Message Date
Hanzo Dev 51d6b45667 feat(github): Hanzo AI GitHub App — PR review, issue triage, @hanzo mentions
A GitHub App (Octokit + webhooks) built on @hanzo/ai (headless model client)
and @hanzo/iam (identity). Reviews pull requests, triages new issues, and
answers @hanzo mentions in issue/PR comments.

Pure cores (network-free, exhaustively unit-tested):
- webhook.ts  X-Hub-Signature-256 HMAC verify (constant time) + event routing
- review.ts   diff windowing/truncation, prompt assembly, review formatting
- triage.ts   issue prompt, severity/label parsing (labels ∩ repo labels)
- mention.ts  thread-context answer prompt

Impure edges compose them: config (env boundary), hanzo (thin @hanzo/ai — the
one model path), github-api (thin Octokit .request wrappers), app (App +
handlers → route → dispatch), server (health + graceful shutdown).

84 vitest tests green; tsc --noEmit clean; build succeeds.
Webhook path /v1/github/webhooks (Hanzo /v1, never /api/). Secrets env-only.
2026-07-03 21:24:23 -07:00
Hanzo Dev aa8444aff6 feat(raycast): Hanzo AI for Raycast — Ask + quick actions over @hanzo/ai
New @hanzo/raycast package (Raycast manifest name: hanzo-ai): the Hanzo
command bar. One view command (Ask Hanzo — Form → streaming Detail) and
five no-view quick actions (Summarize, Explain, Rewrite, Translate, Fix
Grammar) that read the frontmost app's selection (clipboard fallback),
run it through the published @hanzo/ai over api.hanzo.ai /v1, and paste
or copy the result back.

Pure core, thin adapters (mirrors @hanzo/canva + @hanzo/figma):
- config.ts   gateway URLs, default model (zen5), hk- key guard
- actions.ts  action catalog, prompts, context assembly + truncation,
              capture policy (chooseSubject), response parsing
- hanzo.ts    the only caller of @hanzo/ai: runChat / streamChat / listModels
- selection.ts the only caller of @raycast/api I/O
- run-action.ts shared quick-action runner; per-command one-liners
- ask.tsx     the streaming view command

Auth: the hk- key is a secure Raycast preference (never committed).
51 vitest tests (catalog, prompts, context, request shaping incl.
streaming, parsing, capture policy). tsc --noEmit clean; ray build
succeeds; ray lint clean (ESLint 9 flat config + Prettier).

Registers packages/raycast in pnpm-workspace.yaml.
2026-07-03 21:23:57 -07:00
Hanzo Dev 6772773b40 feat(design): Figma + Sketch + Canva — Wave 3 complete
Design tools on the shared design-core (rewrite/localize/critique/variants/
content-fill), all on published @hanzo/ai + @hanzo/iam:
- @hanzo/figma — plugin: code.ts↔iframe messaging, text-node write-back, a11y
  critique using real layer colors, insertable variants (56 tests).
- @hanzo/sketch — .sketchplugin: WebView panel + sketch/dom write-back (43 tests).
- @hanzo/canva — Apps SDK panel: generate/rewrite/translate/ideas, insert/replace
  text elements (40 tests).

CI: figma/sketch/canva test steps.
2026-07-03 21:10:45 -07:00
Hanzo Dev b5a44c8b66 Merge remote-tracking branch 'origin/feat/figma' 2026-07-03 21:10:11 -07:00
Hanzo Dev 0f0def8287 Merge remote-tracking branch 'origin/feat/canva' 2026-07-03 21:09:38 -07:00
Hanzo Dev 71f76cd97e feat(canva): Hanzo AI for Canva — Apps SDK copy assistant
A side-panel Canva app over the published @hanzo/ai: generate copy, rewrite
the selected text in place, translate, brainstorm content ideas, and ask.

- design-core.ts (PURE): the five-action catalog, prompt builders, design-
  context assembly + budget truncation, and response parsing (fence/quote/
  list-marker stripping). Mirrors @hanzo/figma's design-core. Fully unit-tested.
- hanzo.ts: thin over @hanzo/ai (createAiClient) — the only model-gateway path.
- canva.ts: thin over @canva/design — observe the plaintext selection, replace
  it via a fresh draft, insert a new text element at the cursor.
- app.tsx: the @canva/app-ui-kit panel (model picker, action picker, output,
  ideas). index.tsx mounts it under AppUiProvider.
- config.ts: api.hanzo.ai /v1 gateway, default zen5, hk- key guard.

40 vitest tests green, tsc --noEmit clean, esbuild build succeeds. Registered
in pnpm-workspace.yaml.
2026-07-03 21:08:04 -07:00
Hanzo Dev c262c6e506 feat(sketch): Hanzo AI for Sketch — native design-AI plugin
@hanzo/sketch — a Sketch plugin adapter over the shared Hanzo design-AI
action catalog, mirroring @hanzo/figma/@hanzo/canva. Five commands:
summarize/critique, rewrite, translate, content-fill, ask.

- design-core.ts (PURE, 43 vitest tests): the action catalog, selection
  context assembly/truncation, @hanzo/ai request shaping (createAiClient,
  @hanzo/ai@^0.2.0), and per-layer / prose response parsing.
- sketch-bridge.cjs: the only sketch/dom boundary — readSelection,
  applyLayerEdits (layer.text = …), insertNote.
- command.cjs: opens the WebView, bridges messages; key stored via
  sketch/settings (never bundled/committed).
- webview/: HTML + TS panel that runs @hanzo/ai (model call where fetch
  exists), previews edits, applies them back.
- build.js: assembles the .sketchplugin bundle (esbuild, two runtimes).

Model calls route through api.hanzo.ai /v1 via @hanzo/ai only.
2026-07-03 21:07:07 -07:00
Hanzo Dev 7c5557a901 feat(figma): Hanzo AI for Figma plugin over @hanzo/ai
New @hanzo/figma package: a Figma + FigJam plugin that rewrites, translates,
content-fills, critiques (with a11y notes), renames layers, and generates copy
variants for the selected layers — built on the published @hanzo/ai headless
client against api.hanzo.ai.

Two-thread architecture per the Figma plugin model:
- code.ts (main): serializes the selection into a plain SelectionSnapshot
  (text nodes + structure + colors, node-budgeted) and applies write-backs
  (font-loaded setCharacters, rename, createText) — full figma.* access, no net.
- ui.ts (iframe): the only place the model is called; streams via design-core,
  parses per action, posts edits/inserts back to code.ts over a typed
  postMessage protocol (messaging.ts).
- design-core.ts (PURE): the design-action catalog (system+user prompt builders
  per action), selection->context assembly + budget truncation, response parsing
  (JSON id->value edit maps with id allow-listing, variant-list splitting), and
  the @hanzo/ai run funnels over an injectable client.

Key pasted in the UI, validated via /v1/models, stored in figma.clientStorage;
manifest networkAccess allowlists only api.hanzo.ai. esbuild build emits the
three Figma files (code.js IIFE, ui.html with JS+CSS inlined, manifest.json).

56 vitest tests green (design-core + messaging guards); tsc --noEmit clean.
2026-07-03 21:07:06 -07:00
Hanzo Dev b24f0eb76e feat(business): HubSpot + Shopify — Wave 2 complete
- @hanzo/hubspot — CRM card UI extension + serverless fns: record → AI →
  Note/Task write-back (correct HUBSPOT_DEFINED associations); key server-side (75 tests).
- @hanzo/shopify — embedded admin app: product description/SEO generation +
  order summaries, productUpdate write-back (only changed field); dual HMAC
  verify (OAuth callback + webhooks), secret isolation proven (108 tests).

Both on the published @hanzo/ai@0.2.0 + @hanzo/iam. Wave 2 (Salesforce · DocuSign ·
Notion · Zendesk · HubSpot · Shopify) all on main.
2026-07-03 20:55:10 -07:00
Hanzo Dev 5c76eab8bc Merge remote-tracking branch 'origin/feat/hubspot' 2026-07-03 20:54:34 -07:00
Hanzo Dev 68df12bae2 Merge remote-tracking branch 'origin/feat/shopify' 2026-07-03 20:54:07 -07:00
Hanzo Dev 24bec0883f feat(hubspot): Hanzo AI for HubSpot — CRM card + AI over records (@hanzo/hubspot)
A HubSpot public app on the developer-projects platform: a CRM card UI
extension (crm.record.tab, on contacts/companies/deals) backed by two
serverless functions. Summarize a record, draft an email, suggest next
steps, or ask — grounded in the record + its associated records — and
write the result back as a Note or Task.

Built on the published @hanzo/ai@^0.2.0 headless client (createAiClient,
/v1 chat.completions + models.list) + @hanzo/iam. The Hanzo API key
(HANZO_API_KEY secret) and the per-portal OAuth token live only in the
serverless backend; the browser never sees them.

Pure, unit-tested core (config, hanzo call + context windowing + the four
actions, HubSpot CRM v3 request shaping/parsing, OAuth token exchange,
record→context assembly, Note/Task write-back payloads). 75 vitest tests
green, tsc --noEmit clean, dual ESM+CJS build so the CJS serverless
runtime can require it.
2026-07-03 20:53:24 -07:00
Hanzo Dev e79d3a853e feat(shopify): Hanzo AI for Shopify — embedded admin app on the shared foundation
Add packages/shopify (@hanzo/shopify): a Shopify embedded admin app that puts
Hanzo AI over the store as system of record.

- Product content: generate/rewrite descriptions + SEO title/meta from product
  attributes, then write back via the productUpdate mutation (only changed fields).
- Orders/support: summarize an order, draft a customer reply, extract issues.
- Ask: freeform question over a product or order, grounded in its data.

Server (src/server, holds the only secrets):
- oauth.ts   Shopify OAuth request shaping + OAuth-callback HMAC verify (hex over
             Shopify-canonical URLSearchParams form) + state (CSRF) check.
- shopify-api.ts  Admin GraphQL wrappers (getProduct/getOrder/updateProduct) —
                  pure request shaping + parsing + productUpdate userErrors.
- hanzo.ts   thin over the PUBLISHED @hanzo/ai createAiClient; product/order
             context assembly + prompt building. No transport reimplemented.
- actions.ts product + order AI action catalogs, one prompt each over ask.
- webhooks.ts  webhook HMAC verify (base64 over RAW body) + topic routing
               (orders/create, app/uninstalled, the 3 GDPR topics).
- server.ts  OAuth + /v1/* proxy the frontend calls + /webhooks. HMAC verified
             before any param/body is trusted; shop validated to *.myshopify.com.

Frontend (src/app): Polaris + App Bridge React panel — api.ts (proxy client,
no secrets), context.ts (launch context → GID), session-fetch.ts (App Bridge
session-token fetch), App/Assistant/main. App Bridge loaded from Shopify CDN;
public API key stamped into the app, secret never bundled.

Plus shopify.app.toml, an admin-link extension, build.js (esbuild: app bundle +
Node server), tsconfig, vitest config, README. 108 vitest tests (pure), tsc
--noEmit clean, build green. Model calls via @hanzo/ai to api.hanzo.ai/v1 only.

Register packages/shopify in pnpm-workspace.yaml.
2026-07-03 20:53:10 -07:00
Hanzo Dev 21686d4eda feat(zendesk): Hanzo AI ticket-sidebar app (@hanzo/zendesk)
ZAFv2 ticket_sidebar Support app on the published @hanzo/ai@0.2.0: summarize
ticket, draft reply (agent voice), suggest macro/next-steps, ask; write-back via
ticket.comment.appendText (public reply / internal note). Hanzo key is a secure
ZAF setting, proxied server-side so it never reaches the browser (49 tests). CI step added.
2026-07-03 20:49:22 -07:00
Hanzo Dev 1f13590df8 feat(zendesk): Hanzo AI ticket-sidebar Support app on @hanzo/ai
ZAFv2 ticket_sidebar app (@hanzo/zendesk). Reads the current ticket +
conversation via the ZAF client, offers Summarize / Draft reply / Suggest
next steps / Explain / Ask over the published @hanzo/ai headless client, and
writes results back into the reply editor or as an internal note.

The Hanzo API key is a SECURE installation setting: model traffic is proxied
through Zendesk's request API (secureFetch + {{setting.hanzo_api_key}}), so the
key never reaches the browser while still using createAiClient. Pure, tested
ticket-windowing (newest-first, honest scope note) and message shaping.

49 vitest tests, tsc --noEmit clean, esbuild -> dist/ ZAF bundle (zcli-ready).
2026-07-03 20:47:41 -07:00
Hanzo Dev 4b84ccfcd6 feat(business): Salesforce + DocuSign + Notion on the shared foundation
Wave 2 batch 1:
- @hanzo/salesforce — LWC assistant + Apex callouts on record pages, read record →
  AI → write-back (Task/Note/Chatter), Named+External Credential (key never in Apex).
- @hanzo/docusign — summarize/extract-terms/risk-flag agreements + Connect webhook
  (HMAC-verified) auto-summary; envelope docs → pdf.js → @hanzo/ai (103 tests).
- @hanzo/notion — summarize/draft/extract pages → databases, append AI blocks,
  server-held OAuth token + proxy (104 tests).

CI: add notion/docusign/salesforce test steps. All on @hanzo/ai (now published
0.2.0 as the canonical headless client) + @hanzo/iam.
2026-07-03 20:33:13 -07:00
Hanzo Dev dc1c9b4faa Merge remote-tracking branch 'origin/feat/docusign' 2026-07-03 20:32:17 -07:00
Hanzo Dev bbd424bb36 Merge remote-tracking branch 'origin/feat/notion' 2026-07-03 20:32:17 -07:00
Hanzo Dev f85de497ee feat(docusign): Hanzo AI for DocuSign — AI over agreements (@hanzo/docusign)
Extension-App panel + Connect webhook that summarize, extract key terms,
risk-flag clauses, and draft memos over DocuSign envelopes, on the shared
@hanzo/ai + @hanzo/iam foundation over api.hanzo.ai /v1.

- OAuth Authorization Code Grant (account-d/account.docusign.com), server-side
  client_secret, base_uri discovery via /oauth/userinfo (pure shaping).
- eSignature REST v2.1 wrappers (envelopes, documents, combined PDF).
- Connect webhook: X-DocuSign-Signature-N HMAC-SHA256 verify over raw body,
  constant-time, multi-key; envelope-completed auto-summarize pipeline.
- PDF text extraction (pdf.js, pure itemsToText), document windowing/truncation,
  one 'ask' path shared by the four actions, the panel, and the webhook.
- 103 vitest tests (config, oauth, api, webhook HMAC, pdf, hanzo, actions,
  envelope pipeline, panel). tsc clean, esbuild build green.
- Registers packages/docusign in pnpm-workspace.yaml (one line).
2026-07-03 19:40:52 -07:00
Hanzo Dev 1533315944 feat(notion): Hanzo AI for Notion — OAuth integration + web app
Notion has no in-app UI-extension surface, so this ships a public OAuth
integration + REST v1 client and a small web app that operates on the
user's pages/databases. Four actions over a page:

- Summarize: recursively read blocks -> text, summarize via @hanzo/ai,
  append as a callout.
- Draft/expand: generate content continuing the page.
- Extract -> database: pull {task, owner, due} action items and create
  one database row per item.
- Ask: freeform Q&A over page context.

Architecture mirrors @hanzo/clio: pure, unit-tested request/blocks/chat
modules with thin DOM + http glue. The Notion client_secret AND bot token
stay server-side (server.ts) — the browser holds only an opaque session id
and proxies every Notion call through an allow-listed /notion/* proxy.
Model calls route through the createAiClient contract to api.hanzo.ai/v1.

104 vitest tests (OAuth shaping, REST wrappers + pagination, blocks<->text
both directions, @hanzo/ai request shaping, context assembly/truncation,
action-item parsing, proxy allow-list). tsc --noEmit clean; esbuild builds.
2026-07-03 19:38:28 -07:00
Hanzo Dev 0379404940 feat(salesforce): Hanzo AI for Salesforce — LWC assistant + Apex gateway client
New @hanzo/salesforce SFDX package: a Lightning Web Component assistant on
Account/Opportunity/Case/Lead/Contact record pages, backed by Apex callouts to
the api.hanzo.ai/v1 gateway (OpenAI-compatible, same wire format as the browser
extension, Office add-in, gworkspace add-on, and Slack app).

- HanzoClient: pure request/response core (buildMessages/requestBody/
  extractContent/parseModels) + ask/listModels callouts via the Hanzo_API
  Named Credential — the key never touches Apex.
- HanzoContext: SOQL record + related-list context assembly per object,
  WITH SECURITY_ENFORCED, truncated to a token budget.
- HanzoController: @AuraEnabled complete/listModels/writeBack; write-back
  creates a Task, a ContentNote, or a Chatter FeedItem (as user).
- LWC hanzoAssistant: model picker + quick actions (Summarize, Draft email,
  Next steps, Risks) + prompt + write-back, on record pages.
- Named + External Credential for api.hanzo.ai, permission set with principal
  access, Connected App, Hanzo_Config__mdt default-model config.
- Tests: HanzoClientTest + HanzoControllerTest with HttpCalloutMock (asserts
  URL /v1/chat/completions, bearer, body model+messages, response parse, and
  each write-back) — >75% coverage; vitest over the truncate helper.
- Registered packages/salesforce in pnpm-workspace.yaml.
2026-07-03 19:37:36 -07:00
Hanzo Dev 3e90fd6e3a feat(teams): Hanzo AI for Microsoft Teams — Wave 1 comms complete
@hanzo/teams — bot (1:1/channel) + message extensions, Adaptive Cards via
@hanzo/cards, Action.Submit/Execute dispatch, packaged Teams app zip (69 tests).
CI: add teams test step. Wave 1 (Slack · Teams · Zoom/Meet) all on main.
2026-07-03 19:21:14 -07:00
Hanzo Dev e2bd0cacc5 Merge remote-tracking branch 'origin/feat/teams'
# Conflicts:
#	pnpm-workspace.yaml
2026-07-03 19:20:33 -07:00
Hanzo Dev 1c63e5592e feat(comms): Slack + Zoom/Meet apps on the shared foundation; CI build-order fix
Wave 1 comms hubs land:
- @hanzo/slack — /hanzo slash command, App Home, message shortcuts, thread-context
  quick actions, OAuth install; UI 100% @hanzo/cards Block Kit (68 tests).
- @hanzo/meetings — Zoom App + Google Meet add-on panels + Zoom webhook
  transcript→summary (signature-verified); web panels over @hanzo/ai (70 tests).

CI: build @hanzo/cards before dependent tests (consumers resolve its built dist/),
and add slack + meetings test steps.

Note: the headless @hanzo/ai client isn't published under 'latest' yet (npm
@hanzo/ai@0.1.1 is the React <HanzoAI/> UI); each adapter ships a client to the
exact createAiClient /v1 contract as an interim, to become a one-line re-export
once the headless client publishes.
2026-07-03 19:19:44 -07:00
Hanzo Dev 0591d31d95 feat(teams): Hanzo AI for Microsoft Teams (@hanzo/teams)
A Bot Framework (botbuilder) app bringing Hanzo AI into Microsoft Teams,
built on the shared foundation — @hanzo/ai (headless client), @hanzo/iam
(identity), @hanzo/cards (canonical PanelSpec -> Adaptive Card).

- Bot (TeamsActivityHandler): 1:1 + channel chat; @mention/DM a prompt ->
  completion -> reply as a Hanzo assistant Adaptive Card panel.
- Adaptive Card actions: model picker (Input.ChoiceSet), quick actions
  (Draft/Summarize/Explain/Extract action items), prompt input + submit,
  all routed by dispatch() on data.action (from @hanzo/cards).
- Message extension "Ask Hanzo" (action) on a message + "Compose with
  Hanzo" (query) to insert an AI draft; pulls message/thread context.
- Teams app manifest (schema v1.16) + color/outline icons + a dependency-
  free zip build step for the sideloadable app package.
- Pure logic modules (dispatch/panels/context/hanzo/identity/config) with
  vitest coverage; tsc clean; tsup build (library + runnable server).

api.hanzo.ai /v1 via @hanzo/ai only; secrets are env-only (never committed).
2026-07-03 19:19:13 -07:00
Hanzo Dev 0ec82c318b Merge remote-tracking branch 'origin/feat/meetings' 2026-07-03 19:18:08 -07:00
Hanzo Dev b31bcbef56 feat(meetings): Hanzo AI for Zoom + Google Meet
@hanzo/meetings — an in-meeting assistant for Zoom and Google Meet over one
shared panel, wired to api.hanzo.ai/v1 via @hanzo/ai and hanzo.id via @hanzo/iam.

- Shared panel (src/panel): model picker + quick-action chips (Summarize /
  Action items / Follow-up email) + streaming output + prompt, host-agnostic via
  a PanelHost seam. Same UX language as the Office task pane / PDF workspace.
- Zoom App (src/zoom): in-meeting side panel over @zoom/appssdk + a server for
  OAuth install and the recording.completed / meeting.ended webhook — verifies
  the Zoom HMAC signature, answers endpoint.url_validation, downloads the cloud
  recording VTT, and summarizes it via @hanzo/ai.
- Google Meet add-on (src/meet): side-panel web app over @googleworkspace/meet-addons.
- src/hanzo.ts: pure transcript windowing/truncation (honest context note),
  WebVTT parse, OpenAI-compatible /v1 request shaping + SSE streaming, summary-
  prompt assembly. src/config.ts: endpoints, pickBearer, server env (fail-fast).
- 70 vitest tests (windowing, VTT, request shaping over mock fetch, webhook
  signature verify + routing, OAuth shaping, config). tsc --noEmit clean; build
  produces dist/zoom, dist/meet, dist/server.js.
- Registers packages/meetings in pnpm-workspace.yaml (one line).
2026-07-03 19:15:50 -07:00
Hanzo Dev bded43dbce feat(slack): Hanzo AI Slack app (@hanzo/slack)
Bolt app bringing Hanzo AI into Slack, built on the shared foundation:
@hanzo/ai (headless client), @hanzo/iam (identity), @hanzo/cards (UI).

- /hanzo slash command → completion, replies with a @hanzo/cards panel
- App Home assistant panel (model picker, quick actions, prompt, sign-in)
  via toSlackBlocks — no hand-written Block Kit
- Interactivity for the stable card action ids (model select, quick
  actions, prompt submit, sign in)
- 'Ask Hanzo' message shortcut + app_mention: pull thread/message context
  via conversations.replies/history, fence it into the prompt
- OAuth install (/slack/install, /slack/oauth_redirect) with a per-workspace
  bot token + per-workspace Hanzo token, server-side only
- Quick actions: Summarize, Draft reply, Explain, Action items
- Endpoints via api.hanzo.ai /v1 only; secrets from env; /health + graceful
  shutdown for ingress/k8s
- One path from Slack to the model (complete); pure modules unit-tested
- README with Slack app manifest + registration steps

Tests: 68 passing. tsc --noEmit clean. Build emits dist/.
2026-07-03 19:15:01 -07:00
Hanzo Dev 733c87313d ci(cards): test @hanzo/cards; workspace includes packages/cards
The shared card-builder — one canonical PanelSpec → Slack Block Kit / Teams
Adaptive Cards / Google CardService with stable action ids. The missing
foundation piece for card-host adapters (Slack/Teams/Google); pairs with the
existing @hanzo/ai (headless client) + @hanzo/iam (identity) SDKs. Pure, zero
runtime deps, 66 tests.
2026-07-03 18:55:22 -07:00
Hanzo Dev bb1a88abd0 feat(cards): @hanzo/cards — canonical PanelSpec → Slack/Teams/CardService
One canonical PanelSpec (header, model picker, quick-action chips, output,
prompt input, footer, signed-out state) with stable action ids, emitted to
three host card formats:
- toSlackBlocks  → Slack Block Kit blocks[]
- toAdaptiveCard → Teams Adaptive Card v1.5
- toCardServiceJson → Google Workspace CardService JSON

Pure presentation layer: no network, no host SDK deps, no secrets. Emitters
never call the AI or auth (@hanzo/ai / @hanzo/iam do that). assistantPanel()
builder so adapters do not hand-assemble. 66 vitest tests, tsc clean, tsup
build (cjs+esm+dts). Registered in pnpm-workspace.yaml.
2026-07-03 18:54:13 -07:00
Hanzo Dev a1a355b1d1 ci(clio): test @hanzo/clio; workspace includes packages/clio
Clio practice-management integration — OAuth2 + Clio API v4, AI actions over
matters/documents with write-back to matter notes/activities, and Custom Actions
to embed AI buttons inside Clio. A hosted web app + token-exchange service
(clio.hanzo.ai; client_secret server-only, KMS), so CI-tested — no sideload
binary. Completes the daily-lawyer surface set (iManage/NetDocuments deferred).
2026-07-03 17:22:43 -07:00
Hanzo Dev 91a595b822 Merge remote-tracking branch 'origin/feat/clio-integration' 2026-07-03 17:22:10 -07:00
Hanzo Dev 110b91f54c fix(clio): correct OAuth refresh + Custom Action nonce to real Clio v4 contract
Post-review corrections verified against Clio's own developer docs and the
official clio/example-third-party-application, fixing three ways the first
cut would have failed against real Clio:

1. OAuth: removed access_type=offline (a Google-ism Clio ignores — it would
   have yielded NO refresh_token). Clio returns a refresh_token on the
   authorization_code grant by default; added the real redirect_on_decline
   option instead. Wired the refresh path into the SPA (ensureClioToken →
   /oauth/refresh) so a long matter session survives token expiry — the
   refresh apparatus is no longer dead code.

2. Custom Action nonce: the nonce is NOT a locally-verifiable HMAC. It is a
   single-use, 60s code that the app must REPLAY to the Clio API, which
   validates server-side (403 on bad/stale). Removed the wrong local HMAC
   verify (and node:crypto from the service); /custom-action now parses the
   deep-link and redirects into the SPA carrying matter_id + the nonce, and
   getMatter replays it (custom_action_nonce query param) on the first
   authenticated call. Corrected the deep-link params to Clio's actual set
   (custom_action_id/user_id/subject_url/custom_action_nonce; subject_url is
   URL-encoded) — subject_id was never sent.

3. Documents: gate downloads on content_type (isTextExtractable) — PDF/DOCX
   (the bulk of legal docs) would have been fed to the model as mojibake,
   wasting the context budget. Binary docs now contribute name + type only.
   The MIME check matches the subtype so DOCX (whose vendor type contains the
   substring "xml") is correctly binary.

tsc clean, 89 vitest tests pass, esbuild build succeeds; re-verified the
browser bundle has zero secret/crypto refs.
2026-07-03 17:21:01 -07:00
Hanzo Dev 85dc2db900 feat(clio): Hanzo AI integration for Clio practice management
New @hanzo/clio package: brings Hanzo AI into a lawyer's Clio matters.
Pick a matter and Hanzo can summarize it, draft correspondence, extract
key dates & obligations, or answer freeform questions over the matter's
documents — then write the result back to Clio as a Note or Activity.

Architecture (one and only one way, secret-safe):
- Clio OAuth2 authorization-code grant. client_secret lives ONLY in the
  token-exchange service (server.ts, read from env); the browser SPA never
  sees it. Verified: dist/app.js has zero secret / crypto / process.env refs.
- Clio REST API v4 client (clio-api.ts): pure request-shaping wrappers for
  listMatters/getMatter/listDocuments/downloadDocument/listContacts and the
  write-back createNote/createActivity — URL, headers, fields selection,
  pagination all unit-testable; one thin clioFetch/clioDownload at the edge.
- Hanzo chat (hanzo-chat.ts) mirrors @hanzo/office-addin's wire contract:
  api.hanzo.ai/v1/chat/completions + /v1/models, no /api/ prefix.
- Four actions + matter-context assembly/truncation (actions.ts, 48k total /
  12k per-doc caps, truncation marked).
- Custom Action landing (custom-action.ts + server.ts): parses Clio's signed
  deep-link, HMAC-verifies the nonce (constant-time) with the client_secret,
  redirects into the SPA on the right matter.
- Configurable regional Clio base (us/eu/au/ca).

esbuild build (SPA iife + Node ESM service), tsc --noEmit clean, 86 vitest
tests pass across config/oauth/api/chat/actions/custom-action/server.

Registered packages/clio in pnpm-workspace.yaml.
2026-07-03 17:13:30 -07:00
Hanzo Dev c213289abf ci(pdf): test @hanzo/pdf; workspace includes packages/pdf
The Hanzo AI PDF workspace (PDF.js viewer + AI side panel — summarize, extract
parties/dates/obligations, find risky clauses over filings/contracts). A hosted
static surface (pdf.hanzo.ai over hanzoai/static), so CI-tested like the Google
Workspace add-on — no sideload binary.
2026-07-03 17:02:27 -07:00
Hanzo Dev 72464665c5 Merge remote-tracking branch 'origin/feat/pdf-ai' 2026-07-03 17:01:53 -07:00
Hanzo Dev 7f57e4d2b8 feat(pdf): Hanzo AI PDF workspace (@hanzo/pdf)
Self-contained web PDF workspace — PDF.js viewer + Hanzo AI side panel —
for the surface lawyers spend the most time in (filings, discovery,
executed contracts). Cross-platform (any browser), no Adobe license.

- PDF.js (pdfjs-dist 4.10.38) renders pages to canvas and extracts per-page
  text; worker bundled and shipped beside app.js.
- Right panel mirrors @hanzo/office-addin: model picker, quick-action chips,
  streamed replies over api.hanzo.ai/v1/chat/completions (SSE), /v1/models.
- Law-office quick actions: summarize; extract parties/dates/obligations;
  find & explain risky clauses; explain the selection; draft a summary memo.
- Honest large-PDF context windowing (buildContext): caps attached text at a
  char budget, windows whole-document or from the current page, and prepends a
  context note naming the exact page range sent — never claims the full doc
  when truncated. Pure and unit-tested.
- Auth: pasted hk- key validated against /v1/models before save (localStorage);
  OAuth via hanzo.id documented as the additive future path.
- esbuild build.js, vitest (44 tests), tsc clean.

Endpoints: api.hanzo.ai /v1 only.
2026-07-03 17:00:51 -07:00
Hanzo Dev 2655a5f227 ci(release): make Safari packaging reliable — zip the whole safari output tree
The Safari step zipped a fixed 'safari/Hanzo AI' subpath and hit zip exit 12
('nothing to do') → continue-on-error swallowed it → no release asset (v1.9.30
shipped without Safari). Zip the whole dist/safari tree (the Xcode project is
always emitted; a dev builds+signs+sideloads it) + any built .app, guarded so a
zip exit code can't blank the asset. Future releases carry Safari reliably.
2026-07-03 16:56:36 -07:00
Hanzo Dev 1c7679c41e chore(release): sync all packages to 1.9.30 (Outlook downloadable)
Adds the Outlook mail add-in as a downloadable release binary (hanzo-ai-outlook)
alongside Office, browser, IDE (vscode/cursor/windsurf/antigravity/jetbrains),
Claude, and Safari. Google Workspace ships via clasp, not a sideload binary.
v1.9.30
2026-07-03 16:49:08 -07:00
Hanzo Dev 3105d4a099 ci(office-suite): test + release Outlook & Google Workspace add-ins
Wire the two new productivity surfaces into CI/CD:
- ci.yml: test @hanzo/outlook-addin (39) + @hanzo/gworkspace-addon (29).
- publish.yml: new 'outlook' job builds @hanzo/outlook-addin → hanzo-ai-outlook-v<ver>.zip,
  added to release needs/files + an Outlook row in the download table.
  (Google Workspace deploys via clasp to Google, not a sideload binary — CI-tested
  only, no release zip.)
- pnpm-lock: workspace now includes packages/outlook + packages/gworkspace.
2026-07-03 16:32:55 -07:00
Hanzo Dev 5f14961fbb Merge remote-tracking branch 'origin/feat/gworkspace-addon' 2026-07-03 16:29:58 -07:00
Hanzo Dev 36de7695af feat(gworkspace): Google Workspace add-on for Docs, Sheets, Slides, Gmail
Mirror @hanzo/office-addin for the Google ecosystem. Apps Script +
CardService add-on calling api.hanzo.ai/v1 via UrlFetchApp (non-streaming,
one call per insert). Per-user hk- API key in PropertiesService.

- appsscript.json: per-host homepage triggers + Gmail contextual/compose
  triggers, currentonly + gmail.addons scopes, urlFetchWhitelist pinned to
  https://api.hanzo.ai/.
- hanzo.gs: API client (pure buildMessages/requestBody/extractContent/
  parseModels + UrlFetchApp ask/listModels).
- ui.gs: CardService sidebar (model picker, quick-action chips, settings/
  onboarding, universal actions) + all trigger/action handlers.
- docs/sheets/slides/gmail.gs: per-host read + insert glue.
- vitest over the pure logic (loads .gs in a Node sandbox, no duplication);
  validate-manifest.mjs preflights scopes + every runFunction resolves.
- Register packages/gworkspace in pnpm-workspace.yaml.
2026-07-03 16:25:19 -07:00
Hanzo Dev 0ae9186728 feat(outlook): Hanzo AI mail add-in (@hanzo/outlook-addin)
A Microsoft Outlook mail add-in embedding Hanzo AI into email — the
highest-value law-office surface after Word. Mail add-in manifest
(MailApp), Read + Compose command surfaces, ribbon button opens the
task pane.

Quick actions: Summarize thread, Draft reply, Extract deadlines &
action items, Explain plainly — all streamed (askStream/SSE).

- Reuses the Office add-in's pure core identically: config.ts (api.hanzo.ai
  /v1 gateway), chat.ts (ask/askStream/listModels), auth.ts (@hanzo/auth
  OAuth2+PKCE via roamingSettings + hk- API-key path). Shares the
  hanzo-office IAM client — no new auth surface.
- New outlook-host.ts: Office.context.mailbox.item glue (read body/subject/
  from; compose prepend/setSelectedData; displayReplyForm) + pure shaping
  helpers (stripHtml, collapseWhitespace, shapeMessage, formatFrom, mailMode).
- build.js mirrors office (esbuild -> dist/, base-URL stamp, dev HTTPS serve).
- 39 vitest tests pass; manifest validated by office-addin-manifest.
- Registered packages/outlook in pnpm-workspace.yaml.
2026-07-03 16:24:24 -07:00
Hanzo Dev e9531bf4e3 fix(jetbrains): disable buildSearchableOptions — headless libfreetype crash
buildPlugin transitively runs buildSearchableOptions, which launches a headless
IDE (JCEF) needing native font libs (libfreetype.so.6) absent on the CI runners
→ UnsatisfiedLinkError, non-deterministic (only green when the task was cache
UP-TO-DATE, which is why ci.yml passed but the release build failed). The search
index is optional and rebuilt by the IDE at runtime; disabling it makes
buildPlugin reliably emit the distributable plugin zip for the GitHub Release.
v1.9.29
2026-07-03 15:47:49 -07:00
Hanzo Dev 46daa52d43 chore(release): sync all packages to 1.9.29 for full all-platform release
Bumps root + browser + vscode + office + jetbrains to 1.9.29 so every release
asset name matches the tag (the download-table URLs use the tag version). The
first release to carry a downloadable JetBrains plugin binary alongside browser,
VS Code/Cursor/Windsurf/Antigravity, Claude, Safari and Office.
2026-07-03 15:39:46 -07:00
hanzo-dev 780757ca55 feat(auth): one canonical @hanzo/auth core; migrate office; fix vscode HIP-0111
Five surfaces (browser, office, vscode, cli, mcp) each hand-rolled the SAME
IAM OAuth2+PKCE flow — and one drifted and broke: vscode built
hanzo.id/oauth/authorize and iam.hanzo.ai/oauth/token, both MISSING the
/v1/iam prefix (a 404 — the same class of bug as the browser login fix),
plus it POSTed JSON where IAM requires form-urlencoded and sent a
client_secret on a public PKCE client. That is the opposite of one way.

Decomplected into ONE core — packages/auth (@hanzo/auth):
- config.ts   canonical HIP-0111 endpoints (authorize/token/userinfo ALL
              carry /v1/iam) + the one-client-per-surface registry
              (hanzo-browser/office/vscode/cli). URL assembly lives once.
- pkce.ts     the one PKCE (S256), platform-CSPRNG + WebCrypto.
- oauth.ts    buildAuthorizeURL / exchangeCode / refreshTokens / userinfo —
              pure fetch, form-encoded per RFC 6749.
- flow.ts     login / getValidToken (transparent refresh) / logout /
              currentUser, written ONCE against TokenStore + Opener. A
              surface supplies only those two thin adapters — no surface
              re-implements the flow.
23 tests: the drift guard (every path has /v1/iam), client registry, PKCE
RFC-7636 vector, wire shapes, and the full flow with fake adapters.

Migrate office onto it: roamingSettings TokenStore + Dialog-API Opener;
delete office's duplicate pkce.ts + IAM config (now the core's). API-key
path kept. 22 tests; the core bundles into the task pane.

Fix vscode standalone-auth: all three endpoints → ${host}/v1/iam/oauth/*,
token calls form-urlencoded, OIDC scopes, drop the secret on the public
PKCE client. Compiles clean.

CI now gates @hanzo/auth. Browser/mcp/tools already use canonical paths;
migrating them to import the core is the tracked next step (each needs its
own bundler wiring) — the ONE way now exists and has two consumers.
2026-07-03 15:24:01 -07:00
Hanzo Dev 5bf8d934b4 ci(release): downloadable JetBrains + Office + Safari binaries for all platforms
The GitHub Release table advertised JetBrains and Safari downloads that 404'd —
neither job produced a release artifact — and the Office add-in was absent.
Make every advertised binary real and deterministic from the tag:

- jetbrains: always `gradlew buildPlugin` → hanzo-ai-jetbrains-v<ver>.zip artifact
  (Marketplace publish stays token-gated); the job no longer skips without the
  token, so the download link always resolves.
- office: new job builds @hanzo/office-addin → hanzo-ai-office-v<ver>.zip.
- safari: package the built .app(s) → hanzo-ai-safari-v<ver>.zip.
- release: add jetbrains+office to `needs`, add all three globs to `files`, and
  add the Office row to the download table. Asset names use the tag version so
  they match the table URLs.

All-platform release: browser (chrome/edge/firefox/safari), IDE (vscode/cursor/
windsurf/antigravity/jetbrains), Claude dxt, and Office (Word/Excel/PowerPoint).
2026-07-03 15:22:58 -07:00
Hanzo Dev 81db870a51 feat(office): streaming responses, live model picker, quick-action chips
Additive enhancements to the Office add-in, keeping the existing ask/requestBody
contract (and its tests) intact:

- chat.ts: askStream (SSE streaming completion → onDelta), streamDelta (pure SSE
  frame parse), listModels (/v1/models → ids); requestBody gains an opt-in stream
  flag (defaults false, so the one-shot ask path is unchanged).
- taskpane: a live model <select> populated from /v1/models (org-scoped by the
  token, refreshed on sign-in), and one-click quick-action chips — Draft,
  Summarize, Explain plainly, Tighten/redline, Continue — that run over the
  selection. Output now streams in live; Insert stays gated on real content.

Built for document-heavy review/drafting (e.g. a law office). Tests: 31 pass
(+6 new: stream flag, streamDelta, listModels). Build OK; my files tsc-clean.
2026-07-03 15:08:32 -07:00
hanzo-dev 4060ce491b feat(office): API-key auth + self-contained Windows test kit; release 1.9.28
- API-key auth: paste a Hanzo hk- key (console.hanzo.ai → API keys) instead
  of the IAM OAuth dialog, so the add-in is usable with zero infra/IAM setup.
  pickBearer(apiKey, oauthToken) precedence is pure + tested (29 tests).
- Windows test kit: `HANZO_OFFICE_BASE=https://localhost:3000` build ships
  serve.mjs (stdlib HTTPS static server) + SIDELOAD-WINDOWS.md, so the
  downloaded zip runs on any machine — trust a localhost cert, node serve.mjs,
  sideload manifest.xml, paste key, go.
- CI Build now emits BOTH hanzo-ai-office-v${VER}.zip (production, office.hanzo.ai)
  and hanzo-ai-office-localhost-v${VER}.zip (the test kit).

Bump browser+root to 1.9.28 so tagging v1.9.28 publishes a GitHub Release
carrying ALL binaries — chrome/edge/firefox/safari, vscode/cursor/windsurf,
claude(dxt), and now office (prod + localhost) — with permanent download links.
v1.9.28
2026-07-03 15:05:46 -07:00
hanzo-dev 9032801deb feat(office): Hanzo AI add-in for Word, Excel & PowerPoint
The surface a non-developer actually uses — a Microsoft Office task-pane
add-in that puts Hanzo AI inside the document. Select text or a range,
ask, insert. Distinct from the browser extension and the IDE extensions;
same backend as everything else: model calls through api.hanzo.ai/v1
(OpenAI-compatible), sign-in via Hanzo IAM (hanzo.id, auth-code + PKCE,
HIP-0111). No new API surface.

Decomplected so the logic is pure and unit-tested (26 tests), Office.js
glue kept thin:
- chat.ts — request/response shaping (buildMessages fences the selection
  as data; extractContent tolerates the gateway's response shapes)
- pkce.ts — PKCE + authorize URL, verified against the RFC 7636 S256 test
  vector
- host.ts — per-host read/insert; Excel range↔text helpers tested
- config.ts — canonical endpoints pinned (api.hanzo.ai, /v1/iam/oauth/*)
- taskpane/auth/commands — the Office.js + Dialog-API glue

manifest.xml passes `office-addin-manifest validate` ("The manifest is
valid" — Word/Excel/PowerPoint on web, Windows, Mac). build.js stamps the
hosting base URL (office.hanzo.ai, override for localhost dev) into a
sideloadable/AppSource-submittable dist/manifest.xml.

CI runs the tests and builds+zips a sideloadable artifact. Publishing to
AppSource needs the dist hosted at office.hanzo.ai + a hanzo-office IAM
client + a Partner Center submission (documented in the README).
2026-07-03 14:43:29 -07:00