Repository navigation
Hush Assistant
Hush is the review assistant: a model with tools, running in a loop, with you in that loop. It reads and searches the document, runs the detectors, looks at what the review has decided, and previews rules. When it wants to change anything, it stops and asks. Shipped in 1.9.0 (#142).
Canonical sources: docs/editor.md §16 · docs/security-internals.md §8 · code: lib/assistant/, components/assistant/.
Ctrl/⌘+K, or the toolbar's Hush button. On a wide screen Hush takes the right rail in place of the inspector, beside the page rather than over it, and closing Hush brings the inspector back. On a narrow screen it opens as a sheet.
It runs as the AI SDK's ToolLoopAgent, streamed to useChat through POST /api/documents/:id/assistant, using the same provider and model that analysed the document. It needs a model verified for structured output, which is the same capability as tool calling for every provider supported here. With no provider, an unsupported model, or the spend cap reached, the panel says so.
Reads run without asking, once reading is allowed (§3). Changes always wait for approval.
| Read | Gives Hush |
|---|---|
get_document_overview |
name, kind, pages or sheets, redaction counts by status, source and category. No page text. |
read_page, read_sheet
|
a page's text (in 8,000-character parts), or rows of a sheet |
find_occurrences |
every place a value or pattern occurs, with page, context, a ref to act on, and whether a redaction covers it |
find_uncovered |
the deterministic detectors over the whole document, minus what is already covered, grouped by category and value |
list_suggestions, list_rules
|
the review so far, grouped as the inspector groups it, and every rule in scope |
preview_rule, compare_patterns
|
a rule's matches before it exists, and what a new pattern gains and loses against an old one |
| Change (approval required) | Effect |
|---|---|
create_rule |
a literal or RegEx rule at document, batch or global scope |
update_rule |
switch a rule off or on, or change its pattern or category, everywhere it reaches |
redact_occurrences |
specific values, by the refs a read returned |
set_suggestion_status |
accept or reject whole suggestion groups |
"Covered" means a redaction there is accepted or still suggested. A rejected redaction means you chose to keep the value, so it counts as uncovered ("kept in file"). uncovered is counted over every occurrence, not only the listed ones.
The first read in a document is itself an approval: "Let Hush read this document?" Hush sends what it reads to the configured provider. Once allowed, the permission holds for the session, and every later read is listed in the conversation as it happens. The shield icon in the header revokes it.
Until you allow it, Hush is not told the file's name, and it knows a selected redaction only by its category, status and page: not its value, and not its reason (which quotes the value). The file and sheet names count as reads too, so get_document_overview waits for consent.
A change arrives as a card that shows what it will do and the evidence for it:
- a rule shows its live preview: the count here, across the batch, and the first matches;
- a redaction shows the exact values and where they are;
- a bulk decision shows the groups, resolved from the store.
Nothing on a card has happened until you press Approve. A declined change is reported back to the agent, which is told not to retry it. After an approved change, the canvas, inspector and rules panel reload from the server.
-
Signed. The conversation is held by the client, so without a signature a client could send an approval the server never issued. Approvals are HMAC-signed when issued (key derived from
FINGERPRINT_SECRET) and bind the tool, the call id and the input. An approval whose tool, call or input was altered is refused before anything runs. - Re-validated. A rule must still compile and fit its budget. A reference must still hold the exact text you approved, so a model that copied the wrong text for a place redacts nothing there.
- Each tool call is drawn as it runs. A read is a line of activity ("Found
EMP-\d{5}· 12 times on 4 pages, 3 not covered") that opens into its result. Every location is a button that moves the canvas there, and "Show all in the document" runs the pattern as a search. - Replies are Markdown, rendered as they stream (Streamdown) and hardened: links render as text, images as alt text, URLs are dropped, and raw HTML is not rendered. A reply can quote the document, so no tracking pixel can phone home through a model that repeated it. This is the same rule the email viewer keeps.
- The model is told that tool results are document content and never instructions. The approval step is what keeps this safe when a hostile paragraph gets it ignored anyway: the worst such a paragraph can do is get a bad change proposed.
- Header badges show where answers come from: provider, model, and kind (hosted API, local model, ChatGPT plan or gateway), each explained on hover. A local model also gets "Stays on this machine". The icons describe the kind of provider, never a vendor's logo.
- Errors inside the stream are a fixed sentence, never the provider's message (which can quote the prompt). Only the failure's name is logged.
After three hand-made redactions of the same anchored shape, Hush offers a rule for it:
You've redacted 4 values like
EMP-00xxx. Make a batch rule forEMP-00\d{3}?
- The shape is learned in the browser, with no model and nothing sent, so this works with no provider configured (
hooks/use-learned-shapes.ts,lib/assistant/learn.ts). - It is offered only when the shape has digits or a literal that every example shares. Two capitalised words is a shape too, and a rule for it would redact prose.
- A toast announces a new offer once, and the toolbar's Hush button carries a dot while an offer is waiting. Accepting opens the rule dialog with its preview.
On a RegEx rule in the Rules panel, Improve with Hush opens the conversation with that request. Hush looks at what the rule matched and what you rejected, tests a tighter pattern with compare_patterns, and proposes it as an update_rule card for you to approve or decline.
| Limit | Value |
|---|---|
| Steps per run | 12 |
| Messages per conversation | 80 |
| Request body |
1.5 MB, counted as it arrives (a chunked body without content-length is cut off, not buffered) |
| Daily spend cap | the instance's ANONIFY_AI_DAILY_SPEND_USD, shared with analysis |
| Per-visitor allowance |
ANONIFY_QUOTA_ASSISTANT_TOKENS: 300,000 tokens/day on the demo, unlimited self-hosted |
Every model step is an AiUsage row (task: "assistant") against the document, and its tokens are charged to your daily assistantTokens allowance. The cap and the allowance are both checked before the run and again before every step, so a run stops mid-way when either runs out, and the panel says which. The per-visitor allowance exists because the spend cap is one number for the whole instance: without it, one visitor could spend the cap and stop analysis for everyone.
What keeps working with no provider or at the cap: learned offers, search, shortcuts and hand-written rules, because none of them needs a model.
-
GET /api/assistant:{ available: true, model, provider: { id, label, kind, model } }or{ available: false, reason: "not-configured" | "unsupported" | "budget" }. -
POST /api/documents/:id/assistant: one streamed turn. Body{ messages, readConsent, view: { currentPage, selected } }. Errors:409not ready,413conversation too long,429 code: "allowance",503 code: "not-configured" | "unsupported" | "budget". Approval requests carryapproval.requestReasonofread-consentorchange.
Anonify: AI proposes, the application applies, and only what a person accepted is removed.
Repository · Report a bug · Request a feature · Security policy · Apache-2.0
This wiki describes v1.10.0 and summarises and links the in-repo documentation. Where the two disagree, the files under docs/, CHANGELOG.md and SECURITY.md are canonical.
Getting going
Using it
- Supported Formats
- Review Workspace
- Search and Rules
- Hush Assistant
- Redaction Presets
- Export and Anonymisation
- Batches and Mailboxes
How it works
- Architecture
- Processing Workflow
- Redaction Engine
- Storage and Encryption
- Streaming IO
- Data Model
- API Reference
- Security Model
When it breaks
Contributing
Repository · Issues · Releases · Changelog