Run an agent turn
An async Space does not keep an agent process alive. A member posts a turn; the render opens the sealed room state, runs the agent, writes the reply, and goes cold again.
That is the core runtime loop.
The turn flow
turn with room key and message- A member sends
{ op:'turn', room, mk, message }. - The transport checks
commit(mk)against the Space record. - If the Space is gated, identity is verified against the sealed admission policy.
- The render opens the sealed ledger.
- The member entry is appended.
- The agent runs with the Space's persona and tools.
- The reply, widgets, files, or pending proposals are appended.
- The ledger is re-sealed and stored.
- The room key is dropped.
Read the deeper version in Async Spaces.
Minimal turn
const r = await client.turn(room, mk, {
from: 'Ada',
text: 'Summarize our plan and list the unresolved decisions.'
})
For an open Space, that is enough.
Signed member turns
A signed turn proves which admitted member wrote the entry. The client signs the entry core and sends the signature with the message. For an email-gated Space, the message also carries an ID token and the member signing public key.
Conceptually:
await client.turn(room, mk, {
text: 'I approve the itinerary.',
}, {
email: 'ada@example.com',
googleIdToken,
sign: { priv: memberPrivateKey, pub: memberPublicKey }
})
The render verifies the identity and stores only the verified attribution, not the raw ID token.
When the agent answers
A turn always lands in the ledger. Whether the agent replies is a separate decision, controlled per Space from the composer:
| Mode | Behaviour |
|---|---|
always |
Reply to every message. |
auto (default) |
Reply if the Space is solo or the message addresses the agent. In a group message not aimed at it, a cheap model gate decides whether the message wants help. |
ondemand |
Reply only when addressed by name or @-mention; otherwise the message is stored silently. |
A silent turn is not a failure: it returns silent: true with the entry count, so the client can drop its "thinking"
indicator immediately rather than waiting for a timeout.
Two things override the mode. A venue that hard-mutes the agent (agentReplies: false — an observer room) always
wins. And if the chime-in gate cannot be reached, it fails open and the agent answers: an unavailable model must
not silently turn a group Space mute.
The gate is the reason a Space's tests should not depend on ambient model credentials — with a key present it makes a real call, and the model's verdict then decides whether a reply exists.
Tools during a turn
A Space's tools are declared in its sealed config. During a turn, the agent can call those tools. Tool outputs may become:
- plain text in the reply
- a shared widget such as a map, chart, diagram, itinerary, or flights card
- a file attached to the reply
- a pending proposal requiring human approval
High-stakes calls are not run directly. They become proposals under Delegated authority.
Reads after a turn
Clients normally watch a Space with pollSealed. When the sealed ledger changes, the client opens it locally and
renders the new entries.
const r = await client.pollSealed(room, mk, previousEtag, previousCount)
For gated Spaces, read operations also include the caller's identity so the server can refuse unauthorized reads before returning sealed content.
What plaintext exposure means
In the current production tier, the server-side render receives mk during turns. That is why the model can reason
over the Space. The key is request-scoped and dropped afterward, and storage remains ciphertext at rest.
This is not the same as "the operator can never see plaintext in use." The attested server tier is the path to making that stronger property checkable for production traffic; it is built and verifiable, but not yet serving ordinary Spaces.
Read: Status, The double blind, The attested tier
Code map
| Concern | File |
|---|---|
| HTTP turn op | agent/spaceHandler.mjs |
| Runtime service | agent/spaceService.mjs |
| Decrypt-once render | agent/asyncTurn.mjs |
| Agent tool loop | agent/brain.mjs, agent/brainTools.mjs |
| Delegated authority | agent/delegatedAuthority.mjs |
| Client turn wrapper | spaces/public/spaceClient.js |
Machine-readable source: agent-turns.md · every doc in one fetch: llms-full.txt (HTML) · ← create-space · tools-widgets →