Tools and widgets
In Witbitz, tools are how an agent reaches capability, and widgets are how tool results become shared application state. The important distinction is that a widget is not one user's private chat decoration. It is part of the Space that every member can see.
Tools are declared by the Space
A Space's sealed config names the tools available to the agent:
await client.create({
agentName: 'Planner',
persona: 'Plan trips with the group and keep decisions clear.',
tools: ['search_places', 'show_places', 'show_chart', 'write_pdf']
})
The render opens this config during a turn and exposes the declared tools to the agent. If the config is sealed, the platform cannot read the persona, tool list, admission policy, or model config at rest.
The built-in catalogue
These are the tools a hosted Space can name in its config today. A Space only gets what it declares — an empty
tools list is an agent that can talk and nothing else.
| Tool | What it does | Produces |
|---|---|---|
search_places |
Query Google Places for candidates. | Text — the agent then picks |
show_places |
Put chosen places (by id) on the shared map. | places widget |
add_place |
Add one place to the existing map doc. | same places widget |
search_flights |
Query Duffel for real offers. | Text — the agent then picks |
show_flights |
Put chosen offers (by id) on the shared card. | flights widget |
set_itinerary |
Replace the whole day-by-day plan. | itinerary widget |
add_to_itinerary |
Append to the existing plan. | same itinerary widget |
show_chart |
Draw data as SVG. | chart widget |
show_diagram |
Draw a relationship graph as SVG. | diagram widget (same page) |
show_photo |
Put an image in the reply. | Sealed out-of-line blob |
read_file |
Read a shared PDF / Word / Excel / CSV. | Text |
read_url |
Fetch a public URL (SSRF-guarded). | Text |
write_pdf |
Generate a PDF from Markdown. | File on the reply |
Query-then-show is deliberate. search_* returns candidates to the agent; a second call puts the chosen ids on
the shared doc. The member sees a curated set, not a raw result dump, and the agent's choice is the part that gets
sealed into the room.
Two widget kinds are not agent-produced: location (a member shares live location from the composer) and nav
(turn-by-turn, opened from a place card). The client can also render trip and shop docs, which no current
first-party tool emits.
Any shared view can be opened full-screen from the room's ⋯ menu — a widget is a living document, so reaching it must not depend on scrolling back to the message that first posted it.
Tool results become room artifacts
Tools can return different kinds of results:
| Result | Example |
|---|---|
| Text | A searched page summary or calculation result. |
| Widget | A map, chart, diagram, itinerary, route, or flights card. |
| File | A generated PDF attached to the agent reply. |
| Image/blob | A sealed out-of-line photo or generated image. |
| Proposal | A high-stakes action waiting for human approval. |
The agent reply can carry pointers to widgets or files created during the same turn. The client renders those pointers as live shared UI.
Shared widget state
The state op stores a named shared widget document. A member or tool can set a patch and summary; later reads return
the latest state. The state is sealed with the room key.
await client.state(room, mk, 'trip-plan', {
days: [
{ name: 'Day 1', items: [{ text: 'Arrive and check in' }] }
]
}, 'One-day starter itinerary', 'Ada')
Because the document belongs to the Space, every member sees the same widget state.
High-stakes tools become proposals
An agent should not directly run actions that spend money, change external systems, or cross a policy boundary. Instead, the turn records a proposal. A human later approves or denies it:
const pending = await client.pending(room, mk)
await client.decide(room, mk, pending.proposals[0].id, true, 'Ada')
Only an approved proposal executes, and only once.
Read: Delegated authority
Air-gap behavior
When AIRGAP=1 is enabled in an on-prem deployment, internet-touching tools are refused server-side. Local tools such
as chat, document reading, PDF generation, charts, and diagrams continue to work. Map/photo/search/flight tools need
either local replacements or must stay disabled.
Read: Enterprise and on-prem
Code map
| Concern | File |
|---|---|
| Tool declaration and validation | agent/affordance.mjs, agent/agentRecord.mjs |
| Space tool execution | agent/spaceHandler.mjs, agent/spaceService.mjs |
| Shared widget docs | agent/sessionStore.mjs, spaces/public/spaceClient.js |
| Widget renderers | spaces/public/*Widget.* |
| Delegated actions | agent/delegatedAuthority.mjs |
See also — the inverse direction: a widget puts your app's UI inside a room; Embed a Space puts a whole room inside your app's page (a cross-origin iframe + an origin-checked bridge, content-blind either way).
Machine-readable source: tools-widgets.md · every doc in one fetch: llms-full.txt (HTML) · ← agent-turns · embed →