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
ParameterTypeDefault value
delayMsnumberSTREAM_DELAY_MS
Returns

CursorSmoother

Properties#

PropertyModifierTypeDefault value
delayMsreadonlynumberSTREAM_DELAY_MS

Methods#

alpha()
alpha(localMs): number;

Opacity at localMs.

Parameters
ParameterType
localMsnumber
Returns

number

position()
position(localMs): [number, number] | undefined;

The position to draw at localMs, or undefined before any sample.

Parameters
ParameterType
localMsnumber
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
ParameterType
serverMsnumber
localMsnumber
xnumber
ynumber
targetstring
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
ParameterType
titlestring
membersreadonly string[]
options{ createdAt?: Date; id?: string; }
options.createdAt?Date
options.id?string
Returns

GroupChat

Properties#

PropertyModifierTypeDefault value
createdAtreadonlystringundefined
idreadonlystringundefined
messagespublicGroupMessage[][]
titlepublicstringundefined

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
ParameterType
botIDstring
Returns

void

remove()
remove(botID): void;
Parameters
ParameterType
botIDstring
Returns

void


GroupChatError#

A membership rule was broken. The message says which number.

Extends#

  • Error

Properties#

PropertyModifierType
codereadonlyGroupChatErrorCode

Methods#

alreadyAMember()
static alreadyAMember(id): GroupChatError;
Parameters
ParameterType
idstring
Returns

GroupChatError

atFloor()
static atFloor(): GroupChatError;
Returns

GroupChatError

full()
static full(): GroupChatError;
Returns

GroupChatError

notAMember()
static notAMember(id): GroupChatError;
Parameters
ParameterType
idstring
Returns

GroupChatError

tooFewBots()
static tooFewBots(have): GroupChatError;
Parameters
ParameterType
havenumber
Returns

GroupChatError

tooManyBots()
static tooManyBots(have): GroupChatError;
Parameters
ParameterType
havenumber
Returns

GroupChatError

unknownChat()
static unknownChat(id): GroupChatError;
Parameters
ParameterType
idstring
Returns

GroupChatError


GroupChatStore#

Group chats: membership, fan-out, and the merged transcript.

Constructors#

Constructor
new GroupChatStore(messenger?): GroupChatStore;
Parameters
ParameterType
messenger?GroupMessenger
Returns

GroupChatStore

Properties#

PropertyModifierTypeDefault valueDescription
chatspublicGroupChat[][]-
lastErrorpublicstring | undefinedundefinedThe last membership complaint, cleared on the next success.
workingreadonlyMap<string, Set<string>>undefinedBots producing output right now, per chat.

Methods#

add()
add(botID, chatID): void;
Parameters
ParameterType
botIDstring
chatIDstring
Returns

void

attach()
attach(messenger): void;
Parameters
ParameterType
messengerGroupMessenger
Returns

void

chat()
chat(id): GroupChat | undefined;
Parameters
ParameterType
idstring
Returns

GroupChat | undefined

collectReplies()
collectReplies(chatID): Promise<GroupMessage[]>;

Folds what members said since the last poll into the transcript, attributed.

Parameters
ParameterType
chatIDstring
Returns

Promise<GroupMessage[]>

create()
create(title, members): GroupChat;
Parameters
ParameterType
titlestring
membersreadonly string[]
Returns

GroupChat

delete()
delete(id): void;
Parameters
ParameterType
idstring
Returns

void

displayName()
displayName(botID): string;
Parameters
ParameterType
botIDstring
Returns

string

react()
react(emoji, chatID): void;

Attaches a reaction to the last line.

Parameters
ParameterType
emojistring
chatIDstring
Returns

void

refreshWorking()
refreshWorking(chatID): void;
Parameters
ParameterType
chatIDstring
Returns

void

remove()
remove(botID, chatID): void;
Parameters
ParameterType
botIDstring
chatIDstring
Returns

void

send()
send(text, chatID): Promise<GroupDelivery[]>;

Sends one message to every member; returns each member's delivery.

Parameters
ParameterType
textstring
chatIDstring
Returns

Promise<GroupDelivery[]>

