diff --git a/docs/.i18n/glossary.zh-CN.json b/docs/.i18n/glossary.zh-CN.json
index c03b1b9358f7..32127016fead 100644
--- a/docs/.i18n/glossary.zh-CN.json
+++ b/docs/.i18n/glossary.zh-CN.json
@@ -55,14 +55,6 @@
"source": "Mantis",
"target": "Mantis"
},
- {
- "source": "OpenClaw App SDK",
- "target": "OpenClaw 应用 SDK"
- },
- {
- "source": "OpenClaw App SDK API design",
- "target": "OpenClaw 应用 SDK API 设计"
- },
{
"source": "Message lifecycle refactor",
"target": "消息生命周期重构"
diff --git a/docs/concepts/openclaw-sdk.md b/docs/concepts/openclaw-sdk.md
deleted file mode 100644
index a41ad277a048..000000000000
--- a/docs/concepts/openclaw-sdk.md
+++ /dev/null
@@ -1,323 +0,0 @@
----
-summary: "Public OpenClaw App SDK for external apps, scripts, dashboards, CI jobs, and IDE extensions"
-title: "OpenClaw App SDK"
-sidebarTitle: "App SDK"
-read_when:
- - 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.tasks` | Ready | Lists, reads, and cancels Gateway task ledger entries. |
-| `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.environments` | Partial | Lists Gateway-local and node environment candidates; create/delete are not wired. |
-| `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`,
-`TaskSummary`, `TaskStatus`, `TasksListParams`, `TasksListResult`,
-`TasksGetResult`, `TasksCancelResult`, `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.
-
-```typescript
-import { OpenClaw } from "@openclaw/sdk";
-
-const oc = new OpenClaw({
- url: "ws://127.0.0.1:18789",
- 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`:
-
-```typescript
-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()`.
-
-```typescript
-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.
-
-```typescript
-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:
-
-```typescript
-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:
-
-```typescript
-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:
-
-```typescript
-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:
-
-```typescript
-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.
-
-```typescript
-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:
-
-```typescript
-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:
-
-```typescript
-const approvals = await oc.approvals.list();
-await oc.approvals.respond("approval-id", { decision: "approve" });
-```
-
-Task helpers use the durable task ledger that also backs `openclaw tasks`:
-
-```typescript
-const tasks = await oc.tasks.list({ status: "running", sessionKey: "agent:main:main" });
-const task = await oc.tasks.get(tasks.tasks[0].id);
-await oc.tasks.cancel(task.task.id, { reason: "user stopped task" });
-```
-
-Environment helpers expose read-only Gateway-local and node discovery:
-
-```typescript
-const { environments } = await oc.environments.list();
-await oc.environments.status(environments[0].id);
-```
-
-## 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:
-
-```typescript
-await oc.environments.create({});
-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 vs 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.
-
-## Related
-
-- [OpenClaw App SDK API design](/reference/openclaw-sdk-api-design)
-- [Gateway RPC reference](/reference/rpc)
-- [Agent loop](/concepts/agent-loop)
-- [Agent runtimes](/concepts/agent-runtimes)
-- [Sessions](/concepts/session)
-- [Background tasks](/automation/tasks)
-- [ACP agents](/tools/acp-agents)
-- [Plugin SDK overview](/plugins/sdk-overview)
diff --git a/docs/docs.json b/docs/docs.json
index ce35a56d47cf..e82eeff0e526 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -1741,8 +1741,7 @@
"group": "RPC and API",
"pages": [
"reference/rpc",
- "concepts/openclaw-sdk",
- "reference/openclaw-sdk-api-design",
+ "gateway/external-apps",
"reference/code-mode",
"reference/device-models"
]
diff --git a/docs/gateway/external-apps.md b/docs/gateway/external-apps.md
new file mode 100644
index 000000000000..77685f502652
--- /dev/null
+++ b/docs/gateway/external-apps.md
@@ -0,0 +1,86 @@
+---
+summary: "Current integration path for external apps, scripts, dashboards, CI jobs, and IDE extensions"
+title: "Gateway integrations for external apps"
+sidebarTitle: "External apps"
+read_when:
+ - You are building an external app, script, dashboard, CI job, or IDE extension that talks to OpenClaw
+ - You are choosing between Gateway RPC and the Plugin SDK
+ - You are integrating with Gateway agent runs, sessions, events, approvals, models, or tools
+---
+
+External apps should talk to OpenClaw through the Gateway protocol today. Use
+Gateway WebSocket and RPC methods when a script, dashboard, CI job, IDE
+extension, or another process wants to start agent runs, stream events, wait for
+results, cancel work, or inspect Gateway resources.
+
+
+ There is no public npm client package yet. Do not add OpenClaw client package
+ names as application dependencies until release notes announce a published
+ package and this page includes install instructions.
+
+
+
+ This page is for code outside the OpenClaw process. Plugin code that runs
+ inside OpenClaw should use documented `openclaw/plugin-sdk/*` subpaths instead.
+
+
+## What is available today
+
+| Surface | Status | Use it for |
+| --------------------------------------- | ------ | --------------------------------------------------------------------------------------------- |
+| [Gateway protocol](/gateway/protocol) | Ready | WebSocket transport, connect handshake, auth scopes, protocol versioning, and events. |
+| [Gateway RPC reference](/reference/rpc) | Ready | Current Gateway methods for agents, sessions, tasks, models, tools, artifacts, and approvals. |
+| [`openclaw agent`](/cli/agent) | Ready | One-shot script integration when shelling out to the CLI is enough. |
+| [`openclaw message`](/cli/message) | Ready | Sending messages or channel actions from scripts. |
+
+The source tree contains internal package work for a future client library, but
+that is not a public install surface. Treat it as preview implementation detail
+until the packages are published and versioned.
+
+## Recommended path
+
+1. Run or discover a Gateway.
+2. Connect over the [Gateway protocol](/gateway/protocol).
+3. Call documented RPC methods from [Gateway RPC reference](/reference/rpc).
+4. Pin the OpenClaw version you test against.
+5. Recheck the RPC reference when upgrading OpenClaw.
+
+For agent runs, start with the `agent` RPC and pair it with `agent.wait` when
+you need a terminal result. For durable conversation state, use the `sessions.*`
+methods. For UI integrations, subscribe to Gateway events and render only the
+event families your app understands.
+
+## App code vs plugin code
+
+Use Gateway RPC 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
+
+External apps should not import `openclaw/plugin-sdk/*`; those subpaths are for
+plugins loaded by OpenClaw.
+
+## Related
+
+- [Gateway protocol](/gateway/protocol)
+- [Gateway RPC reference](/reference/rpc)
+- [CLI agent command](/cli/agent)
+- [CLI message command](/cli/message)
+- [Agent loop](/concepts/agent-loop)
+- [Agent runtimes](/concepts/agent-runtimes)
+- [Sessions](/concepts/session)
+- [Background tasks](/automation/tasks)
+- [ACP agents](/tools/acp-agents)
+- [Plugin SDK overview](/plugins/sdk-overview)
diff --git a/docs/maturity-scores.yaml b/docs/maturity-scores.yaml
index 0beb1afd378e..3092ed3d1add 100644
--- a/docs/maturity-scores.yaml
+++ b/docs/maturity-scores.yaml
@@ -1803,8 +1803,8 @@ surfaces:
supported: false
reason: none
human_override: false
-- id: openclaw-app-sdk
- name: OpenClaw App SDK
+- id: gateway-external-apps
+ name: Gateway integrations for external apps
family: core
level:
id: alpha
@@ -1831,7 +1831,7 @@ surfaces:
source_ref: openclaw@29dd7847fd
process_version: 3
categories:
- - name: Client API
+ - name: Gateway API
coverage:
score: 86
label: Stable
diff --git a/docs/plugins/sdk-overview.md b/docs/plugins/sdk-overview.md
index 3131c33d584f..5c69e00ce77c 100644
--- a/docs/plugins/sdk-overview.md
+++ b/docs/plugins/sdk-overview.md
@@ -14,9 +14,8 @@ reference for **what to import** and **what you can register**.
This page is for plugin authors using `openclaw/plugin-sdk/*` inside
OpenClaw. For external apps, scripts, dashboards, CI jobs, and IDE extensions
- that want to run agents through the Gateway, use the
- [OpenClaw App SDK](/concepts/openclaw-sdk) and the `@openclaw/sdk` package
- instead.
+ that want to run agents through the Gateway, use
+ [Gateway integrations for external apps](/gateway/external-apps) instead.
diff --git a/docs/reference/openclaw-sdk-api-design.md b/docs/reference/openclaw-sdk-api-design.md
deleted file mode 100644
index 96ef4dd40a7b..000000000000
--- a/docs/reference/openclaw-sdk-api-design.md
+++ /dev/null
@@ -1,390 +0,0 @@
----
-summary: "Reference design for the public OpenClaw App SDK API, event taxonomy, artifacts, approvals, and package structure"
-title: "OpenClaw App SDK API design"
-sidebarTitle: "App SDK API design"
-read_when:
- - You are implementing the proposed public OpenClaw app SDK
- - You need the draft namespace, event, result, artifact, approval, or security contract for the app SDK
- - You are comparing Gateway protocol resources with the high-level OpenClaw App SDK wrapper
----
-
-This page is the detailed API reference design for the public
-[OpenClaw App SDK](/concepts/openclaw-sdk). It is intentionally separate from
-the [Plugin SDK](/plugins/sdk-overview).
-
-
- `@openclaw/sdk` is the external app/client package for talking to the
- Gateway. `openclaw/plugin-sdk/*` is the in-process plugin authoring contract.
- Do not import Plugin SDK subpaths from apps that only need to run agents.
-
-
-The public app SDK should be built in two layers:
-
-1. A low-level generated Gateway client.
-2. A high-level ergonomic wrapper with `OpenClaw`, `Agent`, `Session`, `Run`,
- `Task`, `Artifact`, `Approval`, and `Environment` objects.
-
-## Namespace design
-
-The low-level namespaces should closely follow Gateway resources:
-
-```typescript
-oc.agents.list();
-oc.agents.get("main");
-oc.agents.create(...);
-oc.agents.update(...);
-
-oc.sessions.list();
-oc.sessions.create(...);
-oc.sessions.resolve(...);
-oc.sessions.send(...);
-oc.sessions.messages(...);
-oc.sessions.fork(...);
-oc.sessions.compact(...);
-oc.sessions.abort(...);
-
-oc.runs.create(...);
-oc.runs.get(runId);
-oc.runs.events(runId, { after });
-oc.runs.wait(runId);
-oc.runs.cancel(runId);
-
-oc.tasks.list({ status: "running" });
-oc.tasks.get(taskId);
-oc.tasks.cancel(taskId, { reason });
-oc.tasks.events(taskId, { after }); // future API
-
-oc.models.list();
-oc.models.status(); // Gateway models.authStatus
-
-oc.tools.list();
-oc.tools.invoke("tool-name", { sessionKey, idempotencyKey });
-
-oc.artifacts.list({ runId });
-oc.artifacts.get(artifactId, { runId });
-oc.artifacts.download(artifactId, { runId });
-
-oc.approvals.list();
-oc.approvals.respond(approvalId, ...);
-
-oc.environments.list();
-oc.environments.create(...); // future API: current SDK throws unsupported
-oc.environments.status(environmentId);
-oc.environments.delete(environmentId); // future API: current SDK throws unsupported
-```
-
-High-level wrappers should return objects that make common flows pleasant:
-
-```typescript
-const run = await agent.run(inputOrParams);
-await run.cancel();
-await run.wait();
-
-for await (const event of run.events()) {
- // normalized event stream
-}
-
-const artifacts = await run.artifacts.list();
-const session = await run.session();
-```
-
-## Event contract
-
-The public SDK should expose versioned, replayable, normalized events.
-
-```typescript
-type OpenClawEvent = {
- version: 1;
- id: string;
- ts: number;
- type: OpenClawEventType;
- runId?: string;
- sessionId?: string;
- sessionKey?: string;
- taskId?: string;
- agentId?: string;
- data: unknown;
- raw?: unknown;
-};
-```
-
-`id` is a replay cursor. Consumers should be able to reconnect with
-`events({ after: id })` and receive missed events when retention allows.
-
-Recommended normalized event families:
-
-| Event | Meaning |
-| --------------------- | ----------------------------------------------------------- |
-| `run.created` | Run accepted. |
-| `run.queued` | Run is waiting for a session lane, runtime, or environment. |
-| `run.started` | Runtime started execution. |
-| `run.completed` | Run finished successfully. |
-| `run.failed` | Run ended with an error. |
-| `run.cancelled` | Run was cancelled. |
-| `run.timed_out` | Run exceeded its timeout. |
-| `assistant.delta` | Assistant text delta. |
-| `assistant.message` | Complete assistant message or replacement. |
-| `thinking.delta` | Reasoning or plan delta, when policy allows exposure. |
-| `tool.call.started` | Tool call began. |
-| `tool.call.delta` | Tool call streamed progress or partial output. |
-| `tool.call.completed` | Tool call returned successfully. |
-| `tool.call.failed` | Tool call failed. |
-| `approval.requested` | A run or tool needs approval. |
-| `approval.resolved` | Approval was granted, denied, expired, or cancelled. |
-| `question.requested` | Runtime asks the user or host app for input. |
-| `question.answered` | Host app supplied an answer. |
-| `artifact.created` | New artifact available. |
-| `artifact.updated` | Existing artifact changed. |
-| `session.created` | Session created. |
-| `session.updated` | Session metadata changed. |
-| `session.compacted` | Session compaction happened. |
-| `task.updated` | Background task state changed. |
-| `git.branch` | Runtime observed or changed branch state. |
-| `git.diff` | Runtime produced or changed a diff. |
-| `git.pr` | Runtime opened, updated, or linked a pull request. |
-
-Runtime-native payloads should be available through `raw`, but apps should not
-have to parse `raw` for normal UI.
-
-## Result contract
-
-`Run.wait()` should return a stable result envelope:
-
-```typescript
-type RunResult = {
- runId: string;
- status: "accepted" | "completed" | "failed" | "cancelled" | "timed_out";
- sessionId?: string;
- sessionKey?: string;
- taskId?: string;
- startedAt?: string | number;
- endedAt?: string | number;
- output?: {
- text?: string;
- messages?: SDKMessage[];
- };
- usage?: {
- inputTokens?: number;
- outputTokens?: number;
- totalTokens?: number;
- costUsd?: number;
- };
- artifacts?: ArtifactSummary[];
- error?: SDKError;
-};
-```
-
-The result should be boring and stable. Timestamp values preserve the Gateway
-shape, so current lifecycle-backed runs usually report epoch millisecond
-numbers while adapters may still surface ISO strings. Rich UI, tool traces, and
-runtime-native details belong in events and artifacts.
-
-`accepted` is a non-terminal wait result: it means the Gateway wait deadline
-expired before the run produced a lifecycle end/error. It must not be treated as
-`timed_out`; `timed_out` is reserved for a run that exceeded its own runtime
-timeout.
-
-## Approvals and questions
-
-Approvals must be first-class because coding agents constantly cross safety
-boundaries.
-
-```typescript
-run.onApproval(async (request) => {
- if (request.kind === "tool" && request.toolName === "exec") {
- return request.approveOnce({ reason: "CI command allowed by policy" });
- }
-
- return request.askUser();
-});
-```
-
-Approval events should carry:
-
-- approval id
-- run id and session id
-- request kind
-- requested action summary
-- tool name or environment action
-- risk level
-- available decisions
-- expiration
-- whether the decision can be reused
-
-Questions are separate from approvals. A question asks the user or host app for
-information. An approval asks for permission to perform an action.
-
-## ToolSpace model
-
-Apps need to understand the tool surface without importing plugin internals.
-
-```typescript
-const tools = await run.toolSpace();
-
-for (const tool of tools.list()) {
- console.log(tool.name, tool.source, tool.requiresApproval);
-}
-```
-
-The SDK should expose:
-
-- normalized tool metadata
-- source: OpenClaw, MCP, plugin, channel, runtime, or app
-- schema summary
-- approval policy
-- runtime compatibility
-- whether a tool is hidden, readonly, write capable, or host capable
-
-Tool invocation through the SDK should be explicit and scoped. Most apps should
-run agents, not call arbitrary tools directly.
-
-## Artifact model
-
-Artifacts should cover more than files.
-
-```typescript
-type ArtifactSummary = {
- id: string;
- runId?: string;
- sessionId?: string;
- type:
- | "file"
- | "patch"
- | "diff"
- | "log"
- | "media"
- | "screenshot"
- | "trajectory"
- | "pull_request"
- | "workspace";
- title?: string;
- mimeType?: string;
- sizeBytes?: number;
- createdAt: string;
- expiresAt?: string;
-};
-```
-
-Common examples:
-
-- file edits and generated files
-- patch bundles
-- VCS diffs
-- screenshots and media outputs
-- logs and trace bundles
-- pull request links
-- runtime trajectories
-- managed environment workspace snapshots
-
-Artifact access should support redaction, retention, and download URLs without
-assuming every artifact is a normal local file.
-
-## Security model
-
-The app SDK must be explicit about authority.
-
-Recommended token scopes:
-
-| Scope | Allows |
-| ------------------- | --------------------------------------------------- |
-| `agent.read` | List and inspect agents. |
-| `agent.run` | Start runs. |
-| `session.read` | Read session metadata and messages. |
-| `session.write` | Create, send to, fork, compact, and abort sessions. |
-| `task.read` | Read background task state. |
-| `task.write` | Cancel or modify task notification policy. |
-| `approval.respond` | Approve or deny requests. |
-| `tools.invoke` | Invoke exposed tools directly. |
-| `artifacts.read` | List and download artifacts. |
-| `environment.write` | Create or destroy managed environments. |
-| `admin` | Administrative operations. |
-
-Defaults:
-
-- no secret forwarding by default
-- no unrestricted environment variable pass-through
-- secret references instead of secret values
-- explicit sandbox and network policy
-- explicit remote environment retention
-- approvals for host execution unless policy proves otherwise
-- raw runtime events redacted before they leave Gateway unless the caller has a
- stronger diagnostic scope
-
-## Managed environment provider
-
-Managed agents should be implemented as environment providers.
-
-```typescript
-type EnvironmentProvider = {
- id: string;
- capabilities: {
- checkout?: boolean;
- sandbox?: boolean;
- networkPolicy?: boolean;
- secrets?: boolean;
- artifacts?: boolean;
- logs?: boolean;
- pullRequests?: boolean;
- longRunning?: boolean;
- };
-};
-```
-
-The first implementation does not need to be a hosted SaaS. It can target
-existing node hosts, ephemeral workspaces, CI-style runners, or Testbox-style
-environments. The important contract is:
-
-1. prepare workspace
-2. bind safe environment and secrets
-3. start run
-4. stream events
-5. collect artifacts
-6. clean up or retain by policy
-
-Once this is stable, a hosted cloud service can implement the same provider
-contract.
-
-## Package structure
-
-Recommended packages:
-
-| Package | Purpose |
-| ----------------------- | ------------------------------------------------------------- |
-| `@openclaw/sdk` | Public high-level SDK and generated low-level Gateway client. |
-| `@openclaw/sdk-react` | Optional React hooks for dashboards and app builders. |
-| `@openclaw/sdk-testing` | Test helpers and fake Gateway server for app integrations. |
-
-The repo already has `openclaw/plugin-sdk/*` for plugins. Keep that namespace
-separate to avoid confusing plugin authors with app developers.
-
-## Generated client strategy
-
-The low-level client should be generated from versioned Gateway protocol
-schemas, then wrapped by handwritten ergonomic classes.
-
-Layering:
-
-1. Gateway schema source of truth.
-2. Generated low-level TypeScript client.
-3. Runtime validators for external inputs and event payloads.
-4. High-level `OpenClaw`, `Agent`, `Session`, `Run`, `Task`, and `Artifact`
- wrappers.
-5. Cookbook examples and integration tests.
-
-Benefits:
-
-- protocol drift is visible
-- tests can compare generated methods with Gateway exports
-- App SDK stays independent from Plugin SDK internals
-- low-level consumers still have full protocol access
-- high-level consumers get the small product API
-
-## Related
-
-- [OpenClaw App SDK](/concepts/openclaw-sdk)
-- [Gateway RPC reference](/reference/rpc)
-- [Agent loop](/concepts/agent-loop)
-- [Agent runtimes](/concepts/agent-runtimes)
-- [Background tasks](/automation/tasks)
-- [ACP agents](/tools/acp-agents)
-- [Plugin SDK overview](/plugins/sdk-overview)