TypeScript SDK

Sessions

List sessions, load their messages, delete them, and answer tool approvals with the TypeScript SDK.

client.sessions reads the conversations Blazing Agents stores for you. Use it to show a user their past chats, reload a transcript, delete a conversation, and approve or deny a tool call the agent is waiting on. To start or continue a session, call client.chat(). To learn how sessions and turns behave, read Sessions and turns.

const { data: sessions } = await client.sessions.list({ agentId, userId: "user_123" });
const { data: messages } = await client.sessions.messages({
  agentId,
  sessionId: sessions[0].id,
});

Every method takes one input object and accepts an optional abortSignal.

Available operations

MethodDescriptionReturns
list()List one agent's sessionsSessionsListResponse
listLatest()List recent sessions across agentsLatestSessionsListResponse
messages()Load or poll a session's messagesSessionMessagesResponse
delete()Delete a session for goodvoid
toolApprovals()List the session's tool approvalsToolApprovalsResponse
decideToolApproval()Approve or deny one tool callToolApprovalDecisionResponse
joinToolApprovalContinuation()Stream the rest of the turn after approvalsTerminalStreamResult

Methods

list()

Lists one agent's sessions, most recently updated first.

Signature: list(input: { agentId: string } & SessionsListOptions): Promise<SessionsListResponse>

const page = await client.sessions.list({ agentId, userId: "user_123", limit: 25 });
const next = page.nextCursor
  ? await client.sessions.list({ agentId, userId: "user_123", cursor: page.nextCursor })
  : null;
ParameterTypeRequiredDefaultDescription
agentIdstringyesnoneAgent ID (ag_…)
userIdstringnononeOnly this end user's sessions; "" for tenant-level ones
limitnumberno501 to 200 per page
cursorstringnononenextCursor from the previous page

An agent ID that does not exist in your tenant returns an empty page. Returns SessionsListResponse. Errors: validation_failed, invalid_cursor.

listLatest()

Lists the most recently updated sessions across all your agents.

Signature: listLatest(input?: LatestSessionsListOptions): Promise<LatestSessionsListResponse>

const inbox = await client.sessions.listLatest({ userId: "user_123", byAgent: true });
for (const session of inbox.data) {
  console.log(session.agentId, session.lastMessagePreview);
}
ParameterTypeRequiredDefaultDescription
byAgentbooleannofalseReturn at most one session per agent, its latest
userIdstringnononeOnly this end user's sessions; "" for tenant-level ones
limitnumberno501 to 200 per page
cursorstringnononenextCursor from the previous page

Use byAgent: true to build an inbox with one row per agent, instead of calling list() for each agent. Sessions of disabled agents are included. Returns LatestSessionsListResponse. Errors: validation_failed, invalid_cursor.

messages()

Loads a session's stored messages in the AI SDK UIMessage shape, so you can pass them to useChat as initial messages.

Signature: messages(input: { agentId: string; sessionId: string } & SessionMessagesOptions): Promise<SessionMessagesResponse>

const page = await client.sessions.messages({ agentId, sessionId });

const older = page.nextCursor
  ? await client.sessions.messages({ agentId, sessionId, cursor: page.nextCursor })
  : null;

const newer = page.latestCursor
  ? await client.sessions.messages({ agentId, sessionId, after: page.latestCursor })
  : null;
ParameterTypeRequiredDefaultDescription
agentIdstringyesnoneAgent ID (ag_…)
sessionIdstringyesnoneSession ID (ss_…)
limitnumberno501 to 200 per page
cursorstringnononeGo back to older messages
afterstringnononeFetch messages added since an earlier latestCursor

The first page holds the newest messages, in chronological order within the page. Pass nextCursor as cursor to go further back. Save latestCursor and pass it later as after to fetch only what is new. Do not pass cursor and after together.

Returns SessionMessagesResponse. Errors: validation_failed, invalid_cursor, not_found.

delete()

Deletes a session and its messages for good. You choose whether its artifacts go too.

Signature: delete(input: { agentId: string; sessionId: string; deleteArtifacts: boolean } & ResourceRequestOptions): Promise<void>

await client.sessions.delete({ agentId, sessionId, deleteArtifacts: false });

deleteArtifacts: true also deletes the files the agent published in this session; false keeps them. Errors: validation_failed, not_found.

toolApprovals()

Lists the tool calls in the session that need, or had, a decision. Listing changes nothing.

Signature: toolApprovals(input: { agentId: string; sessionId: string } & ResourceRequestOptions): Promise<ToolApprovalsResponse>

const { data, continuation } = await client.sessions.toolApprovals({ agentId, sessionId });
const pending = data.filter((approval) => approval.decision === "pending");