subscribe()
subscribe(fn): () => void;
Parameters
ParameterType
fn() => void
Returns

() => void

workingBots()
workingBots(chatID): string[];
Parameters
ParameterType
chatIDstring
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#

PropertyModifierTypeDescriptionInherited from
codereadonlySpacesErrorCode-SpacesError.code
detailreadonlystring | undefined-SpacesError.detail
kindreadonlyPlanLimitKind--
limitreadonlynumber--
planreadonlystringfree, pro or teams; any other plan keeps its own name.-
statusreadonlynumber | undefined-SpacesError.status
targetreadonlystring | undefined-SpacesError.target
upgradeUrlreadonlystring | undefinedWhere to add room, or undefined when the error carried no link.-
usedreadonlynumber--

Methods#

PresenceRoster#

Who is in a Space's presence and where their cursors are. Pure state.

Constructors#

Constructor
new PresenceRoster(): PresenceRoster;
Returns

PresenceRoster

Properties#

PropertyType
meIdstring | undefined

Accessors#

entries
Get Signature
get entries(): PresenceEntry[];

Everyone, me first, then in join order.

Returns

PresenceEntry[]

others
Get Signature
get others(): PresenceEntry[];

Everyone but me: the cursors to draw (mine is the real pointer).

Returns

PresenceEntry[]

Methods#

apply()
apply(e): boolean;

Folds one event in. Returns whether anything changed.

Parameters
ParameterType
ePresenceEvent
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
ParameterType
principalIdstring
Returns

string

from()
static from(me, members?): PresenceRoster;

A roster seeded from a fresh session: me and the members at join.

Parameters
ParameterTypeDefault value
mePresenceParticipantundefined
membersreadonly PresenceMember[][]
Returns

PresenceRoster

get()
get(participantId): PresenceEntry | undefined;
Parameters
ParameterType
participantIdstring
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
ParameterTypeDefault value
mestringundefined
delayMsnumberSTREAM_DELAY_MS
Returns

PresenceView

Properties#

PropertyModifierTypeDefault value
delayMsreadonlynumberSTREAM_DELAY_MS
mereadonlystringundefined

Methods#

apply()
apply(e, localMs): boolean;

Folds one event in at localMs. Returns whether anything changed.

Parameters
ParameterType
ePresenceEvent
localMsnumber
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
ParameterType
localMsnumber
pointer?{ x: number; y: number; }
pointer.x?number
pointer.y?number
Returns

PresenceDrawable[]

expire()
expire(localMs): string[];

Drops everyone but the caller when heartbeats stopped for 3 intervals. Returns the removed ids.

Parameters
ParameterType
localMsnumber
Returns

string[]

from()
static from(
   me,
   members?,
   delayMs?,
   localMs?
): PresenceView;

A view seeded from a fresh session: me and the members at join.

Parameters
ParameterTypeDefault value
mePresenceParticipantundefined
membersreadonly PresenceMember[][]
delayMsnumberSTREAM_DELAY_MS
localMsnumber...
Returns

PresenceView

participant()
participant(id): PresenceParticipant | undefined;
Parameters
ParameterType
idstring
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
ParameterType
msnumber
Returns

void

shapeOf()
shapeOf(id): CursorShapeName | undefined;

A participant's current shape (the caller's included).

Parameters
ParameterType
idstring
Returns

CursorShapeName | undefined

upsert()
upsert(participant): void;

Adds or updates a participant.

Parameters
ParameterType
participantPresenceParticipant
Returns

void


RoutineStore#

Routines: storage, editing, and the clock that fires them.

Constructors#

Constructor
new RoutineStore(storage, runner?): RoutineStore;
Parameters
ParameterType
storageRoutineStorage
runner?RoutineRunner
Returns

RoutineStore

Properties#

PropertyModifierTypeDefault valueDescription
logpublicFiringRecord[][]What the scheduler did, newest first (at most 50).
routinespublicRoutine[][]-
storagereadonlyRoutineStorageundefined-

Accessors#

isSchedulerRunning
Get Signature
get isSchedulerRunning(): boolean;
Returns

boolean

Methods#

