Files
openclaw/docs/concepts/openclaw-sdk.md
NVIDIAN ef0eb12615 feat(gateway): add SDK-facing tools.invoke RPC
Adds the SDK-facing tools.invoke Gateway RPC for #74705.

Reuses the /tools/invoke policy path for tool policy, deny-list, owner filtering, before-tool-call hooks, session/agent scoping, and plugin approval handling. Returns typed SDK approval/refusal/success results while preserving HTTP compatibility and uses idempotencyKey as the stable tool-call id.

Includes protocol schema exports, method scope/list registration, SDK helper/types, docs, generated Swift models, tests, and changelog credit.
2026-05-01 03:16:53 -05:00

11 KiB

summary, title, sidebarTitle, read_when
summary title sidebarTitle read_when
Public OpenClaw App SDK for external apps, scripts, dashboards, CI jobs, and IDE extensions OpenClaw App SDK App SDK
You are building an external app, script, dashboard, CI job, or IDE extension that talks to OpenClaw
You are choosing between the App SDK and the Plugin SDK
You are integrating with Gateway agent runs, sessions, events, approvals, models, or tools

The OpenClaw App SDK is the public client API for apps outside the OpenClaw process. Use @openclaw/sdk when a script, dashboard, CI job, IDE extension, or other external app wants to connect to the Gateway, start agent runs, stream events, wait for results, cancel work, or inspect Gateway resources.

The App SDK is different from the [Plugin SDK](/plugins/sdk-overview). `@openclaw/sdk` talks to the Gateway from outside OpenClaw. `openclaw/plugin-sdk/*` is only for plugins that run inside OpenClaw and register providers, channels, tools, hooks, or trusted runtimes.

What Ships Today

@openclaw/sdk ships with:

Surface Status What it does
OpenClaw Ready Main client entry point. Owns transport, connection, requests, and events.
GatewayClientTransport Ready WebSocket transport backed by the Gateway client.
oc.agents Ready Lists, creates, updates, deletes, and gets agent handles.
Agent.run() Ready Starts a Gateway agent run and returns a Run.
oc.runs Ready Creates, gets, waits for, cancels, and streams runs.
Run.events() Ready Streams normalized per-run events with replay for fast runs.
Run.wait() Ready Calls agent.wait and returns a stable RunResult.
Run.cancel() Ready Calls sessions.abort by run id, with session key when available.
oc.sessions Ready Creates, resolves, sends to, patches, compacts, and gets session handles.
Session.send() Ready Calls sessions.send and returns a Run.
oc.models Ready Calls models.list and the current models.authStatus status RPC.
oc.tools Ready Lists, scopes, and invokes Gateway tools through the policy pipeline.
oc.artifacts Ready Lists, gets, and downloads Gateway transcript artifacts.
oc.approvals Ready Lists and resolves exec approvals through Gateway approval RPCs.
oc.rawEvents() Ready Exposes raw Gateway events for advanced consumers.
normalizeGatewayEvent() Ready Converts raw Gateway events into the stable SDK event shape.

The SDK also exports the core types used by those surfaces: AgentRunParams, RunResult, RunStatus, OpenClawEvent, OpenClawEventType, GatewayEvent, OpenClawTransport, GatewayRequestOptions, SessionCreateParams, SessionSendParams, ArtifactSummary, ArtifactQuery, ArtifactsListResult, ArtifactsGetResult, ArtifactsDownloadResult, RuntimeSelection, EnvironmentSelection, WorkspaceSelection, ApprovalMode, and related result types.

Connect To A Gateway

Create a client with an explicit Gateway URL, or inject a custom transport for tests and embedded app runtimes.

import { OpenClaw } from "@openclaw/sdk";

const oc = new OpenClaw({
  url: "ws://127.0.0.1:14565",
  token: process.env.OPENCLAW_GATEWAY_TOKEN,
  requestTimeoutMs: 30_000,
});

await oc.connect();

new OpenClaw({ gateway: "ws://..." }) is equivalent to url. The gateway: "auto" option is accepted by the constructor, but automatic Gateway discovery is not a separate SDK feature yet; pass url when the app does not already know how to discover the Gateway.

For tests, pass an object that implements OpenClawTransport:

const oc = new OpenClaw({
  transport: {
    async request(method, params) {
      return { method, params };
    },
    async *events() {},
  },
});

Run An Agent

Use oc.agents.get(id) when the app wants an agent handle, then call agent.run().

const agent = await oc.agents.get("main");

const run = await agent.run({
  input: "Review this pull request and suggest the smallest safe fix.",
  model: "openai/gpt-5.5",
  sessionKey: "main",
  timeoutMs: 30_000,
});

for await (const event of run.events()) {
  const data = event.data as { delta?: unknown };
  if (event.type === "assistant.delta" && typeof data.delta === "string") {
    process.stdout.write(data.delta);
  }
}

const result = await run.wait({ timeoutMs: 120_000 });
console.log(result.status);

