run function controls what
happens.
Define your first tool
Create a tool beside the agent, for exampleopencomputer/agents/support/tools/lookup-order.ts:
agent.ts:
run() in the managed agent runtime and returns
the result to the model.
Tool definition
* A tool has either
run, or preview and apply — never both, and never
just one of the pair.
Descriptions are the model’s documentation. Include the action, when it is
appropriate, and any important precondition.
Execution context
Therun function receives:
Use the cancellation signal with
fetch() and report progress before slow
steps:
${sessionId}:${toolCallId} can identify the invocation
in a downstream idempotency key. It stays the same when that invocation is
retried. A new call from the model has a new ID, even if it requests the same
action; use a business-level key when those calls must also converge.
The session result
An agent may mark one tool as its result tool. Its latest committed output is the session’sresult: the structured outcome an application reads
instead of parsing the final message. This tool checks a URL itself rather
than accepting a claimed status from the model:
output, and it runs with run() rather than
waiting for a person. The schema is
recorded with the deployment, and every value the tool returns is validated
against it.
A call to the result tool succeeds only once its output is committed to
the session:
- The value
run()returns is validated againstoutputand committed before the model is told the call succeeded. The session’sresultbecomes{ turnId, callId, reportedAt, data }, and the call’stool.completedevent carriesresult: true(Session events). - A value that does not match
output, or exceeds 8 KiB of JSON, fails the call; the model sees the reason and can call again. The previous result stands. - A call from a turn that was interrupted or has ended, or from a runtime the session no longer runs on, is refused the same way.
- A later call replaces the entire value. Reporting does not finish the turn. A subsequent turn that fails or is cancelled does not clear the result, and the event log keeps every call.
run(), where a tool can check that a branch exists
or a pull request is open, and throw to reject them; let the application
check what the result references. Read it from
GET /sessions/<id>. For a review-ready UI, also
check current activity and whether result.turnId belongs to the last settled
turn. An existing result may describe earlier work.
If the commit acknowledgement is lost, the tool can fail even though its
result was saved. OpenComputer retries the commit for the same tool call;
that deduplicates the saved result, not external writes inside run(). Prefer
tools that verify and report existing work. Give any external write its own
idempotency key.
Schema declarations
The result tool’soutput is copied into the deployment without evaluating
code. Use one of these forms:
- An object literal in the
defineTool()call. - A
constobject literal in the same module, as above. - A named import of such a
constfrom a module inside the agent directory.
let bindings, namespace members such as schemas.report,
package imports and re-exports are not supported for this output schema.
The build error names the unsupported form. input may use these static
forms or a schema constructed when the tool module loads; it is not subject
to result-output extraction. Neither schema infers the TypeScript type of
run’s input, which remains Record<string, unknown>. as const preserves
the schema’s literal types for your own typing and validation.
Write name, result and output as ordinary properties on the
defineTool() object. Spreads, computed property names, and a result value
other than literal true or false fail the build. A tool module whose
result declaration disagrees with its deployment fails to load.
Conditional tools
useTool() may be conditional. This keeps sensitive or specialized actions
out of the tool set until they are relevant:
useTool() also accepts a tool ID such as useTool("web-search") when the
runtime already provides that tool.
Give an agent a sandbox shell
Select the built-insandbox_exec tool when an agent needs to run commands,
inspect files, or use installed CLI packages:
sandbox_exec call lazily
acquires an isolated computer for the session, and later calls in that session
reuse it. Files that must survive restarts belong in /workspace; temporary
paths such as /tmp do not provide that persistence guarantee.
If your organization has an Enterprise custom package image, sandbox_exec
uses that image automatically. The agent definition still selects the same
tool ID, and cannot choose or override the image itself. You can see the
package image assigned to your organization under Settings → Customize VM
packages.
Call external APIs safely
For authenticated HTTP APIs, declare a connection, store the key as an OpenComputer secret, and call the connection’sfetch() method. This keeps
the credential outside the agent runtime; see
Secrets and outbound requests.
Managed GitHub connections are the exception: they supply
short-lived environment credentials so Git and gh work directly. Never
embed credentials in tool source or return them to the model.
For Google services, GitHub, or Linear, the credential belongs to whoever
connected the account and the platform refreshes it — call those with
callService instead.
Ask a question
ask is the platform’s question tool. Select it with useTool("ask") when a
turn may need to stop and ask a person before going on:
Calling
ask ends the turn with an open question. The turn completes with
outcome: "question", the session’s question holds what was asked, and
question.asked is recorded in the event log.
The reply arrives as the next input with
answer set, and anything written
meanwhile arrives with it as steering, within
bounds. A session has at most one open
question; the turn that interprets the answer may ask again.
How the question reaches a person depends on where the session lives: an
application answers it through the API or the
React hook, and a
Linear session shows it in the thread with
the options as buttons.
ask records that the agent asked. It does not undo or forbid anything the
turn did before asking. A turn meant only to propose should select ask and
tools that cannot write, and select the writing tools on the turn that
carries the answer.
The id ask is reserved: defineTool({ name: "ask" }) is refused, both by
defineTool and by the CLI’s build. ask runs on the default runtime for new deployments;
sessions with executionMode: "microvm" cannot select it.
ask is different from waiting for a person:
ask ends a turn with an open question the agent reasons about when it is
answered, while an approval tool records one exact write that runs, unchanged,
once someone approves it.
Wait for a person before writing
Some writes should not happen because a model decided they should. Such a tool proposes instead: the model calls it, nothing is written, and the person the agent is talking to sees a card with the exact change and decides. There is no separate primitive for this. GivedefineTool a preview and an
apply instead of a run, and the model’s call becomes a proposal. run is
written for you. useTool selects the tool exactly as it selects any other,
and the model cannot tell the difference — waiting for approval is a property
of a tool, not a different kind of thing.
attach like any other tool and is told the change was
recorded for approval, so it stops rather than reporting the work as done.
apply runs later, with the arguments that were on the card — no second pass
through the model, so what happens is what was agreed to. The proposing session
is usually gone by then, and that is fine: apply needs its arguments, not the
conversation.
Always pass decision.id
decision.id identifies this approval, and it is the same value on every
attempt to carry it out. Give it to whatever you call as an idempotency key.
If a write never reports back — the runtime died mid-flight, the answer was
lost — it is recorded as unconfirmed rather than failed, because it may well
have happened. That is only recoverable if running it again is safe, and this
is what makes it safe.
What this does and does not guarantee
Approval is a convention your code cooperates with. Nothing stops a tool from writing insidepreview. What the platform guarantees is narrower and
still worth having: an approved write runs once, from the arguments the
person agreed to, whether or not the session that proposed it still exists.
A tool that waits needs a conversation to ask in. Called from the playground,
a schedule or a webhook, there is nobody to ask, and the call fails saying so.
Computer commands
A session gets an isolated computer when it needs one; conversation alone does not require a computer. The model runs commands through the built-inshell tool. A command runs in the session’s workspace and takes command
and an optional timeoutSeconds: 120 by default, at
most 1800, and never more than the computer’s remaining lifetime allows; a
value out of range fails the call before anything runs. A command is not
started on a computer that cannot host it for its full timeout; a fresh
computer is used instead.
When the timeout passes, the command and every process it started are
stopped, and the call fails as a tool error the model sees: it names the
command and the timeout and carries the output so far. The turn continues,
and files the command wrote before it was stopped remain.
A command counts as stopped only once that is confirmed: every process it
started, detached ones included, is gone, or the computer itself was
terminated. When the computer stops answering while a command runs, its
outcome is unknown, and the call fails with that as the reason; the model
is never told a command was stopped when that was not confirmed. That
computer runs no further command until the unknown one is confirmed
stopped or the computer is replaced by a fresh one with the session’s
workspace, which the next command does on its own. See
End and interrupt for cancellation guarantees
and the compatibility limit for sessions using the older runtime.
Tools versus skills and MCP
- A tool executes TypeScript you own.
- The same tool with
previewandapplyinstead ofrunwaits for a person before it does it. - A skill teaches the agent a reusable procedure.
- An MCP server supplies a remote collection of tools.