attach()
attach(runner): void;
Parameters
ParameterType
runnerRoutineRunner
Returns

void

create()
create(input): Routine;
Parameters
ParameterType
input{ botID: string; enabled?: boolean; now?: Date; prompt: string; schedule: RoutineSchedule; title: string; }
input.botIDstring
input.enabled?boolean
input.now?Date
input.promptstring
input.scheduleRoutineSchedule
input.titlestring
Returns

Routine

delete()
delete(id): void;
Parameters
ParameterType
idstring
Returns

void

due()
due(now): Routine[];
Parameters
ParameterType
nowDate
Returns

Routine[]

fire()
fire(routine, now?): Promise<FiringRecord>;

Fires one routine now, whatever the clock says ("Run now", and the scheduler's step).

Parameters
ParameterType
routineRoutine
nowDate
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
ParameterType
idstring
Returns

Routine | undefined

routinesFor()
routinesFor(botID): Routine[];
Parameters
ParameterType
botIDstring
Returns

Routine[]

save()
save(): boolean;
Returns

boolean

setEnabled()
setEnabled(id, enabled): void;

Enable or disable without deleting; the firing history is kept.

Parameters
ParameterType
idstring
enabledboolean
Returns

void

startScheduler()
startScheduler(intervalMs?, clock?): void;

One loop for every routine. Ticks never overlap.

Parameters
ParameterTypeDefault value
intervalMsnumber15_000
clock() => Date...
Returns

void

stopScheduler()
stopScheduler(): void;
Returns

void

subscribe()
subscribe(fn): () => void;

Called after every change. Returns an unsubscribe.

Parameters
ParameterType
fn() => void
Returns

() => void

tick()
tick(now?): Promise<FiringRecord[]>;

Evaluates the clock once and fires whatever is due; returns what it fired.

Parameters
ParameterType
nowDate
Returns

Promise<FiringRecord[]>

update()
update(routine): void;
Parameters
ParameterType
routineRoutine
Returns

void


SpacesError#

Extends#

  • Error

Extended by#

Properties#

PropertyModifierType
codereadonlySpacesErrorCode
detailreadonlystring | undefined
statusreadonlynumber | undefined
targetreadonlystring | undefined

Methods#

auth()
static auth(message, options?): SpacesError;
Parameters
ParameterType
messagestring
options?SpacesErrorOptions
Returns

SpacesError

protocol()
static protocol(message, options?): SpacesError;
Parameters
ParameterType
messagestring
options?SpacesErrorOptions
Returns

SpacesError

timeout()
static timeout(message, options?): SpacesError;
Parameters
ParameterType
messagestring
options?SpacesErrorOptions
Returns

SpacesError

usage()
static usage(message, options?): SpacesError;
Parameters
ParameterType
messagestring
options?SpacesErrorOptions
Returns

SpacesError


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

SpaceTurnLock

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
ParameterType
turnIdstring
Returns

object

NameType
acquiredPromise<() => void>
waitingBehindstring | null

Thread#

An agent run in a Space.

Constructors#

Constructor
new Thread(init): Thread;
Parameters
ParameterType
init{ agent: AgentId; deleteOnClose: boolean; isolation: "space" | "none"; label?: string; notes?: string[]; runId: string; space: SpaceLike; spaces: SpacesLike; }
init.agentAgentId
init.deleteOnCloseboolean
init.isolation"space" | "none"
init.label?string
init.notes?string[]
init.runIdstring
init.spaceSpaceLike
init.spacesSpacesLike
Returns

Thread

Properties#

PropertyModifierTypeDescription
agentreadonlyAgentId-
isolationreadonly"space" | "none"space when the thread has its Space to itself, none when shared.
labelreadonlystring | undefined-
notesreadonlystring[]Preparation steps the runtime could not complete, named.
runIdreadonlystring-
spacereadonlySpaceLike-

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
ParameterType
optionsEventOptions
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
ParameterType
textstring
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
ParameterTypeDefault value
tailnumber200
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

TranscriptAdapter

Methods#

ingest()
ingest(tail, now?): ThreadEvent[];

Feed the newest transcript tail; get back only the newly observed events.

Parameters
ParameterType
tailstring
now() => Date
Returns

ThreadEvent[]

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#

PropertyTypeDescriptionOverridesInherited from
derivedbooleantrue 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"approval_request"-ThreadEventBase.kind-
observedAtstringWall clock at the moment the SDK observed it, not when the agent produced it.-ThreadEventBase.observedAt
optionsstring[]Choices parsed from the prompt, when they were parseable.--
promptstring---
seqnumberMonotonic within a thread, assigned by the SDK.-ThreadEventBase.seq

CursorArtShape#

One shape's art: an SVG path (absolute M, L, C, Z; nonzero fill) and its hot spot.

Properties#

PropertyType
dstring
hotspotreadonly [number, number]

ErrorEvent#

Extends#

  • ThreadEventBase

Properties#

PropertyTypeDescriptionOverridesInherited from
derivedbooleantrue 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"error"-ThreadEventBase.kind-
messagestring---
observedAtstringWall clock at the moment the SDK observed it, not when the agent produced it.-ThreadEventBase.observedAt
seqnumberMonotonic within a thread, assigned by the SDK.-ThreadEventBase.seq

EventOptions#

Properties#

PropertyTypeDescription
intervalMs?numberDelay between polls (default 1000 ms).
maxPolls?numberHard bound on status polls (default 600).
signal?AbortSignal-
tail?numberLines of output per poll (default 200).

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#

PropertyTypeDescriptionOverridesInherited from
derivedbooleantrue 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"file"-ThreadEventBase.kind-
observedAtstringWall clock at the moment the SDK observed it, not when the agent produced it.-ThreadEventBase.observedAt
pathstring---
seqnumberMonotonic within a thread, assigned by the SDK.-ThreadEventBase.seq

FiringRecord#

One line of the scheduler's log, newest first.

Properties#

PropertyType
atstring
firingRoutineFiring
routineIDstring
titlestring

GroupDelivery#

The result of fanning one message out to one member.

Properties#

PropertyType
acceptedboolean
botIDstring
reasonstring

GroupMessage#

Properties#

PropertyTypeDescription
atstring-
idstring-
reaction?string-
speakerGroupSpeaker-
textstring-
undeliveredbooleanThis 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
ParameterType
textstring
botIDstring
Returns

Promise<GroupDelivery>

displayName()
displayName(botID): string;
Parameters
ParameterType
botIDstring
Returns

string

isWorking()
isWorking(botID): boolean;

Whether the Bot is producing output right now (the typing row).

Parameters
ParameterType
botIDstring
Returns

boolean

latestReply()
latestReply(botID): Promise<string | undefined>;

The Bot's most recent utterance, or undefined.

Parameters
ParameterType
botIDstring
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#

PropertyTypeDescriptionOverridesInherited from
data?string---
derivedbooleantrue 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"image"-ThreadEventBase.kind-
mimeTypestring---
observedAtstringWall clock at the moment the SDK observed it, not when the agent produced it.-ThreadEventBase.observedAt
path?string---
seqnumberMonotonic within a thread, assigned by the SDK.-ThreadEventBase.seq

LinkEvent#

Extends#

  • ThreadEventBase

Properties#

PropertyTypeDescriptionOverridesInherited from
derivedbooleantrue 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"link"-ThreadEventBase.kind-
observedAtstringWall clock at the moment the SDK observed it, not when the agent produced it.-ThreadEventBase.observedAt
seqnumberMonotonic within a thread, assigned by the SDK.-ThreadEventBase.seq
urlstring---

PresenceDrawable#

One cursor to draw now.

Properties#

PropertyType
alphanumber
colorstring
displayIdstring
displayNamestring
isAgentboolean
isMeboolean
participantIdstring
shapeCursorShapeName
shapeSourcestring
windowId?string
xnumber
ynumber

PresenceEntry#

One participant and their last cursor (normalized 0..1 over the streamed surface).

Properties#

PropertyTypeDescription
agentboolean-
colorstring-
cursor?object-
cursor.shape?CursorShapeName-
cursor.visibleboolean-
cursor.windowId?string-
cursor.xnumber-
cursor.ynumber-
displayNamestring-
participantIdstring-
principalIdstringThe identity it joined with (PresenceIdentity.id), unless the server asserts one.

Routine#

One routine. Dates are ISO 8601 (UTC, whole seconds).

Properties#

PropertyTypeDescription
botIDstringThe Bot that runs it. A routine never spans Bots; a group chat does.
createdAtstring-
idstring-
isEnabledboolean-
lastFiredAt?stringWhen the scheduler last started a firing (persisted, so a restart does not re-fire the past).
lastOutcome?stringThe scheduler's words about the last firing: started, refused, failed.
lastRunID?stringThe agent run the last firing produced.
promptstringThe text handed to the Bot when it fires.
scheduleRoutineSchedule-
titlestring-

RoutineRunner#

Gives a Bot its routine turn. Never throws: a refusal is a result.

Methods#

fire()
fire(routine): Promise<RoutineFiring>;
Parameters
ParameterType
routineRoutine
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
ParameterType
jsonstring
Returns

void


SpacesErrorOptions#

Properties#

PropertyTypeDescription
cause?unknown-
detail?stringFree-form, already-redacted detail shown in the message.
status?numberHTTP status, when code is 'http'.
target?stringRequest target, for triage. Never carries credentials.

StartThreadOptions#

Properties#

PropertyTypeDescription
agentAgentId-
label?stringYour own label, echoed on the thread.
placementThreadPlacement-
promptstringThe first task. There is no empty thread.
show?booleanOpen a terminal on the Space desktop tailing the run (default false).

StateEvent#

Extends#

  • ThreadEventBase

Properties#

PropertyTypeDescriptionOverridesInherited from
derivedbooleantrue 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-
observedAtstringWall clock at the moment the SDK observed it, not when the agent produced it.-ThreadEventBase.observedAt
reasonstringThe ladder's reason for this status.--
seqnumberMonotonic within a thread, assigned by the SDK.-ThreadEventBase.seq
stateAgentStatus---

TextEvent#

Extends#

  • ThreadEventBase

Properties#

PropertyTypeDescriptionOverridesInherited from
derivedbooleantrue 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"text"-ThreadEventBase.kind-
observedAtstringWall clock at the moment the SDK observed it, not when the agent produced it.-ThreadEventBase.observedAt
seqnumberMonotonic within a thread, assigned by the SDK.-ThreadEventBase.seq
textstring---

ThreadStatus#

Everything the SDK knows about a run's current moment.

Properties#

PropertyTypeDescription
acceptsMessagebooleanA 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).
desktopWindowstring | nullThe 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".
reasonstringWhy the ladder landed there, in words. Always populated.
runIdstring-
statusAgentStatus-
transcriptstringThe 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#