Provider-qualified model refs such as openai/gpt-5.5 are split into Gateway provider and model overrides. timeoutMs stays milliseconds in the SDK and is converted to Gateway timeout seconds for the agent RPC.

run.wait() uses the Gateway agent.wait RPC. A wait deadline that expires while the run is still active returns status: "accepted" instead of pretending the run itself timed out. Runtime timeouts, aborted runs, and cancelled runs are normalized into timed_out or cancelled.

Create And Reuse Sessions

Use sessions when the app wants durable transcript state.

const session = await oc.sessions.create({
  agentId: "main",
  label: "release-review",
});

const run = await session.send("Prepare release notes from the current diff.");
await run.wait();

Session.send() calls sessions.send and returns a Run. Session handles also support:

await session.abort(run.id);
await session.patch({ label: "renamed-session" });
await session.compact({ maxLines: 200 });

Stream Events

The SDK normalizes raw Gateway events into a stable OpenClawEvent envelope:

type OpenClawEvent = {
  version: 1;
  id: string;
  ts: number;
  type: OpenClawEventType;
  runId?: string;
  sessionId?: string;
  sessionKey?: string;
  taskId?: string;
  agentId?: string;
  data: unknown;
  raw?: GatewayEvent;
};

Common event types include:

Event type Source Gateway event
run.started agent lifecycle start
run.completed agent lifecycle end
run.failed agent lifecycle error
run.cancelled Aborted/cancelled lifecycle end
run.timed_out Timeout lifecycle end
assistant.delta Assistant streaming delta
assistant.message Assistant message
thinking.delta Thinking or plan stream
tool.call.started Tool/item/command start
tool.call.delta Tool/item/command update
tool.call.completed Tool/item/command completion
tool.call.failed Tool/item/command failure or blocked status
approval.requested Exec or plugin approval request
approval.resolved Exec or plugin approval resolution
session.created sessions.changed create
session.updated sessions.changed update
session.compacted sessions.changed compaction
task.updated Task update events
artifact.updated Patch stream events
raw Any event without a stable SDK mapping yet

Run.events() filters events to one run id and replays already-seen events for fast runs. That means the documented flow is safe:

const run = await agent.run("Summarize the latest session.");

for await (const event of run.events()) {
  if (event.type === "run.completed") {
    break;
  }
}

For app-wide streams, use oc.events(). For raw Gateway frames, use oc.rawEvents().

Models, Tools, Artifacts, And Approvals

Model helpers map to current Gateway methods:

await oc.models.list();
await oc.models.status({ probe: false }); // calls models.authStatus

Tool helpers expose the Gateway catalog, effective tool view, and direct Gateway tool invocation. oc.tools.invoke() returns a typed envelope instead of throwing for policy or approval refusals.

await oc.tools.list();
await oc.tools.effective({ sessionKey: "main" });
await oc.tools.invoke("tool-name", {
  args: { input: "value" },
  sessionKey: "main",
  confirm: false,
  idempotencyKey: "tool-call-1",
});

Artifact helpers expose the Gateway artifact projection for session, run, or task context. Each call requires one explicit sessionKey, runId, or taskId scope:

const { artifacts } = await oc.artifacts.list({ sessionKey: "main" });
const first = artifacts[0];

if (first) {
  const { artifact } = await oc.artifacts.get(first.id, { sessionKey: "main" });
  const download = await oc.artifacts.download(artifact.id, { sessionKey: "main" });
  console.log(download.encoding, download.url);
}

Approval helpers use the exec approval RPCs:

const approvals = await oc.approvals.list();
await oc.approvals.respond("approval-id", { decision: "approve" });

Explicitly Unsupported Today

The SDK includes names for the product model we want, but it does not silently pretend Gateway RPCs exist. These calls currently throw explicit unsupported errors:

await oc.tasks.list();
await oc.tasks.get("task-id");
await oc.tasks.cancel("task-id");

await oc.environments.list();
await oc.environments.create({});
await oc.environments.status("environment-id");
await oc.environments.delete("environment-id");

Per-run workspace, runtime, environment, and approvals fields are typed as future shape, but the current Gateway does not support those overrides on the agent RPC. If callers pass them, the SDK throws before submitting the run so work does not accidentally execute with default workspace, runtime, environment, or approval behavior.

App SDK Versus Plugin SDK

Use the App SDK when code lives outside OpenClaw:

  • Node scripts that start or observe agent runs
  • CI jobs that call a Gateway
  • dashboards and admin panels
  • IDE extensions
  • external bridges that do not need to become channel plugins
  • integration tests with fake or real Gateway transports

Use the Plugin SDK when code runs inside OpenClaw:

  • provider plugins
  • channel plugins
  • tool or lifecycle hooks
  • agent harness plugins
  • trusted runtime helpers

App SDK code should import from @openclaw/sdk. Plugin code should import from documented openclaw/plugin-sdk/* subpaths. Do not mix the two contracts.