# Tool contract

> The current development contracts for tool manifests, intended effects, conditional rules, tool panels, and hook payloads in Raw (raw.tool-api/2, raw.panel/2, raw.hook/2).

This page describes the contracts that connect tools, policy, approval, hooks, and panels. The current versions are:

| Contract | Version          | Covers                                                                    |
| -------- | ---------------- | ------------------------------------------------------------------------- |
| Tool API | `raw.tool-api/2` | Tool manifests, argument validation, effects, and conditional predicates. |
| Panel    | `raw.panel/2`    | Live side panels and their blocks.                                        |
| Hook     | `raw.hook/2`     | Hook manifests, event payloads, and decisions.                            |

> **Caution:**
>
> Raw is in development. These contracts are replaced directly, without migration. Old tool manifests, hook payloads, and predicate formats are rejected, not adapted. Check the version a tool or hook declares before you copy it.

## Arguments and intended effects

A tool call has two separate kinds of data:

- **Arguments** are the original input from the model, after validation. They are never rewritten. Hooks, approval, and the handler all see the same arguments.
- **Intended effects** describe what a call would change, such as the files it would write or delete. They are computed before policy runs.

Intended effects are not results. A call that was approved and then failed has intended effects but no completed outcome. Effects are also not a security boundary, since the handler can still do anything its code permits.

## Dispatch order

For each tool call, Raw runs these steps in order. A failure at any step stops the call before the next step.

1. Check that the tool is exposed, and that no unconditional `deny` rule applies.
2. Validate the whole input against the JSON Schema and the tool’s `validateArgs`.
3. Prepare intended effects, if the tool declares them, and validate the result.
4. Evaluate conditional policy (`tools.rules`).
5. Run `PreToolUse` hooks.
6. Ask for approval when a rule requires it.
7. Run the handler.

Effect descriptors must be synchronous and bounded. They may parse and normalize paths, but they must not read files, call a provider, run a shell, or change anything. A descriptor that returns a promise, or that throws, stops the call before the handler runs.

## Manifest fields

A tool manifest with `api_version: 2` declares the following. Manifests reject unknown fields.

