| .. | ||
| extension.json | ||
| main.go | ||
| README.md | ||
approve — example zot extension
Demonstrates the spontaneous open_panel pattern: an
extension-registered tool opens a panel from inside its handler
goroutine, blocks until the user responds, then returns the result to
the model.
What it does
Registers one LLM-callable tool:
approve_action(action, reason?)
When the model calls it, a panel appears in the TUI:
╭─ Approval required ─────────────────────╮
│ Action: delete /tmp/build │
│ Reason: cleaning up after the build │
│ │
│ y approve n / esc deny │
╰──────────────────────────────────────────╯
y approve n deny esc cancel
The model's tool call is held open until the user presses a key. The
model receives "approved" or "denied: user rejected the action" as
the tool result and replies accordingly.
Build
cd examples/extensions/approve
go build -o approve .
Install
zot ext install .
Try it
In zot, ask:
Request approval to delete the temp directory.
The model calls approve_action; the panel opens. Press y to
approve, n or esc to deny.
Ask me to approve before running any destructive command.
The model will start calling approve_action before suggesting risky
steps; each call pauses until you respond.
Key points
- The tool handler calls
e.OpenPanel(...)directly — no slash command needed. - A
chan boolbridges the panel key handler back to the blocked tool goroutine; nothing in the wire protocol changes. - Panel IDs are unique per call so multiple concurrent approvals don't collide (rare in practice, but handled correctly).
- The
onClosecallback onOnPanelKeyhandles the case where the user dismisses the panel from the TUI border rather than pressing a key, treating it as a denial.
See also
examples/extensions/secret— same pattern with masked text input (credential collection)examples/extensions/guard— blocks dangerous tool calls via interception (no panel)docs/extensions.md— full protocol reference