# kaviri for agents You are an AI agent and something has asked you for a video of a web app. This file is the whole of what you need. It assumes you can run a command and write a file, and nothing else. kaviri is a recording browser. You do not describe a camera move, you describe what happens, and the camera is derived from that. There is no screen recorder involved, no window to keep in focus, no display server, no Wayland portal and no permission dialog. It works the same on your laptop and on a CI runner with no screen at all. ## The one paragraph version Write a `.jsonl` file, one JSON object per line, each one an op. Run `kaviri record --script take.jsonl --out take.mp4`. You get an MP4. Every op answers on stdout with `{"ok":true,"result":{…}}` or `{"ok":false,"error":"…"}`, and the process exits non-zero if any op failed, so you can tell success from failure without watching the video. ## The ops ```jsonl {"op":"navigate","url":"http://127.0.0.1:8099/"} {"op":"wait","selector":"#app","timeout_ms":30000} {"op":"start_recording"} {"op":"wait","ms":800} {"op":"click","selector":"#new-invoice"} {"op":"type","selector":"#amount","text":"1450.00"} {"op":"scroll","y":600,"smooth":true} {"op":"mark","label":"submitted"} {"op":"wait","ms":1600} {"op":"stop_recording"} ``` `click` also takes `x` and `y` instead of a selector. `type` takes `typewriter_ms` to set the per character delay; the default of 18 is deliberately fast, because a demo of someone typing slowly is a demo of someone typing slowly. `navigate` turns a bare path into a `file://` URL. `mark` puts a label in the telemetry and nothing on screen. The full reference, including every field and what each one does to the camera, is `docs/script-protocol.md` in the repository. How the camera decides what to do is `docs/camera.md` in the repository. ## The five things agents get wrong **1. Recording before the page is ready.** `start_recording` after the `wait` that proves the app is up, not before. Otherwise the first two seconds of your video are a blank page, and the zoom on the first interaction fires while the layout is still moving. **2. No trailing wait.** An interaction needs about two seconds after it to be zoomed at all, and a script that ends `{"op":"click"},{"op":"stop_recording"}` throws away the zoom on the one thing it was demonstrating. kaviri says so on stderr rather than silently: `the interaction at 28.4s is too close to the end of the 29.0s take`. End with a `wait` of at least 1600ms. **3. Assuming a selector resolved.** If a modal, a cookie banner or a loading overlay is over your target, kaviri refuses rather than clicking through it: `selector #q is covered by