Show each pending call's toolName and input to the person deciding. continuation tracks the turn that resumes once every call is decided. Returns ToolApprovalsResponse. Errors: validation_failed, not_found.

decideToolApproval()

Approves or denies one pending tool call.

Signature: decideToolApproval(input: DecideToolApprovalBody & { agentId: string; sessionId: string; approvalId: string } & ResourceRequestOptions): Promise<ToolApprovalDecisionResponse>

const decision = await client.sessions.decideToolApproval({
  agentId,
  sessionId,
  approvalId,
  approved: true,
  reason: "The requested file is safe to read.",
});
ParameterTypeRequiredDescription
agentIdstringyesAgent ID (ag_…)
sessionIdstringyesSession ID (ss_…)
approvalIdstringyesapprovalId from toolApprovals()
approvedbooleanyestrue to run the call, false to block it
reasonstringnoWhy you decided, up to 1,000 characters

The decision covers only that one call. When the last pending call is decided, Blazing Agents resumes the turn: approved calls run and denied calls return a denied result to the agent. Disconnecting from the stream does not stop it. Returns ToolApprovalDecisionResponse with the continuationId to stream. Errors: validation_failed, not_found, tool_approval_decision_conflict (already decided).

joinToolApprovalContinuation()

Streams the rest of the turn after its tool calls are decided, from the first chunk.

Signature: joinToolApprovalContinuation(input: { agentId: string; sessionId: string; continuationId: string } & ResourceRequestOptions): Promise<TerminalStreamResult>

const continuation = await client.sessions.joinToolApprovalContinuation({
  agentId,
  sessionId,
  continuationId: decision.continuationId,
});

const response = continuation.toResponse(); // return this from your route

The stream uses the same format as chat(), and you can join it more than once: each join replays what was already produced, then follows the turn live until it ends. Read the body once per join, through toResponse() or toStream().

Returns TerminalStreamResult. Errors: session_busy while some calls still wait for a decision, not_found, and stream_error for a broken stream. A failure inside the resumed turn arrives as an error chunk in the stream.

Response types

SessionsListResponse

interface SessionsListResponse {
  data: SessionListItem[];
  nextCursor: string | null;
}

interface SessionListItem {
  id: string;
  agentVersion: number | null;
  messageCount: number;
  lastMessagePreview: string | null;
  userId: string;
  metadata: Record<string, unknown>;
  createdAt: string;
  updatedAt: string;
}

agentVersion is the version the session is pinned to, or null when each turn uses the agent's current version.

LatestSessionsListResponse

interface LatestSessionsListResponse {
  data: LatestSessionListItem[];
  nextCursor: string | null;
}

interface LatestSessionListItem extends SessionListItem {
  agentId: string;
  model: string | null;
  thinkingLevel: string | null;
  status: "active" | "disabled";
}

model, thinkingLevel, and status describe the agent as it is now, not the version the session is pinned to.

SessionMessagesResponse

interface SessionMessagesResponse {
  data: SessionMessage[];
  nextCursor: string | null;
  latestCursor: string | null;
}

interface SessionMessage {
  id: string;
  role: "system" | "user" | "assistant";
  parts: Array<{ type: string; [key: string]: unknown }>;
  metadata?: unknown;
}

ToolApprovalsResponse

interface ToolApprovalsResponse {
  data: ToolApprovalState[];
  continuation: { id: string; state: ToolApprovalContinuationState } | null;
}

interface ToolApprovalState {
  approvalId: string;
  toolCallId: string;
  toolName: string;
  input: JSONValue;
  decision: "pending" | "approved" | "denied";
  reason: string | null;
  tool?: ToolReference | null;
  assistantMessageId?: string;
  createdAt?: string;
  decidedAt?: string | null;
}

type ToolApprovalContinuationState = "waiting" | "queued" | "running" | "succeeded" | "failed";

input is the exact JSON the agent wants to pass to the tool. tool identifies it as a built-in or MCP tool when known. The package exports ToolApprovalState, ToolApprovalsResponse, and ToolReference.

ToolApprovalDecisionResponse

interface ToolApprovalDecisionResponse {
  continuationId: string;
  state: ToolApprovalContinuationState;
}

TerminalStreamResult

interface TerminalStreamResult {
  requestId?: string;
  toResponse: () => Response;
  toStream: () => ReadableStream<Uint8Array>;
}

Errors

Failures throw BlazingAgentsError. The codes you are most likely to handle:

CodeMeaning
invalid_cursorStart paging again without the cursor
not_foundNo such session, approval, or continuation for this agent
tool_approval_decision_conflictThe call was already decided; reload the approvals
session_busySome calls still wait for a decision; decide them first
agent_disabledThe agent is disabled, so the turn cannot resume

Next

On this page