OPEN SOURCE · MIT · LOCAL-FIRST

You say it.
Witness proves it.

Experience your product and talk naturally — “this button does nothing”. Witness welds every word to the causal evidence of that exact second, then hands the package to your AI coding agent.

$ npm i -g github:wookat/witness
The problem

Agents can't see what you see.

A human notices “the button fades weirdly when I scroll” in two seconds. Writing that up for an AI agent takes ten minutes of screenshots, element selectors and prompt crafting — and the result still loses the why. Voice is the cheapest annotation channel there is; it just never had a recorder.

How it works

Three recording lines, one timeline.

wit start <dev-url> opens a managed browser. From that moment, three streams run in parallel — zero manual work:

01

Your voice

Continuous audio + live speech-to-text. Say everything on your mind — one take, many issues, no need to split.

02

Operations + causal bursts

Every click captures its ~1.8s aftermath: requests, responses, console errors, DOM changes. A click with no aftermath is itself evidence.

03

Page evolution

rrweb full-DOM replay + event-driven screenshots + periodic DOM snapshots. Rewind the exact page state at any second.

wit start <url>→ browse + talk→ Ctrl+click / M to mark→ ■ stop→ evidence package
The output

Your words become evidence anchors.

Each spoken fragment becomes a block on a timeline with the causal facts of that moment attached — the “causal burst”. This is what your agent actually reads:

 session.md
### 00:09 · SAY
> [00:15] "the button fades and shrinks when I scroll"
- `00:08`  SCROLL y=3936
- `00:08`  MUT  `button.feedback` data-collapsed: false → true
- `00:11`  MUT  `button.feedback` data-collapsed: true → false   ← flapping on the boundary

### 00:34 · SAY
> [00:34] "the submit button does nothing"
- `00:33`  CLICK `button#submit` “提交” · @482,701
  └─ (page did nothing for 1.8s after the click — that itself is evidence)

### 01:02 · SAY
> [01:02] "the orders API returns wrong data"
- `01:01`  CLICK `a[href="/orders"]` “订单”
  - `01:01`  NET → GET /api/orders?page=1
  - `01:01`  NET ← **200** /api/orders · [body](bodies/req_0041.json)   ← items: []
Two audiences, one package

Humans get a replay. Agents get a structure.

For humans

  • review.html speech cards + audio seek + screenshots
  • ▶ DOM replay embedded rrweb player, real page states
  • film strip scrub the session like a video
  • audio.webm the original take

For AI agents

  • PROMPT.md handoff instructions + page vocabulary
  • session.md the evidence timeline above
  • events.jsonl complete raw stream, crash-safe
  • dom/ · bodies/ · shots/ snapshots, payloads, frames
Pull, don't push

wit mcp — evidence as an API.

Instead of a static dump, the session becomes a stdio MCP server. Point any MCP-capable agent at it and it queries exactly the evidence it needs:

list_says

Every spoken fragment with timestamps — the issue anchors.

events_around

Raw events in a time window, filtered by kind.

get_dom_state

Full DOM HTML nearest to a timestamp.

get_errors

Console errors, page errors, HTTP ≥ 400.

get_shot / get_body

Screenshot frames and response payloads on demand.

session_md

The rendered timeline when the agent wants it all.

Principles

Evidence never lies.

Local-first, zero config

System Edge/Chrome + Edge SpeechRecognition — free real-time transcription, no API key, works anywhere. Cloud ASR and LLM polish are optional layers, never the pipeline.

Words are never rewritten

Your speech lands verbatim in events.jsonl. Merging, correction and association are display-layer suggestions — the raw testimony stays auditable.

Evidence ≠ intent

Clicks and scrolls are facts, not complaints. Without a spoken anchor, agents are told explicitly: never infer a bug from behavior alone.

Privacy engineered in

Password fields masked in four channels; POST bodies redact credential fields; P freezes everything, mic included; WIT_NO_BODIES for sensitive apps.

Install & run

Two minutes to your first evidence package.

 terminal
# needs Node 18+ and system Edge/Chrome — no downloads, no keys
npm i -g github:wookat/witness
wit start http://localhost:5173     # managed browser opens — talk & click
# stop → .witness/sessions/<ts>/  — feed PROMPT.md + dir to your agent

wit list                            # your sessions
wit mcp                             # serve the latest as an MCP server