TypeScript SDK
Install, configure, and use the @runorca/orca-sdk TypeScript client for the registry API in Orca Agent Engine.
The @runorca/orca-sdk package is the official TypeScript client for the Agent Engine registry API. It wraps every registry resource the SDK supports - agents, sessions, environments, files, skills, vaults, memory stores, and triggers - with typed methods, cursor-based pagination, server-sent-event (SSE) streaming, automatic retries, and a typed error hierarchy.
The package is published from the public orca-ae/orca-sdk-typescript repository to npmjs.org. It requires Node.js 20 or later. This page describes version 0.2.3.
Install
Install the public package from npm. No GitHub token or custom .npmrc is required.
npm install @runorca/orca-sdk@0.2.3If you previously installed @orca-ae/orca-sdk from GitHub Packages, replace that dependency and its imports with @runorca/orca-sdk, then update your lockfile. Remove the old @orca-ae:registry mapping from your project's .npmrc if you no longer use that scope.
Construct the client
Import the default export and construct an Orca client. The constructor takes an apiKey and a baseURL:
import Orca from '@runorca/orca-sdk';
const orca = new Orca({
apiKey: process.env.ORCA_API_KEY,
baseURL: process.env.ORCA_BASE_URL,
});When apiKey is omitted, the SDK reads process.env.ORCA_API_KEY, falling back to null (no Authorization header) if that variable is unset too. baseURL is required; pass it explicitly or set process.env.ORCA_BASE_URL. The constructor throws OrcaError when neither is set. Trailing slashes on baseURL are stripped automatically.
The client also refuses to run in a browser, because the API key would be exposed to page scripts. Set dangerouslyAllowBrowser: true only when the credential is not reachable by end users.
The SDK reads ORCA_BASE_URL and sends its ORCA_API_KEY setting as a Bearer token. The CLI reads ORCA_REGISTRY_URL; its ORCA_API_KEY setting is a Workspace key sent as x-api-key, while ORCA_ACCESS_TOKEN is an OIDC Bearer token. The Go SDK also sends ORCA_API_KEY as x-api-key. Use the host root for every base URL, but configure each credential according to its client.
baseURL is the host root for your Workspace registry. Do not include /v1 or /v1/registry; the SDK adds its own core and extension paths. See Get your registry endpoint for how to derive it from the Workspace's service endpoints.
Authentication options
apiKey accepts three forms:
| Value | Behavior |
|---|---|
string | Used directly as the Authorization: Bearer token. |
Async function () => Promise<string> | Called per request; useful for token rotation. Must return a non-empty string. |
null | Disables the Authorization header (for use behind a separately authenticated proxy). |
Static token
const orca = new Orca({
apiKey: process.env.ORCA_API_KEY,
baseURL: process.env.ORCA_BASE_URL,
});Rotating token
async function fetchFreshToken(): Promise<string> {
const token = process.env.ORCA_API_KEY;
if (!token) throw new Error('Set ORCA_API_KEY.');
return token;
}
const orca = new Orca({
apiKey: fetchFreshToken,
baseURL: process.env.ORCA_BASE_URL,
});Replace fetchFreshToken with your token-refresh call when tokens rotate.
No auth header
const orca = new Orca({
apiKey: null,
baseURL: process.env.ORCA_BASE_URL,
});Workspace API key
The SDK sends apiKey as a Bearer token. To use a Workspace API key, send it as x-api-key instead. This configuration was verified against a Kubernetes registry with a provider-backed session:
import Orca from '@runorca/orca-sdk';
const orca = new Orca({
apiKey: null,
baseURL: process.env.ORCA_BASE_URL,
defaultHeaders: { 'x-api-key': process.env.ORCA_API_KEY },
});Client options
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | (() => Promise<string>) | null | process.env.ORCA_API_KEY | Bearer token, async token provider, or null. |
baseURL | string | process.env.ORCA_BASE_URL | Registry base URL. Required. |
timeout | number | 600000 | Per-request timeout in milliseconds (10 minutes). |
maxRetries | number | 2 | Retry cap for transient failures. |
defaultHeaders | object | - | Headers merged into every request. |
defaultQuery | object | - | Query parameters merged into every URL. |
fetchOptions | RequestInit | - | RequestInit merged into every fetch. |
logger | Logger | console | Object with debug, info, warn, error methods. |
logLevel | 'off' | 'debug' | 'info' | 'warn' | 'error' | process.env.ORCA_LOG or 'warn' | Logging verbosity. |
dangerouslyAllowBrowser | boolean | false | Bypass the guard that blocks browser execution. Only set this if your token is not exposed to end users. |
Resources
Each registry resource is a property on the client. Methods that create or fetch a single object return an APIPromise you can await. Core list methods return a page cursor you can iterate; Cloud extension list methods return their contract response directly (see Pagination).
In method signatures below, square brackets mark optional arguments. options supplies per-request controls such as headers, retries, timeouts, and an abort signal.
The client exposes these namespaces:
| Namespace | Covers |
|---|---|
orca.agents | Agent definitions, plus .versions. |
orca.sessions | Sessions, plus .events, .resources, .files, and .threads (with .threads.events). |
orca.environments | Environment templates. |
orca.files | Workspace-level file uploads and downloads. |
orca.skills | Skill bundles, plus .versions. |
orca.vaults | Vaults, plus .credentials. |
orca.memoryStores | Memory stores, plus .memories and .memoryVersions. |
orca.triggers | Agent triggers, plus .sessions. |
orca.discovery | Extension API group discovery through GET /apis. |
orca.cloud.apiResources | Resources advertised by StreamNative Cloud extension API group. |
orca.cloud.agents.providers | StreamNative Cloud model providers. |
orca.cloud.connections | Pulsar, Kafka, and generic connections. |
orca.cloud.functions | Functions over Pulsar or Kafka. |
orca.cloud.packages | Function, source, and sink packages. |
orca.cloud.catalog | Source, sink, and Kafka connector catalogs. |
orca.cloud.connectors | Pulsar IO sources and sinks, plus Kafka Connect. |
orca.cloud.health | StreamNative Cloud service health probes. |
orca.session(sessionId) | A session handle that pre-fills the session ID. |
The orca.cloud.* namespaces require the deployment to advertise cloud.sn.io through GET /apis. Against a self-hosted engine, they throw ExtensionNotAvailableError before sending the resource request.
Core resource updates use POST. Trigger updates are partial POST requests. Cloud extension methods follow their published contracts: connections and function/source/sink configuration updates use PUT, while Kafka Connect exposes separate config and offset methods.
Agents
An agent is a registry resource that defines a model, system prompt, tools, MCP servers, and skills. Agents are versioned: update takes the current version for optimistic concurrency, and historical versions stay retrievable.
| Method | Signature |
|---|---|
| Create | orca.agents.create(params[, options]) |
| Retrieve | orca.agents.retrieve(agentId[, { version }[, options]]) |
| Update | orca.agents.update(agentId, params[, options]) - POST /v1/agents/{agentId} |
| List | orca.agents.list([{ include_archived, 'created_at[gte]', 'created_at[lte]', limit, page }[, options]]) |
| Archive | orca.agents.archive(agentId[, options]) - POST /v1/agents/{agentId}/archive |
list accepts include_archived and the 'created_at[gte]' / 'created_at[lte]' timestamp filters
alongside limit and page. There is no provider filter - the only provider on this surface is a
field on the model object.
const agent = await orca.agents.create({
model: 'claude-sonnet-4-6',
name: 'Support triage',
system: 'You triage incoming support tickets.',
});
console.log(agent.id, agent.version);
// Optimistic-concurrency update: pass the version you last saw
const updated = await orca.agents.update(agent.id, {
version: agent.version,
description: 'Triages and routes support tickets.',
});Agent versions
orca.agents.versions.list(agentId[, { include_archived, 'created_at[gte]', 'created_at[lte]', limit, page }[, options]])
returns historical snapshots, each an Agent.
for await (const version of orca.agents.versions.list(agent.id)) {
console.log(version.version, version.updated_at);
}Agent providers
Providers are the LLM backends registered in the Workspace. A provider name is what you pass as the
provider filter on list calls.
| Method | Signature |
|---|---|
| List | orca.cloud.agents.providers.list([options]) |
| Retrieve | orca.cloud.agents.providers.retrieve(providerName[, options]) |
providers.list resolves to a plain AgentProvider[], not a page cursor.
const providers = await orca.cloud.agents.providers.list();
for (const p of providers) {
console.log(p.name, p.type, p.api_key_configured);
}An AgentProvider can include name, type, api_url, api_version, beta_version, api_key_env (the environment variable that holds the key), and api_key_configured (whether the server already has one).
Cloud API resources and health
These Cloud extension methods are not paginated.
| Method | Signature | Return value |
|---|---|---|
| API resources | orca.cloud.apiResources.list([options]) | CloudAPIResourceList for cloud.sn.io/v1 |
| Health | orca.cloud.health.check([options]) | boolean |
| Readiness | orca.cloud.health.ready([options]) | boolean |
| Liveness | orca.cloud.health.live([options]) | boolean |
Environments
An environment defines runtime configuration for a session.
| Method | Signature |
|---|---|
| Create | orca.environments.create(params[, options]) |
| Retrieve | orca.environments.retrieve(environmentId[, options]) |
| Update | orca.environments.update(environmentId, params[, options]) - POST /v1/environments/{environmentId} |
| List | orca.environments.list([{ include_archived, limit, page }[, options]]) |
| Delete | orca.environments.delete(environmentId[, options]) |
| Archive | orca.environments.archive(environmentId[, options]) |
Environment.config is a discriminated union of the spec's cloud and self_hosted response shapes. A cloud config carries typed packages and networking blocks.
const environment = await orca.environments.create({ name: 'production' });Sessions
A session is a single run of an agent against an environment. Sessions are top-level: create requires environment_id and either agent or the compatibility agent_id field. list filters by agent_id.
| Method | Signature |
|---|---|
| Create | orca.sessions.create(params[, options]) |
| Retrieve | orca.sessions.retrieve(sessionId[, options]) |
| Update | orca.sessions.update(sessionId, params[, options]) - POST /v1/sessions/{sessionId} |
| List | orca.sessions.list([{ agent_id, include_archived, limit, page }[, options]]) |
| Delete | orca.sessions.delete(sessionId[, options]) |
| Archive | orca.sessions.archive(sessionId[, options]) |
const session = await orca.sessions.create({
agent: agent.id,
environment_id: environment.id,
});
console.log(session.id, session.status);For working with a single session repeatedly, prefer the session handle, which pre-fills the session ID.
Session events
Events are the conversation transcript. Send user input with send, read persisted history with list, and follow the live run with stream (see Streaming).
| Method | Signature |
|---|---|
| List | orca.sessions.events.list(sessionId[, { limit, page, 'created_at[gt]', 'created_at[gte]', 'created_at[lt]', 'created_at[lte]', order, types, subpath }[, options]]) |
| Send | orca.sessions.events.send(sessionId, params[, options]) |
| Stream | orca.sessions.events.stream(sessionId[, options]) or orca.sessions.events.stream(sessionId[, { from_cursor, subpath, event_deltas }[, options]]) |
await orca.sessions.events.send(session.id, {
events: [
{ type: 'user.message', content: [{ type: 'text', text: 'Summarize today\'s tickets.' }] },
],
});Each event has an id and a type (such as user.message, agent.message, tool.result, or session.status_idle); event-specific fields like content vary by type.
Session resources
Resources are objects mounted into a session - for example a memory store directory the agent can read and write.
| Method | Signature |
|---|---|
| List | orca.sessions.resources.list(sessionId[, { limit, page }[, options]]) |
| Add | orca.sessions.resources.add(sessionId, params[, options]) |
| Retrieve | orca.sessions.resources.retrieve(sessionId, resourceId[, options]) |
| Update | orca.sessions.resources.update(sessionId, resourceId, params[, options]) - POST |
| Delete | orca.sessions.resources.delete(sessionId, resourceId[, options]) |
Session files
Files attached to a running session - output the agent wrote, or input mounted into it. These are scoped to one session and are distinct from the Workspace-level files resource.
| Method | Signature |
|---|---|
| List | orca.sessions.files.list(sessionId[, { limit, after_id, before_id }[, options]]) |
| Retrieve | orca.sessions.files.retrieve(sessionId, fileId[, options]) |
| Download | orca.sessions.files.download(sessionId, fileId[, options]) - resolves to Response |
| Delete | orca.sessions.files.delete(sessionId, fileId[, options]) |
download resolves to a Response, so read the bytes with the method that suits your target - arrayBuffer(), text(), or body for a stream.
for await (const file of orca.sessions.files.list(session.id)) {
const response = await orca.sessions.files.download(session.id, file.id);
console.log(file.id, (await response.arrayBuffer()).byteLength);
}SessionFile has typed id, filename, mime_type, size_bytes, and created_at fields.
Session threads
A session has one primary thread plus zero or more child threads spawned by the coordinator as the session runs. The SDK exposes read and archive operations and per-thread event streaming; it does not create threads.
| Method | Signature |
|---|---|
| List | orca.sessions.threads.list(sessionId[, { limit, page }[, options]]) |
| Retrieve | orca.sessions.threads.retrieve(sessionId, threadId[, options]) |
| Archive | orca.sessions.threads.archive(sessionId, threadId[, options]) |
| List events | orca.sessions.threads.events.list(sessionId, threadId[, { limit, page }[, options]]) |
| Stream events | orca.sessions.threads.events.stream(sessionId, threadId[, options]) or orca.sessions.threads.events.stream(sessionId, threadId[, { from_cursor, event_deltas }[, options]]) |
for await (const thread of orca.sessions.threads.list(session.id)) {
console.log(thread.id, thread.status, thread.parent_thread_id);
}Files
Files are ad-hoc assets uploaded with multipart/form-data. Pass a File (or any Uploadable) as the file field.
Set the MIME type when you construct a File or call toFile. The upload request takes only file.
| Method | Signature |
|---|---|
| Upload | orca.files.upload({ file }[, options]) |
| Retrieve | orca.files.retrieve(fileId[, options]) |
| Download | orca.files.download(fileId[, options]) - resolves to Response |
| List | orca.files.list([{ limit, after_id, before_id }[, options]]) |
| Delete | orca.files.delete(fileId[, options]) |
import { toFile } from '@runorca/orca-sdk';
const file = new File(['hello\n'], 'hello.txt', { type: 'text/plain' });
const meta = await orca.files.upload({ file });
console.log(meta);
const generatedFile = await toFile(new TextEncoder().encode('hello\n'), 'generated.txt', {
type: 'text/plain',
});
await orca.files.upload({ file: generatedFile });
// `retrieve` returns metadata; `download` returns the bytes.
const response = await orca.files.download(meta.id);
console.log(await response.text());Skills
A skill is a filesystem-shaped bundle of expertise an agent loads on demand. Skills are uploaded as multipart bundles and are version-additive - upload a new version rather than mutating one in place.
| Method | Signature |
|---|---|
| Create | orca.skills.create(params[, options]) - multipart bundle |
| Retrieve | orca.skills.retrieve(skillId[, options]) |
| List | orca.skills.list([{ limit, page }[, options]]) |
| Delete | orca.skills.delete(skillId[, options]) |
| Create version | orca.skills.versions.create(skillId, params[, options]) - multipart bundle |
| Retrieve version | orca.skills.versions.retrieve(skillId, versionId[, options]) |
| List versions | orca.skills.versions.list(skillId[, { limit, page }[, options]]) |
| Delete version | orca.skills.versions.delete(skillId, versionId[, options]) |
Vaults
A vault is a named container for credentials that agents use to authenticate to external services.
| Method | Signature |
|---|---|
| Create | orca.vaults.create(params[, options]) |
| Retrieve | orca.vaults.retrieve(vaultId[, options]) |
| Update | orca.vaults.update(vaultId, params[, options]) - POST /v1/vaults/{vaultId} |
| List | orca.vaults.list([{ include_archived, limit, page }[, options]]) |
| Delete | orca.vaults.delete(vaultId[, options]) |
| Archive | orca.vaults.archive(vaultId[, options]) |
Vault credentials
| Method | Signature |
|---|---|
| List | orca.vaults.credentials.list(vaultId[, { include_archived, limit, page }[, options]]) |
| Create | orca.vaults.credentials.create(vaultId, params[, options]) |
| Retrieve | orca.vaults.credentials.retrieve(vaultId, credentialId[, options]) |
| Update | orca.vaults.credentials.update(vaultId, credentialId, params[, options]) - POST |
| Delete | orca.vaults.credentials.delete(vaultId, credentialId[, options]) |
| Archive | orca.vaults.credentials.archive(vaultId, credentialId[, options]) |
| Validate | orca.vaults.credentials.validate(vaultId, credentialId[, options]) - validates MCP OAuth configuration |
This static_bearer example needs MCP_SERVER_URL and MCP_ACCESS_TOKEN for the MCP server you configure.
const vault = await orca.vaults.create({ display_name: 'Production secrets' });
const mcpServerURL = process.env['MCP_SERVER_URL'];
const mcpToken = process.env['MCP_ACCESS_TOKEN'];
if (!mcpServerURL || !mcpToken) {
throw new Error('Set MCP_SERVER_URL and MCP_ACCESS_TOKEN.');
}
const credential = await orca.vaults.credentials.create(vault.id, {
display_name: 'Support MCP',
auth: {
type: 'static_bearer',
mcp_server_url: mcpServerURL,
token: mcpToken,
},
});
console.log(credential.id);Memory stores
A memory store is a named, long-lived container you attach to a session through resources[] to mount as a directory the agent reads and writes.
| Method | Signature |
|---|---|
| Create | orca.memoryStores.create(params[, options]) |
| Retrieve | orca.memoryStores.retrieve(memoryStoreId[, options]) |
| Update | orca.memoryStores.update(memoryStoreId, params[, options]) - POST /v1/memory_stores/{memoryStoreId} |
| List | orca.memoryStores.list([{ provider, include_archived, limit, page }[, options]]) |
| Delete | orca.memoryStores.delete(memoryStoreId[, options]) |
| Archive | orca.memoryStores.archive(memoryStoreId[, options]) |
delete resolves to a DeletedMemoryStore tombstone, not void.
const store = await orca.memoryStores.create({
name: 'project-notes',
description: 'Long-lived notes for project X',
});
await orca.memoryStores.update(store.id, { description: 'Updated description' });Memories
Read and write individual entries inside a store. Memory has typed id, path, content hash, size, timestamps, and optional content fields. A list can also contain a MemoryPrefix entry. The request body stays separate from view query controls.
| Method | Signature |
|---|---|
| List | orca.memoryStores.memories.list(memoryStoreId[, { depth, path_prefix, view, limit, page }[, options]]) |
| Create | orca.memoryStores.memories.create(memoryStoreId, { body: { path, content }[, view] }[, options]) |
| Retrieve | orca.memoryStores.memories.retrieve(memoryStoreId, memoryId[, { view }[, options]]) |
| Update | orca.memoryStores.memories.update(memoryStoreId, memoryId, { body[, view] }[, options]) - POST |
| Delete | orca.memoryStores.memories.delete(memoryStoreId, memoryId[, { expected_content_sha256 }[, options]]) |
const memory = await orca.memoryStores.memories.create(store.id, {
body: {
path: '/customers/acme/preferences.md',
content: 'The customer prefers email over phone.',
},
});
for await (const entry of orca.memoryStores.memories.list(store.id)) {
console.log(entry);
}delete resolves to a DeletedMemory tombstone. Pass expected_content_sha256 to make deletion conditional on the entry's current content, so a concurrent write is not silently discarded.
Memory versions
Every write to a memory produces a version. Use these methods to audit that history and to redact a version's content in place.
| Method | Signature |
|---|---|
| List | orca.memoryStores.memoryVersions.list(memoryStoreId[, { memory_id, api_key_id, operation, 'created_at[gte]', 'created_at[lte]', view, limit, page }[, options]]) |
| Retrieve | orca.memoryStores.memoryVersions.retrieve(memoryStoreId, memoryVersionId[, options]) or orca.memoryStores.memoryVersions.retrieve(memoryStoreId, memoryVersionId[, { view }[, options]]) |
| Redact | orca.memoryStores.memoryVersions.redact(memoryStoreId, memoryVersionId[, options]) |
const memoryVersions = await orca.memoryStores.memoryVersions.list(store.id, {
memory_id: memory.id,
});
for (const version of memoryVersions.data) {
console.log(version);
}
const firstVersion = memoryVersions.data[0];
if (firstVersion) {
await orca.memoryStores.memoryVersions.redact(store.id, firstVersion.id);
}redact removes the stored content of a version while leaving the version record in place. It is not reversible.
Agent triggers
An agent trigger runs an agent automatically on a cron schedule or, on StreamNative Cloud, in response to Pulsar or Kafka messages. Triggers are core /v1/triggers operations exposed as orca.triggers on every deployment.
| Method | Signature |
|---|---|
| Create | orca.triggers.create(params[, options]) |
| List | orca.triggers.list([{ agent_id, limit, page }[, options]]) |
| Retrieve | orca.triggers.retrieve(triggerId[, options]) |
| Update | orca.triggers.update(triggerId, params[, options]) - partial POST |
| Delete | orca.triggers.delete(triggerId[, options]) |
| Pause | orca.triggers.pause(triggerId[, options]) |
| Unpause | orca.triggers.unpause(triggerId[, options]) |
| List created sessions | orca.triggers.sessions.list(triggerId[, { include_archived, limit, page }[, options]]) |
The create request uses agent, either as an agent ID string or { type: 'agent', id, version? }. Omitting version pins the agent's current version at create time; it does not track later agent updates.
const trigger = await orca.triggers.create({
name: 'nightly-digest',
agent: { type: 'agent', id: agent.id, version: agent.version },
session_mode: 'SESSION_PER_EVENT',
source: {
type: 'cron',
schedule: '0 2 * * *',
timezone: 'Etc/UTC',
payload: 'Summarize unresolved tickets.',
},
session: { environment_id: environment.id },
});
await orca.triggers.pause(trigger.id);
await orca.triggers.unpause(trigger.id);
for await (const session of orca.triggers.sessions.list(trigger.id)) {
console.log(session.id, session.status);
}Trigger configuration
| Field | Type | Description |
|---|---|---|
name | string | Display name for the trigger. |
agent | string | { type: 'agent', id, version? } | Agent to run. The registry stores the resolved version. |
session_mode | 'SESSION_PER_EVENT' | 'SESSION_PER_TOPIC' | 'SESSION_PER_KEY' | 'SHARED' | How incoming events map onto sessions. |
source | TriggerSourceCreateParams | Cron, Pulsar, or Kafka source. See below. |
session | { environment_id, metadata?, title_template?, vault_ids? } | Template applied to each session the trigger creates. |
replicas | number | Number of trigger workers. |
paused | boolean | Create the trigger without starting it. |
The open-source engine accepts cron with SESSION_PER_EVENT and one replica. StreamNative Cloud widens the same types with Pulsar and Kafka sources, all four messaging session modes, SHARED for cron, and positive replica counts.
For a messaging trigger, set ORCA_KAFKA_CONNECTION_NAME to the name of a configured Cloud connection.
const connectionName = process.env['ORCA_KAFKA_CONNECTION_NAME'];
if (!connectionName) throw new Error('Set ORCA_KAFKA_CONNECTION_NAME.');
await orca.triggers.create({
name: 'ticket-ingest',
agent: { type: 'agent', id: agent.id },
session_mode: 'SESSION_PER_TOPIC',
source: {
type: 'kafka',
connection: connectionName,
topics: ['tickets'],
subscription_name: 'triage-sub',
schema_type: 'json',
},
session: { environment_id: environment.id },
});update accepts only mutable fields and preserves omitted values. It cannot change the pinned agent or set paused; call pause or unpause for lifecycle changes.
Session handle
orca.session(sessionId) returns a SessionHandle that pre-fills the session ID on events, resources, files, and threads calls, so you don't repeat it. It mirrors the methods on orca.sessions.* for a single session, and exposes the ID it was built with as handle.sessionId. handle.events.stream([options]) and handle.events.stream([{ from_cursor, subpath, event_deltas }[, options]]) support the session stream overloads. handle.threads.events.stream(threadId[, options]) and handle.threads.events.stream(threadId[, { from_cursor, event_deltas }[, options]]) do the same for one thread.
handle.sessionId
handle.events list send stream
handle.resources list add retrieve update delete
handle.files list retrieve download delete
handle.threads list retrieve archive
handle.threads.events list streamconst handle = orca.session(session.id);
await handle.events.send({
events: [{ type: 'user.message', content: [{ type: 'text', text: 'Hello' }] }],
});
for await (const event of await handle.events.stream()) {
console.log(event.type);
if (event.type === 'session.status_idle') break;
}
// Files the session produced
for await (const file of handle.files.list()) {
const response = await handle.files.download(file.id);
console.log(file.id, (await response.arrayBuffer()).byteLength);
}
// Per-thread events through the handle
for await (const thread of handle.threads.list()) {
for await (const event of await handle.threads.events.stream(thread.id)) {
console.log(thread.id, event.type);
break;
}
}Streaming
Session and thread events stream over server-sent events (SSE). stream() resolves to a Stream<SessionEvent> that is async-iterable. Iterate with for await and break when the session reaches a terminal state.
const session = await orca.sessions.create({
agent: agent.id,
environment_id: environment.id,
});
await orca.sessions.events.send(session.id, {
events: [{ type: 'user.message', content: [{ type: 'text', text: 'Hello' }] }],
});
const stream = await orca.sessions.events.stream(session.id);
for await (const event of stream) {
if (event.type === 'agent.message') console.log(event);
if (event.type === 'session.status_idle') break;
}A stream can be consumed only once. To process the same stream in two places, split it with .tee(). To stop early and release the connection, call stream.controller.abort() or break out of the loop. The SDK sets Accept: text/event-stream on streaming requests automatically.
Pagination
Core list methods other than files use opaque page tokens. await the call, read .data, and pass next_page as page until it is null.
let page = await orca.agents.list({ limit: 100 });
const all = [...page.data];
while (page.next_page) {
page = await orca.agents.list({ limit: 100, page: page.next_page });
all.push(...page.data);
}File lists use ID cursors instead: orca.files.list and orca.sessions.files.list accept after_id or before_id and return has_more, first_id, and last_id. Automatic iteration preserves direction. For manual iteration, follow last_id with after_id or first_id with before_id.
let filePage = await orca.files.list({ limit: 100 });
const files = [...filePage.data];
while (filePage.hasNextPage()) {
filePage = await filePage.getNextPage();
files.push(...filePage.data);
}Cloud extension list methods are not cursor-based. They resolve directly to plain arrays, objects, or unknown contract responses. Do not read .data or use for await with them.
const providers = await orca.cloud.agents.providers.list();
const apiResources = await orca.cloud.apiResources.list();
console.log(providers.length, apiResources.resources.length);Error handling
Every error the SDK throws extends OrcaError. HTTP errors extend APIError and carry .status, .headers, and .error. Catch specific subclasses to branch on the failure:
import { OrcaError, APIError, NotFoundError, RateLimitError } from '@runorca/orca-sdk';
try {
await orca.agents.retrieve(agent.id);
} catch (err) {
if (err instanceof NotFoundError) {
console.error('Agent not found:', err.status); // 404
} else if (err instanceof RateLimitError) {
console.error('Rate limited; inspect retry headers:', err.headers);
} else if (err instanceof APIError) {
console.error(err.status, err.message);
} else if (err instanceof OrcaError) {
console.error('SDK error:', err.message);
} else {
throw err;
}
}The error hierarchy:
| Class | HTTP status |
|---|---|
BadRequestError | 400 |
AuthenticationError | 401 |
PermissionDeniedError | 403 |
NotFoundError | 404 |
ConflictError | 409 |
UnprocessableEntityError | 422 |
RateLimitError | 429 |
InternalServerError | 5xx |
APIConnectionError | network failure |
APIConnectionTimeoutError | request timed out |
APIUserAbortError | request aborted by the caller |
Retries and timeouts
The client retries transient failures - network errors and 408, 409, 429, and 5xx responses - with exponential backoff and jitter. Tune the defaults on the client, or override per request.
const orca = new Orca({
apiKey: process.env.ORCA_API_KEY,
baseURL: process.env.ORCA_BASE_URL,
maxRetries: 3, // default: 2
timeout: 30_000, // milliseconds; default: 600000 (10 minutes)
});
// Per-request override
await orca.agents.list({}, { maxRetries: 0, timeout: 5_000 });Logging
Pass a logger (any object with debug, info, warn, and error methods) and set logLevel to control verbosity. The default is console at 'warn'. Set it to 'off' to suppress SDK logs. The level can also come from process.env.ORCA_LOG.
const orca = new Orca({
apiKey: process.env.ORCA_API_KEY,
baseURL: process.env.ORCA_BASE_URL,
logger: console,
logLevel: 'info', // 'off' | 'debug' | 'info' | 'warn' | 'error'
});