# 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:

```js
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.

## 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.

```js
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:

```js
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](./delegated-authority.md)

## 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](./on-prem.md)

## 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` |
