> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rewind.codes/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent tools

> Ready-made tools that let your model write, run, test, follow and fix scripts, with adapters for the Anthropic and OpenAI APIs.

The model needs more than `run_script`: it must be able to check, test, follow and steer its runs. The SDK ships the
tool definitions and their handlers, so your harness does not write them.

```ts theme={null}
const agentTools = rewind.namespace(user.id).agentTools({
  toolset: toolset.id,
  labels: { agent: "repo-analyst", conversation: conversation.id },
});
```

The runs the model starts belong to the client's end user: take the agent tools from `rewind.namespace(user.id)`, or
from a client holding [the end user's token](https://docs.rewind.codes/sdk/end-users.md#tokens).

## The tools

| Tool | Does |
| - | - |
| `run_script({ code, input?, wait? })` | Type-checks the script and starts a run; returns its output, its error, or what it waits for |
| `check_script({ code })` | Type-checks only |
| `test_script({ code, scenario })` | Runs against mocks, in virtual time; `scenario.from_run` replays the calls a run recorded |
| `get_run({ run_id, entry?, offset?, wait? })` | A run's state and trace; one journal entry in full; the next page of a long output |
| `list_runs({ status? })` | The runs started with the same labels: this conversation's |
| `signal_run({ run_id, name, payload? })` | Sends a signal |
| `cancel_run({ run_id })` | Cancels a run |
| `retry_run({ run_id, code })` | Forks the run with a corrected script: the calls already made are not made again |
| `search_tools({ query })` | The declarations of the matching tools, for toolsets too large for the prompt (with a toolset) |
| `save_workflow({ name, description, code, input, output, signals? })` | Saves a [workflow](https://docs.rewind.codes/sdk/workflows.md) the model's later scripts can start (with a toolset) |

## Plug them into your model

> [tab] Anthropic
>
> ```ts agent/anthropic.ts theme={null}
> import Anthropic from "@anthropic-ai/sdk";
> import { Rewind } from "@rewind.codes/sdk";
>
> const anthropic = new Anthropic();
> const rewind = new Rewind();
>
> const toolset = await rewind.toolsets.create({ http: { allow: ["api.github.com"] } });
> const agentTools = rewind.namespace(user.id).agentTools({
>   toolset: toolset.id,
>   labels: { agent: "repo-analyst", conversation: conversation.id },
> });
>
> const system = [
>   "You answer questions about GitHub repositories.",
>   "Solve each task by writing one TypeScript script and running it with run_script: the script",
>   "makes every call the task needs, in loops and in parallel, and returns only the answer.",
>   "If a run fails, read the error, fix the script and call retry_run: calls already made are reused.",
>   "",
>   "What scripts can use:",
>   toolset.declarations.prompt,
> ].join("\n");
>
> const messages: Anthropic.Beta.BetaMessageParam[] = [{ role: "user", content: question }];
>
> for (;;) {
>   const response = await anthropic.beta.messages.create({
>     model: "claude-opus-5-5",
>     max_tokens: 16000,
>     output_config: { effort: "high" },
>     betas: ["server-side-fallback-2026-07-01"],
>     fallbacks: "default",
>     system,
>     tools: agentTools.anthropic.tools,
>     messages,
>   });
>   messages.push({ role: "assistant", content: response.content });
>
>   if (response.stop_reason === "refusal") break;
>   if (response.stop_reason !== "tool_use") {
>     for (const block of response.content) if (block.type === "text") console.log(block.text);
>     break;
>   }
>
>   const calls = response.content.filter((block) => block.type === "tool_use");
>   const results = await Promise.all(calls.map((call) => agentTools.anthropic.handle(call)));
>   messages.push({ role: "user", content: results });
> }
> ```

> [tab] OpenAI Responses
>
> ```ts agent/openai.ts theme={null}
> import OpenAI from "openai";
>
> const openai = new OpenAI();
> let input: OpenAI.Responses.ResponseInput = [{ role: "user", content: question }];
> let previous: string | undefined;
>
> for (;;) {
>   const response = await openai.responses.create({
>     model: process.env.OPENAI_MODEL ?? "gpt-5",
>     instructions,
>     tools: agentTools.openai.responses.tools,
>     input,
>     previous_response_id: previous,
>   });
>
>   const calls = response.output.filter((item) => item.type === "function_call");
>   if (calls.length === 0) {
>     console.log(response.output_text);
>     break;
>   }
>   input = await Promise.all(calls.map((call) => agentTools.openai.responses.handle(call)));
>   previous = response.id;
> }
> ```

> [tab] OpenAI Chat Completions
>
> ```ts theme={null}
> const completion = await openai.chat.completions.create({
>   model,
>   messages,
>   tools: agentTools.openai.tools,
> });
> const message = completion.choices[0].message;
> messages.push(message);
> for (const call of message.tool_calls ?? []) {
>   messages.push(await agentTools.openai.handle(call)); // { role: "tool", tool_call_id, content }
> }
> ```

> [tab] Any other API
>
> ```ts theme={null}
> // Definitions: name, description, inputSchema (JSON Schema).
> const tools = agentTools.definitions.map((d) => toYourFormat(d));
>
> // A call: the tool's name, its input, and the model's tool call id.
> const result = await agentTools.handle(name, input, { toolCallId });
> // result.content: text for the model; result.isError: the model should look at it
> ```

The complete programs are in [`examples/agent`](https://github.com/planchon/rewind.codes/tree/main/examples/agent).

## What the model gets back

Results are plain text written for the model, and bounded, because they go into its context:

* **Completed**: the output, up to 256 KiB, with the call that reads the rest (`get_run` with `offset`), and the last
  log lines.
* **Waiting**: the run's id and what it waits for, such as a signal, an approval or a host tool.
* **Failed**: the error with its line and code frame, the call that failed, and the trace up to the failure.
* **Refused**: the type-checker's diagnostics, with their code frames.

Mistakes the model can fix (a bad argument, a refused script, a run that has ended, a quota reached) come back as a
result with `isError`. Faults of the platform or of your harness (an unreachable server, a 401, a 5xx, a key's client
that names no namespace) are thrown, for your code to handle.

`result.run` holds the run the call started or read, for your harness: for example, `rewind.approvals.pending(result.run)`
says which calls wait for your approval.

## Idempotency

The model's tool call id becomes the idempotency key of `run_script`, `retry_run` and `signal_run`: handling the same
tool call twice (a retry of your harness, a replayed conversation) starts one run, or delivers one signal. The key is
derived from the agent's labels and the id, so agents with different labels never share one.

## Labels scope the agent

`labels` are set on every run the model starts. They filter `list_runs`, and the tools that take a run id answer for a
run without them as for a run that does not exist. Give your harness an API key limited to these labels, or a token
carrying them, and the server enforces the scope too.

## Options

| Option | Default | What |
| - | - | - |
| `toolset` | | The toolset scripts import from `rewind:tools`. `search_tools` and `save_workflow` need one |
| `labels` | | Set on every run; scope `list_runs` and the run ids the tools accept |
| `tools` | all | A subset of the tools, such as `["run_script", "get_run"]` |
| `wait` | `"30s"` | How long `run_script`, `retry_run` and `cancel_run` wait for the run to end |
| `maxWait` | `"2m"` | The longest `wait` the model may ask for |
| `ttl` | 30 days | The TTL of the runs the model starts |
| `traceparent` | | The trace context of the LLM call that wrote the script, or a function that returns the current one |
| `outputLimit` | 256 KiB | The most of a run's output one result holds |

## Workflows the model saves

`save_workflow` saves a workflow with the agent's toolset, then derives a toolset that pins it. The later calls of the
same agent tools use the derived toolset: `agentTools.toolset` is its id.

If your harness creates the agent tools for each turn, keep `agentTools.toolset` with the conversation and pass it as
`toolset` the next time. The derived toolset has a new id: a worker started with `toolset: <first id>` does not claim
the tasks of its runs (start it without `toolset`), and credentials set on the first toolset must be set on the
derived one too.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.