PropertyTypeDescription
idstring-
queuedBehind?stringThe turn this one waited behind on a shared Space, if any.
reasonstring-
state"delivered" | "refused"delivered means the process started, not that the work is done.
textstring-

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:

  1. 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.
  2. 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";
}
NameTypeDescription
deleteOnClose?booleanDelete the Space when the thread closes. Default true.
image?stringImage of the Space this thread creates (default: the canonical Linux image).
kind?stringauto (default), container or vm.
on?stringWhere: local or cloud (default: the user's default location).
runtime?stringauto (default) or an engine the location offers for the kind.
type"dedicated"-

Type Literal
{
  acknowledgeNoIsolation: true;
  space: string | SpaceLike;
  type: "shared";
}
NameTypeDescription
acknowledgeNoIsolationtrueRequired, 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.
spacestring | SpaceLikeThe 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#

NameType
canvasnumber
outlineobject
outline.colorstring
outline.widthnumber
shapesReadonly<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#

ParameterType
spacesSpacesLike
spaceIdstring
runIdstring

Returns#

Promise<Thread>


agentIdentity()#

function agentIdentity(id, displayName): object;

An agent's presence identity, requesting its stable presenceColor.

Parameters#

ParameterType
idstring
displayNamestring

Returns#

object

NameType
agenttrue
colorstring
displayNamestring
idstring

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#

ParameterType
decide(manifest) => TeleportDecision | undefined

Returns#

TeleportApprover


canCreateGroup()#

function canCreateGroup(members): boolean;

Whether a Create button should be enabled for members.

Parameters#

ParameterType
membersreadonly string[]

Returns#

boolean


clockLabel()#

function clockLabel(hour, minute): string;

8:05 AM.

Parameters#

ParameterType
hournumber
minutenumber

Returns#

string


cursorArt()#

function cursorArt(shape): CursorArtShape;

The art for shape; unknown names get the arrow.

Parameters#

ParameterType
shapestring

Returns#

CursorArtShape


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#

ParameterTypeDefault value
shapestringundefined
colorstringundefined
sizenumberCURSOR_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#

ParameterType
freshstring

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#

ParameterTypeDefault value
bodystringundefined
maxnumber400

Returns#

string


firingSummary()#

function firingSummary(f): string;

started run <id>, refused: <why>, failed: <why>.

Parameters#

ParameterType
fRoutineFiring

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#

ParameterType
textstring
botIDstring
chatGroupChat
name(botID) => string

Returns#

string


idleAlpha()#

function idleAlpha(idleMs): number;

Opacity of a cursor idle for idleMs.

Parameters#

ParameterType
idleMsnumber

Returns#

number


isCursorShape()#

function isCursorShape(name): name is CursorShapeName;

Whether name is a known shape.

Parameters#

ParameterType
nameunknown

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#

ParameterType
routineRoutine
nowDate

Returns#

boolean


isoSeconds()#

function isoSeconds(d): string;

ISO 8601 in UTC with whole seconds, the portable form every SDK reads.

Parameters#

ParameterType
dDate

Returns#

string


isSpacesError()#

function isSpacesError(value, code?): value is SpacesError;

Narrowing helper for consumers: if (isSpacesError(e, 'verification')) …

Parameters#

ParameterType
valueunknown
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#

ParameterType
spaceIdstring

Returns#

SpaceTurnLock


memoryStorage()#

function memoryStorage(initial?): RoutineStorage & object;

A RoutineStorage in memory.

Parameters#

ParameterType
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#

ParameterType
scheduleRoutineSchedule
referenceDate

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#

ParameterType
textstring

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#

ParameterType
jsonstring

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#

ParameterType
idstring

Returns#

string


presenceTextColor()#

function presenceTextColor(background): string;

Black or white text, whichever has the better WCAG contrast on background (#rrggbb).

Parameters#

ParameterType
backgroundstring

Returns#

string


routineNextFire()#

function routineNextFire(routine, reference): Date | undefined;

When routine next fires after reference, or undefined when disabled.

Parameters#

ParameterType
routineRoutine
referenceDate

Returns#

Date | undefined


routineTurnText()#

function routineTurnText(routine): string;

The text a runner hands the Bot: [routine] <title>: <prompt>.

Parameters#

ParameterType
routinePick<Routine, "title" | "prompt">

Returns#

string


scheduleLabel()#

function scheduleLabel(schedule): string;

The one-line description of a schedule: Every day at 8:00 AM.

Parameters#

ParameterType
scheduleRoutineSchedule

Returns#

string


startThread()#

function startThread(spaces, options): Promise<Thread>;

Starts an agent thread. placement is required and has no default.

Parameters#

ParameterType
spacesSpacesLike
optionsStartThreadOptions

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#

ParameterType
errorunknown

Returns#

SpacesError


validatePlacement()#

function validatePlacement(placement): ThreadPlacement;

Throws unless the caller genuinely chose a placement.

Parameters#

ParameterType
placementThreadPlacement | undefined

Returns#

ThreadPlacement


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#

ParameterTypeDefault value
sessionPresenceEventSourceundefined
match(e) => booleanundefined
timeoutMsnumberundefined
maxEventsnumber50
roster?PresenceRosterundefined

Returns#

Promise<PresenceEvent>


wrapErrors()#

function wrapErrors<T>(body): Promise<T>;

Runs body, rethrowing native errors as SpacesError.

Type Parameters#

Type Parameter
T

Parameters#

ParameterType
body() => Promise<T>

Returns#

Promise<T>