@opencomputer/react provides useAgent: a session’s conversation, turn
activity and input controls. Your server creates sessions and checks access;
the hook reads their event log and sends turns through your routes. The API
key stays on the server.
Create the session on your server
Use the management client from trusted code. Address the agent by its environment alias, with one key per submission:taskId belongs to one submission. The platform chooses the deployment
active in the environment and records it on the session, so retry an
uncertain create with the same parameters and key: it returns the same
session even after a redeploy. Nothing in your application handles
deployment ids; pinning one is an advanced option of sessions.create.
Creating a session and sending its first turn are separate calls with
separate keys.
Expose three routes
The hook needs these management API routes, relative to itsbasePath:
Authenticate each request and check access to the session before forwarding
it with your organization API key. For example, a Next.js route handler:
requireUser and userOwnsSession are your application’s access checks.
The latter must enforce the application’s project, environment and agent
scope as well as the user’s access. Forward JSON bodies and response statuses
unchanged: the hook supplies the turn key in the body and reads the admission
receipt or error envelope from the response.
Attach and submit
Keep each submission’s text and key until its receipt arrives. If the reply is lost, Retry sends the same submission; it must not generate a new key.payload in it when sending structured input. An explicit rejection such as
session_ended requires resolving that error; a new key starts new work and
is not recovery of an uncertain request.
Turns sent while another runs are queued. send resolves on admission, not
when the agent finishes. It returns { sessionId, turnId, status, duplicate }:
- A new turn is
queuedorrunning. - A repeated key returns the existing turn and its current status, including
completed,failedorcancelled, withduplicate: true. - Keep both the text and payload unchanged when retrying a key.
messages after admission. Its event-log record
confirms that message instead of adding a second one.
Send options and errors
send rejects with a SendError. Rejection alone does not establish
whether a turn was admitted:
The hook also puts the message in
error. A turn that fails after admission
is separate: turn.failed sets the turn’s failure and the hook’s error.
Display activity and results
turns comes from the same durable event log as messages. Use its statuses
to distinguish queued work from running and settled turns:
Reporting a result does not finish a turn. The session’s latest result may
also belong to an earlier turn; see result provenance
before treating a session as ready for review.
A tool call is
running, completed, failed or cancelled. Calls still
running when their turn settles are settled too. The hook exposes failure
status but does not currently retain tool.failed.message or settledBy on
ToolCall; those details remain in the event log,
also available through onEvent. Older tool records may lack a name or
output.
isRunning is an activity hint, not the session’s lifecycle status. It also
includes this hook instance’s unsettled admission receipts. After reopening,
a queued turn is visible in turns but does not by itself set isRunning.
Use turns for queued/running counts and the session API
for session status.
Answer a question
An agent can end a turn by asking a question. The hook exposes the open question asquestion, { id, text, options, turnId? }, or null, and answer(questionId, text, options?) sends the
reply. The asking turn’s outcome is "question".
answer(id, text, options) is send(text, { ...options, answers: id })
and resolves with the same receipt. options is empty for a free-text
question; answer it with the person’s text. question clears when the
log records question.answered or question.closed for it. Answering a
question that is no longer open rejects with a SendError whose code is
question_stale; the hook’s question shows the current one.
While a question is open, send without answers resolves with a held
receipt, { status: "held", questionId } and no turnId: the message stays
in messages, rebuilt from the log’s message.held events after a reload,
and reaches the agent with the answer. dismiss(questionId) closes the
question without an answer; held messages then run as ordinary turns.
Stop and reopen
stop() requests an interrupt without spending a model turn. It resolves
once the request was accepted and rejects with a SendError when it was
not, after setting error, so a control that showed a stopping state can be
re-enabled and the request retried; a resolved promise is not
confirmation that execution has stopped. Watch the running
turn settle in turns. Stop affects that turn, so the next queued turn can
start afterwards. The interrupt contract
describes settlement and remote-command guarantees.
The hook does not expose stopping or ended session status. Read the
session through your server when your interface needs that lifecycle detail.
It also never suspends, resumes or ends an attached session; those operations
belong to the server that created it.
Reopen by mounting the hook with the same sessionId. It reads history from
after (default 0), including work completed while the page was closed,
then polls for new events. A failed poll sets error, backs off and resumes
from the same cursor; a successful recovery clears that polling error.
Changing sessionId replays the new session. A send still in flight for the
previous session settles for its caller without changing the new session’s
view. Remount your composer for the new session, for example
<Chat key={sessionId} sessionId={sessionId} />, so a retained submission
cannot be retried into another session.
after skips earlier events; it is not a snapshot. Start at 0 to reconstruct
the conversation and turn activity. Use another cursor only when the earlier
view is already retained elsewhere. See Sessions and turns
for the distinction between reopening a conversation and recovering failed
execution.
Bind memory
Choose memory bindings during server-side session creation; the browser cannot change them. To create a document and bind it in one helper call, seestartOnDocument
and its retry limits. For pinned session creation, use the explicit
document and session calls.
Pass onMemorySaved to refresh your document panel through an authenticated
owner route:
memorySaves also contains those events, with resource, documentId,
revision and bytes. Save events are best-effort; refresh on opening the
panel too, because documents can change without an event in this session.
Local development
For a local application, passagent-id@alias instead of a session ID:
send creates a session through the authenticated development
bridge. Later calls resume that session; each call streams a turn and then
suspends it. Unlike attach mode, it normally waits through the turn and suspension
before resolving. Failures after admission appear through error; the
returned receipt still describes admission, not successful completion. Created sessions
have no memory bindings, and the hook does not recover their identity across
a page reload. Use attach mode for an application users will reopen.
Run the watched agent deployment and web application separately:
basePath; the
second starts the web application. Credentials are not bundled into browser
code.