In-app AI console
Open the ✦ AI tool rail, enter a provider and API key in settings, and you can operate oniyanma in natural language.
Color by elevation and look straight down from above
Show only the slab within 3 meters from the top
How many points are in the current selection?
Instructions are converted into command calls via tool-use, and actually executed. Enter sends, Shift+Enter inserts a newline, and "Stop" cancels.
Providers and models
| Provider | Selectable models | Default |
|---|---|---|
| Anthropic (Claude) | claude-opus-4-8 / claude-sonnet-5 / claude-haiku-4-5 | claude-opus-4-8 |
| OpenAI (GPT) | gpt-4o / gpt-4o-mini / gpt-4.1 / o4-mini | gpt-4o |
What differs between providers is only the client, the tool format, and loop normalization. The command layer, the UI, and state injection are shared, so what actually executes is the same either way.
Where the API key lives
oniyanma is a client-only app with no backend, so the key is stored only in this device's browser (localStorage) and sent straight from the browser to the chosen provider's API. It goes nowhere else. Don't use this on a shared device.
What the AI sees
Every message adds the following to the system prompt.
- Tool definitions (all 109 commands from
toolDefs(), with JSON Schema) - Notes on the coordinate system —
selectBoxuses the aligned frame,setSection'spos/thickare 0–1,lookAlong'saxisis "the direction the camera is placed", checkqueryCrsfor the height datum, and so on - A snapshot of the current state (the JSON from
getState()) — loaded sources,alignedBounds, selection count, edit count, camera position, CRS
Because alignedBounds is included, a relative instruction like "3 m from the top" can be grounded to actual coordinates. When no data is loaded, it's instructed to say so and prompt a load.
As for rules, it's told to only execute a destructive operation when the user has clearly instructed it, to go through the confirmation gate's two steps (see the count, then confirm: true), and to check back rather than retry when the count is wildly different from expected, is zero, or the instruction is ambiguous.
Three more for findings: filing produces a draft, never published on its own without an instruction to do so; exporting defaults to published only (drafts are included only when there's a request for internal review); and a type definition is the client's own inspection form, so it's only touched when a revision is clearly instructed. "Never publish on its own" isn't left to wording alone — it's also scored by the eval set.
The system prompt's wording lives in exactly one place, buildSystemFromState(). The eval set calls that same function too, so what the eval measures is always the real prompt.
Continuing a conversation
Everything up to the current point is kept as history, so referring expressions work.
Hide the range I just selected
At this point, the history holds not just the text but the tool round-trips (tool_use / tool_result) as-is too. If it only held the text, the agent could only reconstruct its own past actions from its own summary sentences, having to re-guess the previous selectBox's min/max from prose. Whether referring expressions hold together depends on this.
"Clear log" resets the history.
Sending as a proposal
Turning on "Send as a proposal (you decide whether to accept)" makes that turn's edits open a batch with status:'proposed' — unlike the confirmation gate's confirm:true, it actually executes, but stays in a "not yet accepted by a human" state separate from Undo, usable as an intermediate checkpoint when giving the AI a longer instruction.
await oniyanma.execute('acceptBatch', { id }) // accept (doesn't touch the ops — just flips the batch's status to applied)
await oniyanma.execute('revertBatch', { id }) // reject (reverts a proposed batch; there's no dedicated rejectBatch)acceptBatch can only be called by a human actor — if the AI could accept its own proposal, the whole point of proposing would be lost.
Some commands can't be called
A few commands (acceptBatch / confirmFinding / rejectFinding) can't be executed from the AI / MCP even with the confirmation gate's confirm:true. If a machine could approve its own proposal or detection, approval as a mechanism would lose its meaning. A human (UI / ⌘K / shortcuts) can call these normally.
Provenance of edits
Edits issued by the AI are recorded in the Command log as ai, along with the model name and the first 500 characters of the instruction. The edit panel shows something like "Edits: 12 (3 by AI/API)".
These don't mix with direct operations from the UI (human). → Non-destructive editing
Errors
API errors are shown with a translated message.
| Message | Cause |
|---|---|
| Invalid API key | Authentication error |
| This API key doesn't have access | Insufficient permission |
| Rate limit reached | Rate limiting |
| Failed to connect (network/CORS) | Connection error |