Agents APIReference

TypeScript SDK

@latentcode/sandbox-agents 0.1.1: a typed client for hosted agent sessions. It has no runtime dependencies and runs anywhere fetch does.

Install

The package is distributed by LatentStack rather than npm. Installing from the URL records it in package.json:

$npm install https://latentstack.dev/dl/latentcode-sandbox-agents-0.1.1.tgz

bun add, pnpm add and yarn add take the same URL. To pin a copy, download the tarball and install the file (npm install ./latentcode-sandbox-agents-0.1.1.tgz).

RuntimeSupported
Node.js22 or newer
Bun · DenoYes
BrowsersTechnically yes, but never ship an ls- key to a browser. Call from your server.

Create a client

TypeScript
import { LatentStack } from "@latentcode/sandbox-agents"

const client = new LatentStack({ apiKey: process.env.LS_API_KEY! })
const sessions = client.agents.sessions
new LatentStack(options: LatentStackOptions)
OptionType
apiKeystringRequired. Your ls-… key.
baseUrlstringYour LatentStack URL, if it isn't https://latentstack.dev.
fetchtypeof fetchYour own fetch, for proxies, tracing or tests.

client.agents.sessions

Lifecycle

create(params: CreateSessionParams): Promise<Session>
Starts a session. params holds the session settings (agent, environment, input, metadata) and an optional idempotencyKeythat makes the call safe to retry.
returnsSession
get(sessionId: string): Promise<Session>
Reads a session.
returnsSession
list(params?: { limit?: number; after?: string }): Promise<SessionList>
Your sessions, newest first. Pass next_after as after for the next page.
returns{ data, has_more, next_after }
delete(sessionId: string): Promise<Session>
Stops the session for good. Its events and patches stay readable.
returnsSession (stopping or stopped)
pause(sessionId: string): Promise<Session>
Saves its state and releases the sandbox.
returnsSession (pausing)
resume(sessionId: string): Promise<Session>
Continues a paused session in a new sandbox. message() does this on its own.
returnsSession (provisioning)

Input

message(sessionId: string, text: string, options?: { delivery?: "steer" | "queue" }): Promise<InputAccepted>
Sends a message. steer (default) folds into the current run; queue runs after it.
returns{ accepted, queued?, resumed? }
approve(sessionId: string, requestId: string, reply: "once" | "always" | "reject", message?: string): Promise<InputAccepted>
Answers an approval.requested frame.
cancel(sessionId: string): Promise<InputAccepted>
Interrupts the current run. The session stays open.
send(sessionId: string, input: SessionInput): Promise<InputAccepted>
Any of the above as a typed object, e.g. { type: "message", text, delivery }.

Following

events(sessionId: string, options?: EventsOptions): AsyncGenerator<StreamFrame>
The session's frames, live. It reconnects with after=<last seq> when the connection drops and ends when the session is stopped or failed. It throws a LatentStackError if the session can't be read (e.g. not_found) or the stream keeps closing (5 attempts in a row).
OptionType
afternumberStart after this seq, e.g. a stored session.last_seq. Default: from the beginning.
signalAbortSignalEnds the loop when aborted.
retryDelayMsnumberFixed wait between reconnects. Default: 1 s, 2 s, 3 s … per failed attempt.
wait(sessionId: string, options?: { until?: SessionStatusName[]; timeoutMs?: number; pollMs?: number }): Promise<Session>
Polls until the status is in until (default SETTLED: idle, waiting, paused, stopped, failed). timeoutMs defaults to 10 minutes and pollMs to 2 seconds. On timeout it throws LatentStackError with type timeout. Right after create() a session is briefly idle before its first message starts, so follow events() to know a task is done.
returnsSession

Artifacts

artifacts(sessionId: string): Promise<ArtifactList>
One record per repository, with a per-file summary.
returns{ data: Artifact[] }
artifact(sessionId: string, name = "patch", options?: { repo?: string }): Promise<string>
The patch as text. Pass repo when the session has several repositories.
returnsstring (unified diff)

Working with frames

StreamFrame is a union of SessionStatus, ApprovalRequested and Event. A durable event's type is an open string, so narrow on "seq" in frame first. data may be missing; read it with ?..

TypeScript
for await (const frame of sessions.events(id)) {
  if ("seq" in frame) {
    // Event: { seq, type, data: Record<string, unknown>, time }
    if (frame.type === "text.ended") console.log(frame.data?.text)
    if (frame.type === "run.completed") break
  } else if (frame.type === "session.status") {
    // SessionStatus: status, error, instance_number, pending_approvals
  } else if (frame.type === "approval.requested") {
    // ApprovalRequested: request_id, action, resources, metadata
    await sessions.approve(id, frame.request_id, "once")
  }
}

Errors

Every failed call throws LatentStackError, with status (0 if no response arrived), type and message. See Errors for the list of types.

TypeScript
import { LatentStackError } from "@latentcode/sandbox-agents"

try {
  await sessions.create({ agent: { model: "bedrock/us.anthropic.claude-sonnet-5" }, input: "hi" })
} catch (e) {
  if (e instanceof LatentStackError && e.type === "model_not_allowed") { /* pick another model */ }
  else throw e
}

Exports

ExportWhat
LatentStackThe client.
SessionsThe class behind client.agents.sessions.
LatentStackErrorThe error class.
SETTLED["idle", "waiting", "paused", "stopped", "failed"]
DEFAULT_BASE_URLhttps://latentstack.dev
typesSession, SessionList, SessionStatusName, Agent, Rule, Event, StreamFrame, SessionStatus, ApprovalRequested, PendingApproval, InputAccepted, SessionInput, Artifact, ArtifactList, FileChange, ApiError, CreateSessionParams, ListSessionsParams, WaitOptions, EventsOptions, LatentStackOptions

Upgrading

  • Each new version has its own install URL; see Install above. Update the URL in package.json and reinstall.
  • 0.1.1 added several repositories per session and the repo option on artifact().