| Field               | Required | Description                                                                               |
| ------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `api_version`       | Yes      | `2`.                                                                                      |
| `id`                | Yes      | Folder ID.                                                                                |
| `version`           | Yes      | `major.minor.patch`.                                                                      |
| `name`              | Yes      | Model-facing name.                                                                        |
| `description`       | Yes      | What the tool does and what its result means.                                             |
| `input_schema`      | Yes      | JSON Schema for the arguments.                                                            |
| `entry`             | Yes      | Module that exports `handler` and optionally `validateArgs`, `describeEffects`.           |
| `condition_sources` | No       | Sources a predicate may inspect: `arguments` and `effects`.                               |
| `effects_schema`    | No       | Object schema for the effects the tool declares. Required when the tool supports effects. |
| `panels`            | No       | Up to four panel declarations. See [Panels](#panels).                                     |

## Predicates

Every conditional rule states its source explicitly. The `source` must be one of the tool’s declared `condition_sources`.

```json
{ "source": "effects", "any": "files[*].path", "regex": "protected\\.txt$" }
```

| Field    | Meaning                                                                                                 |
| -------- | ------------------------------------------------------------------------------------------------------- |
| `source` | `arguments` or `effects`.                                                                               |
| `any`    | A dotted path with `[*]` for each array step. It must resolve to a string field in the source’s schema. |
| `regex`  | An RE2 pattern, unanchored.                                                                             |

A path that does not exist in the schema is a validation error. A field that is absent from a valid value does not match. Policies and hook subscriptions use the same predicate evaluator.

## Write effects

The `write_file` tool declares effects for both its `operations` form and its `patch` form.

Each effect is an object with an absolute, lexically normalized `path` and an `operation`:

| Operation            | Produced by                                                                      |
| -------------------- | -------------------------------------------------------------------------------- |
| `write`              | `overwrite`, `append`, `replace_text`, `replace_lines`, and patch add or update. |
| `delete`             | Patch delete.                                                                    |
| `rename_source`      | Patch move, for the original path.                                               |
| `rename_destination` | Patch move, for the new path.                                                    |

Paths are lexical. A symbolic link is not resolved. Every operation and patch target appears in the effects, so a rule on `files[*].path` covers both forms.

```json
{ "source": "effects", "any": "files[*].path", "regex": "/protected/" }
```

## Approval

When a rule requires approval, the approval callback receives one request object with these fields:

| Field        | Description                                        |
| ------------ | -------------------------------------------------- |
| `identity`   | The tool’s canonical identity.                     |
| `name`       | The model-facing name.                             |
| `arguments`  | The original arguments.                            |
| `effects`    | The intended effects, when the tool declares them. |
| `toolCallId` | The call’s ID.                                     |
| `signal`     | An abort signal.                                   |

The interface shows intended effects apart from the raw arguments. Approving a call does not change its arguments, and it does not turn intended effects into a completed outcome.

## Hook payloads

Hooks declare `protocol_version: 2`. For tool events, the payload contains:

| Field            | Description                                                                                                                |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `tool.arguments` | The original arguments.                                                                                                    |
| `tool.effects`   | The intended effects, when the tool declares them.                                                                         |
| `tool.source`    | `"model"` for a model call, or `"user_action"` for a panel action run from the dashboard. A missing value means `"model"`. |

Conditions on effects use the separately declared effects schema. See [Hooks](https://raw.tlelabs.com/docs/extend/hooks/) for the response protocol.

## Panels

A panel is a live view that a tool keeps up to date. Panel state never goes to the model, except for one short confirmation line.

### Declaration

Declare panels in `tool.json` (`panels`), in the server’s config (for MCP), or by registration over ACP. Each declaration has these fields:

| Field       | Description                                                                              |
| ----------- | ---------------------------------------------------------------------------------------- |
| `id`        | Panel ID, unique within the tool.                                                        |
| `title`     | Title shown to the user.                                                                 |
| `icon`      | Optional. Unknown icons fall back to `panel` with a warning.                             |
| `placement` | `sidebar` (default) or `chat`.                                                           |
| `context`   | Optional. `summary` panels also send a bounded reminder to the model after a compaction. |
| `acp_plan`  | Optional. Sends the panel’s checklist as an ACP plan.                                    |
| `actions`   | Optional. Menu items on the panel.                                                       |

A `chat` placement shows the panel inline in the conversation. A `sidebar` placement shows it in the side panel.

### Updates

A tool updates a panel in one of two ways:

- Return a result block of the form `{ "type": "panel", "panel": "<id>", "op": "replace" | "patch" | "close", ... }` next to its normal content. Raw removes this block before hooks, caps, providers, and history see the result.
- Call `context.panels.update(panel, body)` while the handler runs. The call resolves with the new `revision`. `context.panels.get(panel)` returns the last revision and document.

Panel updates are included with the tool result, including when the result is an error.

### Blocks

A panel document is made of blocks. The same block catalog is used in both placements.

| Block     | Description                                                                           |
| --------- | ------------------------------------------------------------------------------------- |
| `form`    | A request for input, with text, single-select, or multi-select fields.                |
| `mermaid` | A diagram, with its source. If rendering is unavailable, the source is shown as text. |

Form response actions submit or cancel a pending request. They never grant tool permission.

### Limits

| Limit                      | Value                                       |
| -------------------------- | ------------------------------------------- |
| Declared panels per tool   | 4                                           |
| Panels per session         | 16. A closed panel is evicted to make room. |
| Updates per panel per call | 200                                         |
| Document size              | 64 KiB                                      |

### Panel actions

A panel action is a menu item that runs when the user selects it.

- `prompt` actions draft a message for the user. They run in the browser or client.
- `tool` actions run the declaring tool again through the normal tool path. Its `allow`, `ask`, and `deny` rules and its hooks apply. A click is never approval for an `ask` rule.

### Errors

Panel errors use these codes: `panel_invalid`, `panel_too_large`, `panel_undeclared`, `panel_not_owned`, `panel_unknown`, `panel_revision_conflict`, `panel_rate_limited`, `panel_limit`, and `panel_closed_context`.

A rejected update in a result block never fails the tool. The model receives a line such as `panel <id> update rejected: <code> <message>`, and the history records the error.

### Interaction requests

A local tool can ask the user a question with `context.interactions.request({ panel, document, timeout_ms? })`. The request contains exactly one form block.

- The default deadline is 30 minutes. A caller may set up to 24 hours.
- A request has one of these states: `pending`, `answered`, `cancelled`, `expired`, or `interrupted`.
- Cancelling the turn cancels the request. Reconnecting a browser leaves the wait in place.
- An answer is validated against the form, and invalid answers are kept pending.
- Answering a question never grants permission for a tool.

The `ask_user` tool uses this interface. A piped or non-interactive run returns `interaction_unavailable` immediately.

## Related

- [Tools](https://raw.tlelabs.com/docs/extend/tools/) covers manifests, selection, and rules from a user’s perspective.
- [Hooks](https://raw.tlelabs.com/docs/extend/hooks/) covers the hook protocol and examples.
- [Architecture](https://raw.tlelabs.com/docs/reference/architecture/) describes how the registry, policy, and sessions fit together.
