Agents
Connect a model to document editing tools through revise/server.
Programmatic access uses revise/server from the revise package and follows the SDK license.
Your own agent loop#
agentTools(doc, { provider }) returns the editing tools in the shape a model
API expects, and a handle function that runs one tool call and returns the
tool-result message to send back. No model SDK is a dependency: plain objects
in, plain objects out.
import Anthropic from "@anthropic-ai/sdk";
import { ReviseDocument, agentTools } from "revise/server";
const anthropic = new Anthropic();
const doc = await ReviseDocument.open("contract.docx", { author: "Contract bot" });
const agent = agentTools(doc, { provider: "anthropic" });
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: "Change the governing law to New York." },
];
for (;;) {
const reply = await anthropic.messages.create({
model: "claude-sonnet-5-5",
max_tokens: 4096,
tools: agent.tools,
messages,
});
messages.push({ role: "assistant", content: reply.content });
const calls = reply.content.filter((block) => block.type === "tool_use");
if (calls.length === 0) break;
messages.push({
role: "user",
content: await Promise.all(calls.map(agent.handle)),
});
}
const { bytes } = await doc.export({ format: "docx" });
doc.dispose();| Provider | tools | handle(call) takes | and returns |
|---|---|---|---|
"anthropic" | Messages API tools | a tool_use content block | a tool_result block |
"openai" | Chat Completions function tools | an entry of message.tool_calls | a role: "tool" message |
"openai-responses" | Responses API function tools | a function_call output item | a function_call_output item |
With the OpenAI Responses API, append every output item the model returned
(reasoning and function_call items included) to your input, then the
function_call_output items handle returns, and call again until the model
stops calling tools.
Options:
| Option | |
|---|---|
include, exclude | Only these tools, or every tool except these |
maxResultChars | Cap on one result's text (default 100,000). Longer results end with a note telling the model how to page. |
handle never throws for a bad call: the model gets the error and can
correct itself. Calls run on the document's one tool session, so reads,
searches, and edits share context across turns, and parallel tool calls queue.
The session remembers what the model has read: when you compact or drop the
transcript, call agent.reset(). agent.version() changes whenever the
document does, so you can tell whether a turn edited it.
Edits are tracked changes unless you open the document with
mode: "editing". Review them with doc.changes(),
or let a person review them in the Revise Editor SDK:
pass the document as Y.js state (Y.encodeStateAsUpdate(doc.ydoc)), or
export a .docx with tracked changes and open that.