Spaces (TypeScript)
The @trycua/cua/spaces helpers: threads, events, teleport approvals and typed errors.
Import from @trycua/cua/spaces. This page covers the hand-written TypeScript layer; the generated native binding (sandboxes, guest, spaces objects) is documented per module in the Cua SDK reference.
Classes#
CursorSmoother#
One remote cursor's interpolation buffer.
Constructors#
Constructor
new CursorSmoother(delayMs?): CursorSmoother;Parameters
| Parameter | Type | Default value |
|---|---|---|
delayMs | number | STREAM_DELAY_MS |
Returns
Properties#
Methods#
alpha()
alpha(localMs): number;Opacity at localMs.
Parameters
| Parameter | Type |
|---|---|
localMs | number |
Returns
number
position()
position(localMs): [number, number] | undefined;The position to draw at localMs, or undefined before any sample.
Parameters
| Parameter | Type |
|---|---|
localMs | number |
Returns
[number, number] | undefined
push()
push(
serverMs,
localMs,
x,
y,
target
): void;Adds a sample (serverMs 0 = unknown: the local time is used). Out-of-order samples are dropped.
Parameters
| Parameter | Type |
|---|---|
serverMs | number |
localMs | number |
x | number |
y | number |
target | string |
Returns
void
reset()
reset(): void;Forgets every sample (the next one snaps).
Returns
void
GroupChat#
A group chat. The 2..6 bound holds for every instance: construction and membership changes throw.
Constructors#
Constructor
new GroupChat(
title,
members,
options?
): GroupChat;Parameters
| Parameter | Type |
|---|---|
title | string |
members | readonly string[] |
options | { createdAt?: Date; id?: string; } |
options.createdAt? | Date |
options.id? | string |
Returns
Properties#
| Property | Modifier | Type | Default value |
|---|---|---|---|
createdAt | readonly | string | undefined |
id | readonly | string | undefined |
messages | public | GroupMessage[] | [] |
title | public | string | undefined |
Accessors#
isAtFloor
Get Signature
get isAtFloor(): boolean;Returns
boolean
isFull
Get Signature
get isFull(): boolean;Returns
boolean
memberIDs
Get Signature
get memberIDs(): readonly string[];Bot ids in join order. The human is implicit.
Returns
readonly string[]
membershipLabel
Get Signature
get membershipLabel(): string;4 of 6 bots.
Returns
string
remainingSeats
Get Signature
get remainingSeats(): number;Returns
number
Methods#
add()
add(botID): void;Parameters
| Parameter | Type |
|---|---|
botID | string |
Returns
void
remove()
remove(botID): void;Parameters
| Parameter | Type |
|---|---|
botID | string |
Returns
void
GroupChatError#
A membership rule was broken. The message says which number.
Extends#
Error
Properties#
| Property | Modifier | Type |
|---|---|---|
code | readonly | GroupChatErrorCode |
Methods#
alreadyAMember()
static alreadyAMember(id): GroupChatError;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
atFloor()
static atFloor(): GroupChatError;Returns
full()
static full(): GroupChatError;Returns
notAMember()
static notAMember(id): GroupChatError;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
tooFewBots()
static tooFewBots(have): GroupChatError;Parameters
| Parameter | Type |
|---|---|
have | number |
Returns
tooManyBots()
static tooManyBots(have): GroupChatError;Parameters
| Parameter | Type |
|---|---|
have | number |
Returns
unknownChat()
static unknownChat(id): GroupChatError;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
GroupChatStore#
Group chats: membership, fan-out, and the merged transcript.
Constructors#
Constructor
new GroupChatStore(messenger?): GroupChatStore;Parameters
| Parameter | Type |
|---|---|
messenger? | GroupMessenger |
Returns
Properties#
| Property | Modifier | Type | Default value | Description |
|---|---|---|---|---|
chats | public | GroupChat[] | [] | - |
lastError | public | string | undefined | undefined | The last membership complaint, cleared on the next success. |
working | readonly | Map<string, Set<string>> | undefined | Bots producing output right now, per chat. |
Methods#
add()
add(botID, chatID): void;Parameters
| Parameter | Type |
|---|---|
botID | string |
chatID | string |
Returns
void
attach()
attach(messenger): void;Parameters
| Parameter | Type |
|---|---|
messenger | GroupMessenger |
Returns
void
chat()
chat(id): GroupChat | undefined;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
GroupChat | undefined
collectReplies()
collectReplies(chatID): Promise<GroupMessage[]>;Folds what members said since the last poll into the transcript, attributed.
Parameters
| Parameter | Type |
|---|---|
chatID | string |
Returns
Promise<GroupMessage[]>
create()
create(title, members): GroupChat;Parameters
| Parameter | Type |
|---|---|
title | string |
members | readonly string[] |
Returns
delete()
delete(id): void;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
void
displayName()
displayName(botID): string;Parameters
| Parameter | Type |
|---|---|
botID | string |
Returns
string
react()
react(emoji, chatID): void;Attaches a reaction to the last line.
Parameters
| Parameter | Type |
|---|---|
emoji | string |
chatID | string |
Returns
void
refreshWorking()
refreshWorking(chatID): void;Parameters
| Parameter | Type |
|---|---|
chatID | string |
Returns
void
remove()
remove(botID, chatID): void;Parameters
| Parameter | Type |
|---|---|
botID | string |
chatID | string |
Returns
void
send()
send(text, chatID): Promise<GroupDelivery[]>;Sends one message to every member; returns each member's delivery.
Parameters
| Parameter | Type |
|---|---|
text | string |
chatID | string |
Returns
Promise<GroupDelivery[]>
subscribe()
subscribe(fn): () => void;Parameters
| Parameter | Type |
|---|---|
fn | () => void |
Returns
() => void
workingBots()
workingBots(chatID): string[];Parameters
| Parameter | Type |
|---|---|
chatID | string |
Returns
string[]
PlanLimitError#
The relay refused a new computer in use because the account's plan is at its
cap (HTTP 429, x-cua-relay-error: plan_limit_sessions|plan_limit_hours).
Computers already in use are never cut and retrying gets the same answer: close one,
or send the person to PlanLimitError.upgradeUrl.
message is the plain sentence, such as "You have 5 of 5 computers in use at
once on Pro. Close one or upgrade to Teams."
Extends#
Properties#
| Property | Modifier | Type | Description | Inherited from |
|---|---|---|---|---|
code | readonly | SpacesErrorCode | - | SpacesError.code |
detail | readonly | string | undefined | - | SpacesError.detail |
kind | readonly | PlanLimitKind | - | - |
limit | readonly | number | - | - |
plan | readonly | string | free, pro or teams; any other plan keeps its own name. | - |
status | readonly | number | undefined | - | SpacesError.status |
target | readonly | string | undefined | - | SpacesError.target |
upgradeUrl | readonly | string | undefined | Where to add room, or undefined when the error carried no link. | - |
used | readonly | number | - | - |
Methods#
PresenceRoster#
Who is in a Space's presence and where their cursors are. Pure state.
Constructors#
Constructor
new PresenceRoster(): PresenceRoster;Returns
Properties#
Accessors#
entries
Get Signature
get entries(): PresenceEntry[];Everyone, me first, then in join order.
Returns
others
Get Signature
get others(): PresenceEntry[];Everyone but me: the cursors to draw (mine is the real pointer).
Returns
Methods#
apply()
apply(e): boolean;Folds one event in. Returns whether anything changed.
Parameters
| Parameter | Type |
|---|---|
e | PresenceEvent |
Returns
boolean
colorOf()
colorOf(principalId): string;The color an agent shows as, for its avatar and its cursor alike: the color the server assigned once it is present (a requested color is kept unless another participant already holds it), else its stable presenceColor. Keyed by the id it joined with.
Parameters
| Parameter | Type |
|---|---|
principalId | string |
Returns
string
from()
static from(me, members?): PresenceRoster;A roster seeded from a fresh session: me and the members at join.
Parameters
| Parameter | Type | Default value |
|---|---|---|
me | PresenceParticipant | undefined |
members | readonly PresenceMember[] | [] |
Returns
get()
get(participantId): PresenceEntry | undefined;Parameters
| Parameter | Type |
|---|---|
participantId | string |
Returns
PresenceEntry | undefined
PresenceView#
Who is present and what to draw for each, folded from presence events.
Constructors#
Constructor
new PresenceView(me, delayMs?): PresenceView;A view for me (the caller's participant id).
Parameters
| Parameter | Type | Default value |
|---|---|---|
me | string | undefined |
delayMs | number | STREAM_DELAY_MS |
Returns
Properties#
Methods#
apply()
apply(e, localMs): boolean;Folds one event in at localMs. Returns whether anything changed.
Parameters
| Parameter | Type |
|---|---|
e | PresenceEvent |
localMs | number |
Returns
boolean
drawables()
drawables(localMs, pointer?): PresenceDrawable[];Everything to draw at localMs: remote cursors interpolated and faded,
and your own cursor at pointer (normalized; undefined when the pointer
is not over the surface) with its server shape. Also applies expire.
Parameters
| Parameter | Type |
|---|---|
localMs | number |
pointer? | { x: number; y: number; } |
pointer.x? | number |
pointer.y? | number |
Returns
expire()
expire(localMs): string[];Drops everyone but the caller when heartbeats stopped for 3 intervals. Returns the removed ids.
Parameters
| Parameter | Type |
|---|---|
localMs | number |
Returns
string[]
from()
static from(
me,
members?,
delayMs?,
localMs?
): PresenceView;A view seeded from a fresh session: me and the members at join.
Parameters
| Parameter | Type | Default value |
|---|---|---|
me | PresenceParticipant | undefined |
members | readonly PresenceMember[] | [] |
delayMs | number | STREAM_DELAY_MS |
localMs | number | ... |
Returns
participant()
participant(id): PresenceParticipant | undefined;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
PresenceParticipant | undefined
participantIds()
participantIds(): string[];Participant ids, the caller first, then in join order.
Returns
string[]
setHeartbeatIntervalMs()
setHeartbeatIntervalMs(ms): void;The expected heartbeat interval (default 5 s).
Parameters
| Parameter | Type |
|---|---|
ms | number |
Returns
void
shapeOf()
shapeOf(id): CursorShapeName | undefined;A participant's current shape (the caller's included).
Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
CursorShapeName | undefined
upsert()
upsert(participant): void;Adds or updates a participant.
Parameters
| Parameter | Type |
|---|---|
participant | PresenceParticipant |
Returns
void
RoutineStore#
Routines: storage, editing, and the clock that fires them.
Constructors#
Constructor
new RoutineStore(storage, runner?): RoutineStore;Parameters
| Parameter | Type |
|---|---|
storage | RoutineStorage |
runner? | RoutineRunner |
Returns
Properties#
| Property | Modifier | Type | Default value | Description |
|---|---|---|---|---|
log | public | FiringRecord[] | [] | What the scheduler did, newest first (at most 50). |
routines | public | Routine[] | [] | - |
storage | readonly | RoutineStorage | undefined | - |
Accessors#
isSchedulerRunning
Get Signature
get isSchedulerRunning(): boolean;Returns
boolean
Methods#
attach()
attach(runner): void;Parameters
| Parameter | Type |
|---|---|
runner | RoutineRunner |
Returns
void
create()
create(input): Routine;Parameters
| Parameter | Type |
|---|---|
input | { botID: string; enabled?: boolean; now?: Date; prompt: string; schedule: RoutineSchedule; title: string; } |
input.botID | string |
input.enabled? | boolean |
input.now? | Date |
input.prompt | string |
input.schedule | RoutineSchedule |
input.title | string |
Returns
delete()
delete(id): void;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
void
due()
due(now): Routine[];Parameters
| Parameter | Type |
|---|---|
now | Date |
Returns
Routine[]
fire()
fire(routine, now?): Promise<FiringRecord>;Fires one routine now, whatever the clock says ("Run now", and the scheduler's step).
Parameters
| Parameter | Type |
|---|---|
routine | Routine |
now | Date |
Returns
Promise<FiringRecord>
load()
load(): void;Reads the list back. A corrupt file starts empty, with a logged complaint.
Returns
void
routine()
routine(id): Routine | undefined;Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
Routine | undefined
routinesFor()
routinesFor(botID): Routine[];Parameters
| Parameter | Type |
|---|---|
botID | string |
Returns
Routine[]
save()
save(): boolean;Returns
boolean
setEnabled()
setEnabled(id, enabled): void;Enable or disable without deleting; the firing history is kept.
Parameters
| Parameter | Type |
|---|---|
id | string |
enabled | boolean |
Returns
void
startScheduler()
startScheduler(intervalMs?, clock?): void;One loop for every routine. Ticks never overlap.
Parameters
| Parameter | Type | Default value |
|---|---|---|
intervalMs | number | 15_000 |
clock | () => Date | ... |
Returns
void
stopScheduler()
stopScheduler(): void;Returns
void
subscribe()
subscribe(fn): () => void;Called after every change. Returns an unsubscribe.
Parameters
| Parameter | Type |
|---|---|
fn | () => void |
Returns
() => void
tick()
tick(now?): Promise<FiringRecord[]>;Evaluates the clock once and fires whatever is due; returns what it fired.
Parameters
| Parameter | Type |
|---|---|
now | Date |
Returns
Promise<FiringRecord[]>
update()
update(routine): void;Parameters
| Parameter | Type |
|---|---|
routine | Routine |
Returns
void
SpacesError#
Extends#
Error
Extended by#
Properties#
| Property | Modifier | Type |
|---|---|---|
code | readonly | SpacesErrorCode |
detail | readonly | string | undefined |
status | readonly | number | undefined |
target | readonly | string | undefined |
Methods#
auth()
static auth(message, options?): SpacesError;Parameters
| Parameter | Type |
|---|---|
message | string |
options? | SpacesErrorOptions |
Returns
protocol()
static protocol(message, options?): SpacesError;Parameters
| Parameter | Type |
|---|---|
message | string |
options? | SpacesErrorOptions |
Returns
timeout()
static timeout(message, options?): SpacesError;Parameters
| Parameter | Type |
|---|---|
message | string |
options? | SpacesErrorOptions |
Returns
usage()
static usage(message, options?): SpacesError;Parameters
| Parameter | Type |
|---|---|
message | string |
options? | SpacesErrorOptions |
Returns
SpaceTurnLock#
Serializes turns per Space, shared by every thread on that Space, so two bots on one Space take the Space in turn instead of fighting over it.
Constructors#
Constructor
new SpaceTurnLock(): SpaceTurnLock;Returns
Accessors#
active
Get Signature
get active(): string | null;The turn currently holding the Space, or null.
Returns
string | null
Methods#
enqueue()
enqueue(turnId): object;Queues turnId now (call order, not scheduling order).
Parameters
| Parameter | Type |
|---|---|
turnId | string |
Returns
object
| Name | Type |
|---|---|
acquired | Promise<() => void> |
waitingBehind | string | null |
Thread#
An agent run in a Space.
Constructors#
Constructor
new Thread(init): Thread;Parameters
| Parameter | Type |
|---|---|
init | { agent: AgentId; deleteOnClose: boolean; isolation: "space" | "none"; label?: string; notes?: string[]; runId: string; space: SpaceLike; spaces: SpacesLike; } |
init.agent | AgentId |
init.deleteOnClose | boolean |
init.isolation | "space" | "none" |
init.label? | string |
init.notes? | string[] |
init.runId | string |
init.space | SpaceLike |
init.spaces | SpacesLike |
Returns
Properties#
| Property | Modifier | Type | Description |
|---|---|---|---|
agent | readonly | AgentId | - |
isolation | readonly | "space" | "none" | space when the thread has its Space to itself, none when shared. |
label | readonly | string | undefined | - |
notes | readonly | string[] | Preparation steps the runtime could not complete, named. |
runId | readonly | string | - |
space | readonly | SpaceLike | - |
Accessors#
id
Get Signature
get id(): string;Returns
string
Methods#
close()
close(): Promise<void>;Stops the run and deletes a dedicated Space created for it.
Returns
Promise<void>
events()
events(options?): AsyncGenerator<ThreadEvent>;Polls the run and yields derived events: text, files, links, approval
prompts, and a state event on every status change. Stops once the run
settles or after maxPolls (bounded).
Parameters
| Parameter | Type |
|---|---|
options | EventOptions |
Returns
AsyncGenerator<ThreadEvent>
send()
send(text, options?): Promise<Turn>;Sends a follow-up. Turns on one Space are serialized. A run whose
published acceptsMessage is false (a turn is running, or it could not
be read) refuses unless force, and the refusal is returned, not hidden.
Parameters
| Parameter | Type |
|---|---|
text | string |
options | { force?: boolean; } |
options.force? | boolean |
Returns
Promise<Turn>
status()
status(tail?): Promise<ThreadStatus>;The run's status on the closed ladder, with the raw output tail.
Parameters
| Parameter | Type | Default value |
|---|---|---|
tail | number | 200 |
Returns
Promise<ThreadStatus>
stop()
stop(): Promise<{
reason: string;
stopped: boolean;
}>;Stops the run; stopped says whether its death was witnessed.
Returns
Promise<{
reason: string;
stopped: boolean;
}>
TranscriptAdapter#
Turns a growing transcript into events, without re-emitting what it already emitted.
The runtime gives us a tail, not a stream, so the adapter tracks what it has
consumed by content, not by offset: a tail that scrolled past our last known
position is detected, and only the portion it can prove is new is emitted.
When it cannot prove overlap at all: the tail
scrolled entirely past: it emits the whole tail and an error event saying
output was lost, rather than silently dropping it.
Constructors#
Constructor
new TranscriptAdapter(): TranscriptAdapter;Returns
Methods#
ingest()
ingest(tail, now?): ThreadEvent[];Feed the newest transcript tail; get back only the newly observed events.
Parameters
| Parameter | Type |
|---|---|
tail | string |
now | () => Date |
Returns
reset()
reset(): void;Reset for a new turn that restarts the session (codex resume does this).
Returns
void
Interfaces#
ApprovalRequestEvent#
The agent is blocked on a human decision.
No agent runtime in a Space emits this today (they run auto-approved). It is
defined because an app that puts an agent in front of a user needs the shape,
and because a derived detector can spot a REPL prompt that is waiting. Answer
it with thread.send(...): there is no separate approval RPC yet, and this
SDK will not invent one that silently does nothing.
Extends#
ThreadEventBase
Properties#
CursorArtShape#
One shape's art: an SVG path (absolute M, L, C, Z; nonzero fill) and its hot spot.
Properties#
ErrorEvent#
Extends#
ThreadEventBase
Properties#
EventOptions#
Properties#
FileEvent#
A file the agent appears to have produced, by absolute path inside the Space.
Fetch it with space.download(path, destDir): the SDK does not pre-fetch.
Extends#
ThreadEventBase
Properties#
FiringRecord#
One line of the scheduler's log, newest first.
Properties#
| Property | Type |
|---|---|
at | string |
firing | RoutineFiring |
routineID | string |
title | string |
GroupDelivery#
The result of fanning one message out to one member.
Properties#
GroupMessage#
Properties#
| Property | Type | Description |
|---|---|---|
at | string | - |
id | string | - |
reaction? | string | - |
speaker | GroupSpeaker | - |
text | string | - |
undelivered | boolean | This line records a message that did not reach its Bot. |
GroupMessenger#
How a group reaches its Bots. deliver never throws: a refusal is a result.
Methods#
deliver()
deliver(text, botID): Promise<GroupDelivery>;Parameters
| Parameter | Type |
|---|---|
text | string |
botID | string |
Returns
Promise<GroupDelivery>
displayName()
displayName(botID): string;Parameters
| Parameter | Type |
|---|---|
botID | string |
Returns
string
isWorking()
isWorking(botID): boolean;Whether the Bot is producing output right now (the typing row).
Parameters
| Parameter | Type |
|---|---|
botID | string |
Returns
boolean
latestReply()
latestReply(botID): Promise<string | undefined>;The Bot's most recent utterance, or undefined.
Parameters
| Parameter | Type |
|---|---|
botID | string |
Returns
Promise<string | undefined>
ImageEvent#
An image the agent produced. data is base64; path is set instead when the
image is only known by its in-Space location.
Extends#
ThreadEventBase
Properties#
LinkEvent#
Extends#
ThreadEventBase
Properties#
PresenceDrawable#
One cursor to draw now.
Properties#
| Property | Type |
|---|---|
alpha | number |
color | string |
displayId | string |
displayName | string |
isAgent | boolean |
isMe | boolean |
participantId | string |
shape | CursorShapeName |
shapeSource | string |
windowId? | string |
x | number |
y | number |
PresenceEntry#
One participant and their last cursor (normalized 0..1 over the streamed surface).
Properties#
| Property | Type | Description |
|---|---|---|
agent | boolean | - |
color | string | - |
cursor? | object | - |
cursor.shape? | CursorShapeName | - |
cursor.visible | boolean | - |
cursor.windowId? | string | - |
cursor.x | number | - |
cursor.y | number | - |
displayName | string | - |
participantId | string | - |
principalId | string | The identity it joined with (PresenceIdentity.id), unless the server asserts one. |
Routine#
One routine. Dates are ISO 8601 (UTC, whole seconds).
Properties#
| Property | Type | Description |
|---|---|---|
botID | string | The Bot that runs it. A routine never spans Bots; a group chat does. |
createdAt | string | - |
id | string | - |
isEnabled | boolean | - |
lastFiredAt? | string | When the scheduler last started a firing (persisted, so a restart does not re-fire the past). |
lastOutcome? | string | The scheduler's words about the last firing: started, refused, failed. |
lastRunID? | string | The agent run the last firing produced. |
prompt | string | The text handed to the Bot when it fires. |
schedule | RoutineSchedule | - |
title | string | - |
RoutineRunner#
Gives a Bot its routine turn. Never throws: a refusal is a result.
Methods#
fire()
fire(routine): Promise<RoutineFiring>;Parameters
| Parameter | Type |
|---|---|
routine | Routine |
Returns
Promise<RoutineFiring>
RoutineStorage#
Where the routine list lives. load returns undefined when nothing was saved.
Methods#
load()
load(): string | undefined;Returns
string | undefined
save()
save(json): void;Parameters
| Parameter | Type |
|---|---|
json | string |
Returns
void
SpacesErrorOptions#
Properties#
StartThreadOptions#
Properties#
| Property | Type | Description |
|---|---|---|
agent | AgentId | - |
label? | string | Your own label, echoed on the thread. |
placement | ThreadPlacement | - |
prompt | string | The first task. There is no empty thread. |
show? | boolean | Open a terminal on the Space desktop tailing the run (default false). |
StateEvent#
Extends#
ThreadEventBase
Properties#
| Property | Type | Description | Overrides | Inherited from |
|---|---|---|---|---|
derived | boolean | true when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | ThreadEventBase.derived |
kind | "state" | - | ThreadEventBase.kind | - |
observedAt | string | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | ThreadEventBase.observedAt |
reason | string | The ladder's reason for this status. | - | - |
seq | number | Monotonic within a thread, assigned by the SDK. | - | ThreadEventBase.seq |
state | AgentStatus | - | - | - |
TextEvent#
Extends#
ThreadEventBase
Properties#
ThreadStatus#
Everything the SDK knows about a run's current moment.
Properties#
| Property | Type | Description |
|---|---|---|
acceptsMessage | boolean | A follow-up sent now starts the next turn: the server's published rule (idle, or a crashed or failed run it restarts; not mid-turn). |
desktopWindow | string | null | The agent's terminal window on the Space desktop, when it was found. null means "not found", which is not the same as "does not exist". |
reason | string | Why the ladder landed there, in words. Always populated. |
runId | string | - |
status | AgentStatus | - |
transcript | string | The raw transcript tail exactly as the Space reported it. Always present, because every derived event above is an interpretation of this. |
Turn#
One delivered (or refused) message.
Properties#
Type Aliases#
AgentId#
type AgentId =
| "claude-code"
| "gemini-cli"
| "google-antigravity"
| "goose"
| "hermes"
| "openai-codex"
| "openclaw"
| "opencode"
| "pi";Agent CLIs the Spaces runtime can start (the contract's AGENT_IDS).
AgentStatus#
type AgentStatus =
| "running"
| "awaiting_input"
| "idle"
| "finished"
| "failed"
| "crashed"
| "unknown";The agent status ladder: one closed vocabulary for every harness and every provider. A UI can rely on this being closed.
idle and finished are deliberately different: both mean "no turn is
running", but idle means the session can still be continued and finished
means it cannot.
unknown is a real, reachable state and is not a synonym for "done". A
probe that could not run, an unreachable Space, and an interactive REPL whose
working-vs-waiting we cannot tell apart all land here. Reporting any of them
as idle is how a caller concludes an agent finished when the Space actually
fell over: and crashed exists so a dead process that never recorded an
exit status is never read as a clean finish.
CursorShapeName#
type CursorShapeName =
| "arrow"
| "text"
| "pointer"
| "resize_ew"
| "resize_ns"
| "resize_nwse"
| "resize_nesw"
| "move"
| "crosshair"
| "wait"
| "progress"
| "not_allowed"
| "grab"
| "grabbing";A presence cursor shape (the names of cua.env.v1.CursorShape).
GroupChatErrorCode#
type GroupChatErrorCode =
| "tooFewBots"
| "tooManyBots"
| "full"
| "atFloor"
| "alreadyAMember"
| "notAMember"
| "unknownChat";GroupSpeaker#
type GroupSpeaker =
| {
kind: "human";
}
| {
botID: string;
kind: "bot";
}
| {
kind: "system";
};Who said a line: the human, a Bot, or the group itself (joins, leaves, refusals).
PlanLimitKind#
type PlanLimitKind = "sessions" | "hours";Which plan cap the relay refused.
RoutineFiring#
type RoutineFiring =
| {
kind: "started";
runId: string;
}
| {
kind: "refused";
reason: string;
}
| {
kind: "failed";
reason: string;
};What happened when a routine fired. A refusal is not a failure.
RoutineSchedule#
type RoutineSchedule =
| {
kind: "everyMinutes";
minutes: number;
}
| {
hour: number;
kind: "dailyAt";
minute: number;
}
| {
hour: number;
kind: "weeklyOn";
minute: number;
weekday: number;
};The three recurrence shapes. weekday is 1 = Sunday … 7 = Saturday.
SpacesErrorCode#
type SpacesErrorCode =
| "auth"
| "transport"
| "http"
| "protocol"
| "usage"
| "not_found"
| "ambiguous_sandbox"
| "timeout"
| "verification"
| "isolation"
| "space_command"
| "host_unavailable"
| "unsupported"
| "capability_missing"
| "host_capability_missing"
| "spacesd_not_available"
| "teleport_refused"
| "plan_limit";Stable, matchable error codes. Adding a code is a minor release; removing or repurposing one is a breaking release.
ThreadEvent#
type ThreadEvent =
| TextEvent
| FileEvent
| ImageEvent
| LinkEvent
| ApprovalRequestEvent
| StateEvent
| ErrorEvent;ThreadEventKind#
type ThreadEventKind =
| "text"
| "file"
| "image"
| "link"
| "approval_request"
| "state"
| "error";What an agent sends back.
The honest situation, as of this release: the agent runtime inside a Space is
a terminal. agent_status returns the tail of the run's output (the agent
runs as a detached, tagged cua-spacesd process). There is no structured
result channel yet: no file manifest, no image parts, no approval
protocol. The supported agents are launched auto-approved and the sandbox is
the safety boundary.
So this module does two things and is careful about the difference:
- It defines the result model a chat app with agent coworkers needs: text, files, images, links, approval requests: as a stable discriminated union, so an app can be written against it today and keep working when the runtime starts emitting the real thing.
- It derives what it can from the terminal transcript, and marks every
derived event
derived: true. A consumer can therefore tell a fact ("the agent emitted this file") from an inference ("a path-shaped string appeared in the scrollback"). Nothing here pretends to be ground truth.
When the agent runtime gains a structured channel (see docs/), the adapter
gains a branch that emits the same events with derived: false. The union is
the stable contract; the adapter is not.
ThreadPlacement#
type ThreadPlacement =
| {
deleteOnClose?: boolean;
image?: string;
kind?: string;
on?: string;
runtime?: string;
type: "dedicated";
}
| {
acknowledgeNoIsolation: true;
space: string | SpaceLike;
type: "shared";
};Where a thread's agent runs. There is no default; choosing is the point.
Union Members#
Type Literal
{
deleteOnClose?: boolean;
image?: string;
kind?: string;
on?: string;
runtime?: string;
type: "dedicated";
}| Name | Type | Description |
|---|---|---|
deleteOnClose? | boolean | Delete the Space when the thread closes. Default true. |
image? | string | Image of the Space this thread creates (default: the canonical Linux image). |
kind? | string | auto (default), container or vm. |
on? | string | Where: local or cloud (default: the user's default location). |
runtime? | string | auto (default) or an engine the location offers for the kind. |
type | "dedicated" | - |
Type Literal
{
acknowledgeNoIsolation: true;
space: string | SpaceLike;
type: "shared";
}| Name | Type | Description |
|---|---|---|
acknowledgeNoIsolation | true | Required, and required to be literally true: this thread shares a filesystem, a browser profile, cookies and every app login with every other thread on the Space; nothing inside a Space is a security boundary. |
space | string | SpaceLike | The Space (id or handle) to place this thread on. |
type | "shared" | - |
Variables#
AGENT_IDS#
const AGENT_IDS: readonly AgentId[];CURSOR_ART#
const CURSOR_ART: object;The shared presence cursor art: fill with the participant's color over the outline.
Type Declaration#
| Name | Type |
|---|---|
canvas | number |
outline | object |
outline.color | string |
outline.width | number |
shapes | Readonly<Record<CursorShapeName, CursorArtShape>> |
CURSOR_SHAPES#
const CURSOR_SHAPES: readonly CursorShapeName[];Every shape, in wire order.
DATAGRAM_DELAY_MS#
const DATAGRAM_DELAY_MS: 66 = 66;Render delay on the 30 Hz datagram channel.
GROUP_MAX_BOTS#
const GROUP_MAX_BOTS: 6 = 6;GROUP_MIN_BOTS#
const GROUP_MIN_BOTS: 2 = 2;The product bound: a group of one is a thread; every message fans out to every member.
PRESENCE_FALLBACK_COLOR#
const PRESENCE_FALLBACK_COLOR: "#3b82f6" = "#3b82f6";Used when the server assigned no color.
PRESENCE_PALETTE#
const PRESENCE_PALETTE: readonly string[];The cursor palette, in cua-spacesd's assignment order.
ROUTINE_PREFIX#
const ROUTINE_PREFIX: "[routine]" = "[routine]";Marks a routine-originated turn in a transcript.
SETTLED#
const SETTLED: ReadonlySet<AgentStatus>;Statuses after which events() stops polling.
STREAM_DELAY_MS#
const STREAM_DELAY_MS: 100 = 100;The presence netcode model, mirrored exactly from the Rust core
(cua_spaces::presence::view) and checked against the same conformance
vectors. Pure and deterministic: every method takes the local time in
milliseconds (Date.now()).
- your own cursor is drawn at the local pointer, never from the network;
- remote cursors render STREAM_DELAY_MS (100 ms; 66 on the datagram channel) behind the newest sample, Catmull-Rom through at least 4 samples, else linear;
- past the newest sample they extrapolate for at most 100 ms, then hold;
- a jump over 25% of the surface, a target change or a re-show snaps;
- a cursor idle for 5 s fades out over 300 ms;
- a participant a heartbeat does not list, or everyone when heartbeats stop for 3 intervals, is removed.
Functions#
adoptThread()#
function adoptThread(
spaces,
spaceId,
runId
): Promise<Thread>;Adopts a run that already exists (started by another process or the MCP).
Parameters#
| Parameter | Type |
|---|---|
spaces | SpacesLike |
spaceId | string |
runId | string |
Returns#
Promise<Thread>
agentIdentity()#
function agentIdentity(id, displayName): object;An agent's presence identity, requesting its stable presenceColor.
Parameters#
| Parameter | Type |
|---|---|
id | string |
displayName | string |
Returns#
object
| Name | Type |
|---|---|
agent | true |
color | string |
displayName | string |
id | string |
approve()#
function approve(decide): TeleportApprover;A TeleportApprover from a function. The function sees exactly what
would leave this machine and returns the human's decision, or undefined
to cancel (the teleport then fails with TeleportRefused). It runs on a
worker thread of the SDK and must be synchronous.
Parameters#
| Parameter | Type |
|---|---|
decide | (manifest) => TeleportDecision | undefined |
Returns#
TeleportApprover
canCreateGroup()#
function canCreateGroup(members): boolean;Whether a Create button should be enabled for members.
Parameters#
| Parameter | Type |
|---|---|
members | readonly string[] |
Returns#
boolean
clockLabel()#
function clockLabel(hour, minute): string;8:05 AM.
Parameters#
| Parameter | Type |
|---|---|
hour | number |
minute | number |
Returns#
string
cursorArt()#
function cursorArt(shape): CursorArtShape;The art for shape; unknown names get the arrow.
Parameters#
| Parameter | Type |
|---|---|
shape | string |
Returns#
cursorArtSvg()#
function cursorArtSvg(
shape,
color,
size?
): string;A standalone SVG of shape filled with color, size pixels square.
The hot spot is at cursorArt(shape).hotspot * size / CURSOR_ART.canvas.
Parameters#
| Parameter | Type | Default value |
|---|---|---|
shape | string | undefined |
color | string | undefined |
size | number | CURSOR_ART.canvas |
Returns#
string
detectApproval()#
function detectApproval(fresh):
| {
options: string[];
prompt: string;
}
| null;Spot a terminal prompt that is waiting on a human.
Only fires on a prompt at the very end of the fresh output: a question in
the middle of the scrollback has already been answered. Recognizes the two
common shapes: a [y/N]-style suffix and a numbered choice list followed by
a prompt line.
Parameters#
| Parameter | Type |
|---|---|
fresh | string |
Returns#
| {
options: string[];
prompt: string;
}
| null
excerpt()#
function excerpt(body, max?): string;Trim an untrusted body for an error message without hiding it entirely.
Parameters#
| Parameter | Type | Default value |
|---|---|---|
body | string | undefined |
max | number | 400 |
Returns#
string
firingSummary()#
function firingSummary(f): string;started run <id>, refused: <why>, failed: <why>.
Parameters#
| Parameter | Type |
|---|---|
f | RoutineFiring |
Returns#
string
frameGroupMessage()#
function frameGroupMessage(
text,
botID,
chat,
name
): string;The framing each member receives: the room, then the human's words on the last line (so an agent that reads the last line still sees them).
Parameters#
| Parameter | Type |
|---|---|
text | string |
botID | string |
chat | GroupChat |
name | (botID) => string |
Returns#
string
idleAlpha()#
function idleAlpha(idleMs): number;Opacity of a cursor idle for idleMs.
Parameters#
| Parameter | Type |
|---|---|
idleMs | number |
Returns#
number
isCursorShape()#
function isCursorShape(name): name is CursorShapeName;Whether name is a known shape.
Parameters#
| Parameter | Type |
|---|---|
name | unknown |
Returns#
name is CursorShapeName
isDue()#
function isDue(routine, now): boolean;Whether the scheduler should fire routine at now. Measured from the
last firing, not the tick, so a sleeping scheduler fires once on waking.
Parameters#
| Parameter | Type |
|---|---|
routine | Routine |
now | Date |
Returns#
boolean
isoSeconds()#
function isoSeconds(d): string;ISO 8601 in UTC with whole seconds, the portable form every SDK reads.
Parameters#
| Parameter | Type |
|---|---|
d | Date |
Returns#
string
isSpacesError()#
function isSpacesError(value, code?): value is SpacesError;Narrowing helper for consumers: if (isSpacesError(e, 'verification')) …
Parameters#
| Parameter | Type |
|---|---|
value | unknown |
code? | SpacesErrorCode |
Returns#
value is SpacesError
lockFor()#
function lockFor(spaceId): SpaceTurnLock;One lock per Space id, so two handles to the same Space share it.
Parameters#
| Parameter | Type |
|---|---|
spaceId | string |
Returns#
memoryStorage()#
function memoryStorage(initial?): RoutineStorage & object;A RoutineStorage in memory.
Parameters#
| Parameter | Type |
|---|---|
initial? | string |
Returns#
RoutineStorage & object
nextFireDate()#
function nextFireDate(schedule, reference): Date | undefined;The first slot strictly after reference, in local time; undefined for an invalid schedule.
Parameters#
| Parameter | Type |
|---|---|
schedule | RoutineSchedule |
reference | Date |
Returns#
Date | undefined
parsePlanLimit()#
function parsePlanLimit(text):
| {
kind: PlanLimitKind;
limit: number;
message: string;
plan: string;
upgradeUrl?: string;
used: number;
}
| undefined;Reads the relay's plan-limit sentence (and the Upgrade: <url> the CLI and
SDK append) back out of an error's text. Undefined for any other text.
Parameters#
| Parameter | Type |
|---|---|
text | string |
Returns#
| {
kind: PlanLimitKind;
limit: number;
message: string;
plan: string;
upgradeUrl?: string;
used: number;
}
| undefined
parseRoutines()#
function parseRoutines(json): Routine[];Parses a saved routine list. Throws on anything malformed rather than guessing.
Parameters#
| Parameter | Type |
|---|---|
json | string |
Returns#
Routine[]
presenceColor()#
function presenceColor(id): string;The stable presence color of an agent or other principal: a palette color
picked by a hash (FNV-1a, 32-bit, over the UTF-8 id), the same answer as the
Rust core's presence_color in every binding. Request it when the agent
joins presence (PresenceIdentity.color) and use it as the background of
its avatar, so avatar and cursor come from one source.
Parameters#
| Parameter | Type |
|---|---|
id | string |
Returns#
string
presenceTextColor()#
function presenceTextColor(background): string;Black or white text, whichever has the better WCAG contrast on background (#rrggbb).
Parameters#
| Parameter | Type |
|---|---|
background | string |
Returns#
string
routineNextFire()#
function routineNextFire(routine, reference): Date | undefined;When routine next fires after reference, or undefined when disabled.
Parameters#
| Parameter | Type |
|---|---|
routine | Routine |
reference | Date |
Returns#
Date | undefined
routineTurnText()#
function routineTurnText(routine): string;The text a runner hands the Bot: [routine] <title>: <prompt>.
Parameters#
| Parameter | Type |
|---|---|
routine | Pick<Routine, "title" | "prompt"> |
Returns#
string
scheduleLabel()#
function scheduleLabel(schedule): string;The one-line description of a schedule: Every day at 8:00 AM.
Parameters#
| Parameter | Type |
|---|---|
schedule | RoutineSchedule |
Returns#
string
startThread()#
function startThread(spaces, options): Promise<Thread>;Starts an agent thread. placement is required and has no default.
Parameters#
| Parameter | Type |
|---|---|
spaces | SpacesLike |
options | StartThreadOptions |
Returns#
Promise<Thread>
toSpacesError()#
function toSpacesError(error): SpacesError;Wraps anything the native SDK threw as a SpacesError with a stable
code (the CuaError variant stays reachable as cause). Already-wrapped
errors pass through.
Parameters#
| Parameter | Type |
|---|---|
error | unknown |
Returns#
validatePlacement()#
function validatePlacement(placement): ThreadPlacement;Throws unless the caller genuinely chose a placement.
Parameters#
| Parameter | Type |
|---|---|
placement | ThreadPlacement | undefined |
Returns#
waitForPresence()#
function waitForPresence(
session,
match,
timeoutMs,
maxEvents?,
roster?
): Promise<PresenceEvent>;Reads session until an event matches, folding every event into roster
when given. Throws after timeoutMs or maxEvents events.
Parameters#
| Parameter | Type | Default value |
|---|---|---|
session | PresenceEventSource | undefined |
match | (e) => boolean | undefined |
timeoutMs | number | undefined |
maxEvents | number | 50 |
roster? | PresenceRoster | undefined |
Returns#
Promise<PresenceEvent>
wrapErrors()#
function wrapErrors<T>(body): Promise<T>;Runs body, rethrowing native errors as SpacesError.
Type Parameters#
| Type Parameter |
|---|
T |
Parameters#
| Parameter | Type |
|---|---|
body | () => Promise<T> |
Returns#
Promise<T>