diff --git a/.github/assets/architecture.svg b/.github/assets/architecture.svg new file mode 100644 index 0000000..2e00cf0 --- /dev/null +++ b/.github/assets/architecture.svg @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + How It Works + + + + YOUR APPLICATION + + + + + Credentials + API keys, env vars + + + + Persistence + Session files, logs + + + + Hosted Tools + Your custom logic + + + + Hooks + 26 lifecycle points + + + + MCP Servers + stdio + HTTP + + + + + + + + createSession() + + + + GENERAL AGENT SDK + + + + + Session Factory + create · resume · fork + list · reset · history + + + + + + + + + + + Agent Execution Loop + + + + + User + + ’ + + + LLM + + ’ + + + Tool + + ’ + + + LLM + + ’ + + +  + + + + + + + Event Stream + assistant_delta · tool_call + hosted_tool_call · turn_complete + + + + TOOLS + + + + + + read + files + + +  + write + create + + + =' + edit + surgical + + + + exec + shell + + + = + web_search + search + + + < + web_fetch + pages + + + > + subagents + delegate + + + + apply_patch + diff + + + + RUNTIME + + + + Context Compaction + Auto-manage context window + + + + File Checkpoints + Auto-rollback on mutations + + + + Hook Runner + 19 SDK-native + 7 host-bridged + + + + LLM Providers + Claude · GPT · Gemini + + + + You own the boundary. The SDK owns the execution. + + diff --git a/.github/assets/event-flow.svg b/.github/assets/event-flow.svg new file mode 100644 index 0000000..1e6297b --- /dev/null +++ b/.github/assets/event-flow.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Event Stream Timeline + + + + + + + + + U + + streamTurn() + user message + + + + + + + reasoning + _delta + + + + +  + + assistant + _delta + + + + + =' + + tool_call + built-in or hosted + + + + +  + + tool_result + output returned + + + + loops until done + + + + + + + usage + _snapshot + + + + + + + turn + _complete + + + + + + + + + + + + + for await (const event of session.streamTurn(input)) { ... } + All events delivered via a single AsyncIterable  no callbacks, no observers + + diff --git a/.github/assets/features.svg b/.github/assets/features.svg new file mode 100644 index 0000000..87eee14 --- /dev/null +++ b/.github/assets/features.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + Real-time Streaming + Every token, tool call, result as events + + + + =' + 8 Built-in Tools + read · write · exec · web + more + + + + <― + Hosted Tools + Your code, suspend/resume safe + + + + + Multi-turn Memory + Automatic conversation history + + + + + + 26 Lifecycle Hooks + Intercept model, tools, sessions + + + + = + MCP Integration + stdio + HTTP, hot-pluggable + + + + > + Subagent Runtime + Scoped child agents with isolation + + + + =α + Production Ready + Compaction, checkpoints, restart-safe + + diff --git a/.github/assets/hero-banner.svg b/.github/assets/hero-banner.svg new file mode 100644 index 0000000..ca27a1e --- /dev/null +++ b/.github/assets/hero-banner.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + General Agent SDK + + + Build AI agents that actually do things. + + + + + + + + + Streaming + + + Tools + + + Sessions + + + Hooks + + + MCP + + + Subagents + + + + + + v0.1.0 · MIT + + + diff --git a/.github/assets/quick-start-header.svg b/.github/assets/quick-start-header.svg new file mode 100644 index 0000000..1503345 --- /dev/null +++ b/.github/assets/quick-start-header.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + ‘ Get Started in 60 Seconds + + diff --git a/.github/assets/why-different.svg b/.github/assets/why-different.svg new file mode 100644 index 0000000..e33741e --- /dev/null +++ b/.github/assets/why-different.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + VS + + + Other Agent Frameworks + + + + +  + Chat completion wrappers + + + +  + Framework-owned state you can't control + + + +  + Manual tool loop management + + + +  + Restart = lost state + + + +  + Callback-heavy APIs, hard to reason about + + + + + General Agent SDK + + + + +  + Full autonomous execution engine + + + +  + Host-owned persistence & credentials + + + +  + Agent runs tools autonomously until done + + + +  + Restart-safe hosted tool suspend/resume + + + +  + Single for-await loop, zero callbacks + + + + diff --git a/README.md b/README.md index e8cb483..a6fdf07 100644 --- a/README.md +++ b/README.md @@ -1,449 +1,520 @@ -# General Agent SDK +
-`general-agent-sdk` is a session-first embedded SDK that extracts the agent execution kernel from OpenClaw and exposes it as a host-controlled TypeScript package. + + + + General Agent SDK + -The SDK is intentionally narrow: it preserves execution-layer semantics such as tool calls, hosted-tool suspend/resume, compaction, plugin policy, and provider-specific streaming, while leaving orchestration, channel routing, profile ownership, and canonical session state to the host application. +
-## Status +[![npm](https://img.shields.io/npm/v/general-agent-sdk?style=for-the-badge&logo=npm&logoColor=white&color=CB3837&label=)](https://www.npmjs.com/package/general-agent-sdk) +  +[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178c6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) +  +[![License](https://img.shields.io/badge/MIT-license-blue?style=for-the-badge)](./LICENSE) +  +[![Tests](https://img.shields.io/badge/133-tests_passing-22c55e?style=for-the-badge&logo=vitest&logoColor=white)](./tests) +  +[![Node](https://img.shields.io/badge/Node-%E2%89%A522-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)](https://nodejs.org/) -- Repository: `https://github.com/babelcloud/general-agent-sdk` -- Package name: `general-agent-sdk` -- Current package version: `0.1.0` -- Runtime: Node.js `>=22.14.0` -- Module format: ESM -- CI workflow: [`.github/workflows/sdk-ci.yml`](./.github/workflows/sdk-ci.yml) +
-This repository is currently host-oriented and private by default. It is designed to be consumed as a pinned dependency or submodule by a parent host application. +**The TypeScript SDK for building AI agents that call tools, manage sessions, and ship to production.** -## Breaking Changes +[Quick Start](#-get-started-in-60-seconds) Β· [Architecture](#-architecture) Β· [API Reference](./SDK%20DOCS/API-REFERENCE.md) Β· [Examples](./SDK%20DOCS/) -The current General Agent SDK surface intentionally removes earlier transitional names and compatibility entrypoints. +
-- Root factory/type names are now `createGeneralAgentSdk`, `GeneralAgentSdk`, `GeneralAgentSdkOptions`, and `GeneralAgentSession`. -- The package no longer ships the `./compat/visionclaw` entrypoint. -- The package no longer ships the `./plugin-sdk` alias. +
-If you are upgrading from an earlier internal prototype, update root imports and switch any host integration that depended on removed subpaths to the native SDK session/event APIs. + -## What This SDK Is +## Why General Agent SDK? -- A standalone embedded agent kernel extracted from OpenClaw -- A session factory plus session objects -- A bridge that preserves structured execution semantics instead of flattening everything into text -- A host-integrated runtime that writes all state and logs under host-owned roots +Most agent frameworks give you **wrappers around chat completions** β€” you manage the tool loop, you track the state, you handle restarts. General Agent SDK gives you a **complete execution kernel**: -## What This SDK Is Not +
+
-- Not the full OpenClaw gateway -- Not a replacement for a host application's outer runtime loop -- Not a second authoritative session registry -- Not a channel manager, cron daemon, control plane, or desktop automation environment +Comparison -## Design Principles +

+
-- **Session-first API**: the host bootstraps the SDK once, then creates and reuses sessions explicitly -- **Host-owned persistence**: the host decides where session files, state files, and raw event logs live -- **Execution fidelity**: tool-call identity, hosted-tool resume boundaries, and tool-result ordering are preserved -- **Failure containment**: the SDK stays behind an engine-gated loader path in the host -- **Traceability**: extracted upstream files are tracked through a provenance manifest and verification scripts +> **The SDK runs the agent loop for you.** You send a message, the agent autonomously calls tools, reasons about results, calls more tools, and streams every event back β€” until it's done or needs your input. -## Host / SDK Boundary +
-### SDK responsibilities + -- Create and reuse agent sessions -- Stream assistant, reasoning, tool, hosted-tool, compaction, and usage events -- Preserve `tool_call`, `tool_result`, and `tool_error` semantics -- Resolve embedded provider/auth/plugin/tool behavior -- Start and stop registered stdio MCP runtimes for active runs -- Emit canonical host-facing logs and optional raw stream events +
-### Host responsibilities +Features -- Profile roots and workspace roots -- Credentials and environment variables -- Canonical session metadata -- Channel ingress and egress -- Cross-engine continuity and owner-facing orchestration -- Which MCP servers are registered and enabled for a session +
-This separation is intentional. The SDK does not introduce a new top-level runtime abstraction above the host. +
-## Public API + -The supported API surface is exported from [`src/index.ts`](./src/index.ts) and backed by the files under [`src/public/`](./src/public). +
-### Factory +Quick Start -```ts -import { createGeneralAgentSdk } from "general-agent-sdk"; +
-const sdk = await createGeneralAgentSdk({ - workspaceDir, - stateDir, - agentDir, - profileId: "default", - pluginMode: "disabled", - logger, - sessionStore, - hostedTools, - env: process.env, - tools: { - web: { - fetch: { - firecrawl: { - apiKey: process.env.FIRECRAWL_API_KEY, - }, - }, - search: { - apiKey: process.env.BRAVE_SEARCH_API_KEY, - }, - }, - }, -}); +### Step 1 β€” Install + +```bash +npm install general-agent-sdk ``` -### Session creation +### Step 2 β€” Set your API key -```ts -const session = sdk.createSession({ - identity: { - mode: "general", - sessionId: "sess-general", - sessionKey: "host:default:general", - }, - systemPrompt: "Use the finish tool immediately.", - modelRef: "openai/gpt-5.4", - sessionFile, - authProfileId: "enterprise-default", - rawEventLogPath, -}); +```bash +export ANTHROPIC_API_KEY="sk-ant-..." + +# Optional: use a custom endpoint +# export ANTHROPIC_BASE_URL="https://your-proxy.example.com" ``` -### Session lifecycle +### Step 3 β€” Run your first agent -The SDK can enumerate stored sessions, reopen them by `sessionId`, continue a known identity, fork a stored transcript into a new session, and read persisted transcript history. +```typescript +import { createGeneralAgentSdk } from "general-agent-sdk"; +import { randomUUID } from "node:crypto"; +import path from "node:path"; +import os from "node:os"; -```ts -const sessions = await sdk.listSessions(); -const resumed = await sdk.resumeSession("sess-general"); -const continued = await sdk.continueSession({ - identity: { - mode: "general", - sessionId: "sess-general", - sessionKey: "host:default:general", - }, -}); -const forked = await sdk.forkSession("sess-general", { - identity: { - mode: "general", - sessionId: "sess-general-fork", - sessionKey: "host:default:general-fork", +// 1. Initialize +const sdk = await createGeneralAgentSdk({ + workspaceDir: process.cwd(), + stateDir: path.join(process.cwd(), ".agent-state"), + agentDir: path.join(process.cwd(), ".agent"), + profileId: "default", + pluginMode: "disabled", + logger: { onDebug() {}, onInfo() {}, onWarn() {}, onError() {} }, + sessionStore: { + async load() { return null; }, + async save() {}, + async resolveSessionFile(id) { + return path.join(os.tmpdir(), `${id.sessionId}.jsonl`); + }, }, - sessionFile: forkSessionFile, }); -const history = await sdk.readSessionHistory("sess-general"); -``` -### Turn streaming +// 2. Create a session +const session = sdk.createSession({ + identity: { mode: "general", sessionId: randomUUID(), sessionKey: "demo" }, + systemPrompt: "You are a helpful assistant. Use tools when needed.", + modelRef: "claude-sonnet-4-20250514", + sessionFile: path.join(os.tmpdir(), "demo-session.jsonl"), +}); -```ts +// 3. Stream a turn β€” the agent calls tools autonomously for await (const event of session.streamTurn({ role: "user", - content: [{ type: "text", text: "finish now" }], + content: [{ type: "text", text: "List the files in the current directory" }], })) { - // host consumes normalized GeneralAgentStreamEvent values + switch (event.kind) { + case "assistant_delta": process.stdout.write(event.text); break; + case "tool_call": console.log(`\nπŸ”§ ${event.toolName}`); break; + case "tool_result": console.log(` βœ… done`); break; + case "turn_complete": console.log(`\n\n🏁 Turn complete (${event.stopReason})`); break; + } } + +await sdk.shutdown(); ``` -### Hosted-tool resume +> **That's it.** The agent autonomously reads the directory, reasons about the output, and streams a formatted answer β€” all in one `for await` loop. -When the SDK emits a hosted-tool call, the host must execute the hosted tool and resume the same session with the same `callId`. +
-```ts -for await (const event of session.submitHostedToolResult({ - callId, - output: { ok: true }, -})) { - // resumed stream continues with the same execution context -} -``` +--- -Hosted tools currently force sequential tool execution inside the vendored loop. That keeps same-run suspend/resume robust for hosted tools such as `finish`. + -Across SDK recreation, the runtime can recover both single-tool and multi-tool hosted-tool suspensions. When the assistant issues multiple tool calls and one is a hosted tool, the SDK snapshots the full context and resumes correctly after restart. +## 🎯 Core Concepts -### Hooks +### The Event Stream -The SDK now exposes an OpenClaw-aligned hook surface for embedded-agent flows. Runtime-managed hooks currently include pre-run model/prompt hooks, `llm_input`, `agent_end`, `llm_output`, tool hooks, transcript persist hooks, and session lifecycle hooks. Host-bridged hooks such as `message_sending`, `message_sent`, `message_received`, `inbound_claim`, `before_dispatch`, `gateway_start`, and `gateway_stop` can be emitted directly through the SDK. +Every turn returns an `AsyncIterable`. No callbacks, no observers β€” one loop handles everything: -```ts -const result = await sdk.emitHook({ - hookName: "message_sending", - event: { - to: "channel:123", - content: "hello", - }, - context: { - channelId: "discord", - }, -}); -``` +
+
-The public hook registry accepts the full `GeneralAgentHookRegistration` union, including: - -- `before_model_resolve` -- `before_prompt_build` -- `before_agent_start` -- `llm_input` -- `llm_output` -- `agent_end` -- `before_compaction` -- `after_compaction` -- `before_reset` -- `inbound_claim` -- `message_received` -- `message_sending` -- `message_sent` -- `before_tool_call` -- `after_tool_call` -- `tool_result_persist` -- `before_message_write` -- `session_start` -- `session_end` -- `subagent_spawning` -- `subagent_delivery_target` -- `subagent_spawned` -- `subagent_ended` -- `gateway_start` -- `gateway_stop` -- `before_dispatch` - -All SDK-native hooks listed above are now auto-emitted by the runtime at the appropriate lifecycle points. This includes `before_reset`, compaction hooks (`before_compaction` / `after_compaction`), and subagent lifecycle hooks (`subagent_spawning` / `subagent_delivery_target` / `subagent_spawned` / `subagent_ended`). Host-bridged hooks such as `gateway_start`, `gateway_stop`, `inbound_claim`, `message_received`, `message_sending`, `message_sent`, and `before_dispatch` remain available through the `sdk.emitHook(...)` dispatch path. - -### Dynamic MCP servers - -The session can register dynamic MCP servers. The current runtime supports local `stdio` MCP servers and injects their tools into the same vendored loop as built-ins and hosted tools. - -```ts -session.setDynamicMcpServers({ - echo_server: { - transport: "stdio", - command: process.execPath, - args: ["/abs/path/to/echo-server.mjs"], - }, -}); +Event Flow + +

+
-const query = session.getCurrentQuery(); -const status = await query?.mcpServerStatus?.(); -await query?.toggleMcpServer?.("echo_server", false); +```typescript +for await (const event of session.streamTurn(input)) { + switch (event.kind) { + case "assistant_delta": // β†’ streaming text from the model + case "reasoning_delta": // β†’ model thinking (extended thinking) + case "tool_call": // β†’ built-in tool invoked + case "tool_result": // β†’ tool returned a result + case "hosted_tool_call": // β†’ YOUR tool was requested β€” SDK suspends ⏸️ + case "usage_snapshot": // β†’ token usage update + case "turn_complete": // β†’ this turn is done βœ… + } +} ``` -Both MCP transport modes are supported: +--- -- `stdio`: local process servers -- `http`: remote HTTP-based MCP endpoints +### 🎯 Hosted Tools β€” Your Code, The Model's Brain -Example with `http` transport: +Define tools the AI can call. You implement the logic; the SDK orchestrates the lifecycle: -```ts -session.setDynamicMcpServers({ - remote_server: { - transport: "http", - url: "https://mcp.example.com/api", - headers: { Authorization: "Bearer token" }, - }, +```typescript +const sdk = await createGeneralAgentSdk({ + // ... + hostedTools: [{ + name: "get_weather", + description: "Get current weather for a city", + inputSchema: { + type: "object", + properties: { city: { type: "string" } }, + required: ["city"], + }, + }], }); + +for await (const event of session.streamTurn(userMsg)) { + if (event.kind === "hosted_tool_call") { + // SDK suspends automatically ⏸️ + const weather = await getWeather(event.input.city); + + // Resume with your result ▢️ + for await (const e of session.submitHostedToolResult({ + callId: event.callId, + output: weather, + })) { + if (e.kind === "assistant_delta") process.stdout.write(e.text); + } + break; + } +} ``` -### Session reset +> **Restart-safe:** Hosted tool pauses survive process restarts. The SDK snapshots context and resumes correctly β€” even with multi-tool parallel calls. + +--- + +### πŸ’¬ Multi-Turn Memory -A session can be reset to clear its message history, usage state, and pending hosted-tool state while preserving the session identity and configuration. This is useful when the host wants to start fresh within the same session without creating a new one. +Sessions automatically maintain conversation history: -```ts -await session.reset("context_overflow"); +```typescript +// Turn 1 +for await (const e of session.streamTurn({ + role: "user", + content: [{ type: "text", text: "My name is Alice. I'm building a TypeScript app." }], +})) { /* ... */ } + +// Turn 2 β€” the agent remembers everything +for await (const e of session.streamTurn({ + role: "user", + content: [{ type: "text", text: "What's my name and what am I building?" }], +})) { /* ... */ } +// ➜ "Your name is Alice and you're building a TypeScript app." ``` -The reset fires a `before_reset` hook before clearing state, allowing hooks to observe the outgoing transcript. +--- -### Compaction +### πŸͺ Hooks β€” Intercept Everything -The SDK supports runtime compaction to manage context window pressure. Compaction can be triggered manually or automatically based on token usage thresholds. +26 hooks let you observe, modify, or block any lifecycle event: -```ts -// Manual compaction -await session.requestCompaction(); +```typescript +const sdk = await createGeneralAgentSdk({ + hooks: [ + // πŸ”€ Dynamic model routing + { + pluginId: "my-app", + hookName: "before_model_resolve", + handler: (ev) => ({ + modelOverride: needsPower(ev) ? "claude-opus-4-20250514" : "claude-sonnet-4-20250514", + }), + }, -// Automatic compaction when usage exceeds threshold -await session.maybeCompactByTokens({ - usedPctThreshold: 85, // compact when context is 85% full - cooldownMs: 60_000, // minimum 60s between compactions + // πŸ›‘οΈ Safety guardrails + { + pluginId: "my-app", + hookName: "before_tool_call", + handler: (ev) => { + if (ev.toolName === "exec" && ev.params.command?.includes("rm -rf")) + return { block: true, blockReason: "Dangerous command blocked" }; + }, + }, + + // πŸ“Š Usage audit trail + { + pluginId: "my-app", + hookName: "llm_output", + handler: (ev) => { + db.insert({ model: ev.model, tokens: ev.usage?.input + ev.usage?.output }); + }, + }, + ], }); ``` -Compaction truncates older messages and replaces them with a concise summary, keeping the most recent conversation context intact. The SDK emits `compaction_started` and `compaction_finished` stream events and fires `before_compaction` / `after_compaction` hooks during the process. - -The context window size is resolved dynamically based on the model (e.g., 200K for Claude models, 128K for GPT-4o, 1M+ for Gemini models). - -### Subagents +
+πŸ“‹ Full hook reference (19 SDK-native + 7 host-bridged) + +
+ +**SDK-native hooks** β€” auto-fired by the runtime: + +| Hook | Modifiable | Fires when... | +|:-----|:---:|:---| +| `before_model_resolve` | βœ… | Model selection begins | +| `before_prompt_build` | βœ… | System prompt is being assembled | +| `before_agent_start` | βœ… | Agent run is about to begin | +| `llm_input` | β€” | Request sent to LLM | +| `llm_output` | β€” | Response received from LLM | +| `agent_end` | β€” | Agent run completed | +| `before_tool_call` | βœ… | Tool is about to execute | +| `after_tool_call` | β€” | Tool execution finished | +| `tool_result_persist` | βœ… | Tool result being saved to transcript | +| `before_message_write` | βœ… | Message being written to history | +| `session_start` | β€” | Session first activated | +| `session_end` | β€” | Session completed | +| `before_compaction` | β€” | Context compaction starting | +| `after_compaction` | β€” | Context compaction finished | +| `before_reset` | β€” | Session about to clear | +| `subagent_spawning` | βœ… | Child agent creation requested | +| `subagent_delivery_target` | βœ… | Routing child agent delivery | +| `subagent_spawned` | β€” | Child agent created | +| `subagent_ended` | β€” | Child agent finished | + +**Host-bridged hooks** β€” you trigger these via `sdk.emitHook()`: + +| Hook | Purpose | +|:-----|:--------| +| `inbound_claim` | Incoming message routing | +| `before_dispatch` | Pre-dispatch filtering | +| `message_received` | Message arrival notification | +| `message_sending` | Modify/cancel outgoing messages | +| `message_sent` | Delivery confirmation | +| `gateway_start` | Gateway lifecycle start | +| `gateway_stop` | Gateway shutdown | + +
+ +--- + +### πŸ”Œ MCP Integration + +Plug in any [Model Context Protocol](https://modelcontextprotocol.io/) server β€” tools appear alongside built-ins: + +```typescript +session.setDynamicMcpServers({ + // Local process (stdio) + filesystem: { + transport: "stdio", + command: "npx", + args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"], + }, -The `subagents` tool is a first-class core built-in. When the agent calls it, the SDK automatically creates a child `GeneralAgentSdkSession` with: + // Remote endpoint (HTTP) + my_api: { + transport: "http", + url: "https://mcp.example.com/api", + headers: { Authorization: "Bearer token" }, + }, +}); +``` -- **Independent message history** β€” the child session has its own transcript, isolated from the parent -- **Scoped instructions** β€” the child receives its own system prompt via the `instructions` parameter -- **Scoped tool access** β€” the child inherits the parent's tools except `subagents` itself (preventing infinite recursion). An optional `allowedTools` parameter further restricts the child's tool set. -- **Parent/child coordination** β€” the parent's agent loop blocks while the child runs to completion, then receives the child's output as the tool result +--- -All 4 subagent lifecycle hooks fire automatically: -- `subagent_spawning` β€” before creation (can block with `{ status: "error" }`) -- `subagent_delivery_target` β€” after creation, before execution -- `subagent_spawned` β€” after child session is ready -- `subagent_ended` β€” after child completes (with `outcome: "ok"` or `"error"`) +### πŸ€– Subagents -### File checkpoints +The agent can spawn scoped child agents to divide and conquer complex tasks: -File mutation tools automatically create SDK-managed checkpoints before successful `write`, `edit`, and `apply_patch` calls. Checkpoints are Git-independent and can be rewound through the session API. +```typescript +const session = sdk.createSession({ + systemPrompt: `You are a tech lead. Delegate tasks using the subagents tool.`, + // ... +}); -```ts -const checkpoints = await session.listCheckpoints(); -await session.restoreCheckpoint(checkpoints[0]!.id); +// The agent will autonomously: +// 1. Analyze the task +// 2. Spawn child agents with scoped instructions + tools +// 3. Collect results from each child +// 4. Synthesize a final answer ``` -Restoring a checkpoint rewinds that checkpoint and any newer checkpoints, so rollback stays linear and predictable. +Each child gets **independent message history** and **scoped tool access**. The `subagents` tool is excluded from children to prevent infinite recursion. Four lifecycle hooks fire automatically: `subagent_spawning` β†’ `subagent_spawned` β†’ `subagent_ended`. -## Event Model +--- -`GeneralAgentStreamEvent` currently supports: +### πŸ“Š Session Management -- `assistant_delta` -- `reasoning_delta` -- `reasoning_end` -- `tool_call` -- `tool_result` -- `tool_error` -- `hosted_tool_call` -- `usage_snapshot` -- `compaction_started` -- `compaction_finished` -- `turn_complete` +```typescript +const session = sdk.createSession({ ... }); // Create +const resumed = await sdk.resumeSession("sess-123"); // Resume +const forked = await sdk.forkSession("sess-123", { // Fork + identity: { ... }, +}); +const sessions = await sdk.listSessions(); // List all +const history = await sdk.readSessionHistory("id"); // Read transcript +await session.reset("context_overflow"); // Reset +const usage = session.getUsageSnapshot(); // Token usage +``` -The host is expected to normalize these events into its own runtime contract when necessary. +**Context Compaction** β€” long conversations don't overflow: -## Persistence Model +```typescript +await session.maybeCompactByTokens({ + usedPctThreshold: 85, // trigger at 85% context usage + cooldownMs: 60_000, // min 60s between compactions +}); +``` -The SDK does not own canonical session identity. Instead, the host provides a `sessionStore` adapter and resolves the session file path explicitly. +**File Checkpoints** β€” every write creates an automatic rollback point: -Key persistence properties: +```typescript +const checkpoints = await session.listCheckpoints(); +await session.restoreCheckpoint(checkpoints[0].id); +``` -- provider-specific transcripts are allowed -- provider-specific raw stream logs are allowed -- both must remain under host-owned directories -- session identity must come from the host -- no parallel SDK-owned global session registry is introduced -- pending hosted-tool wait states and reconstructible continuation snapshots may be persisted when the runtime can resume them safely +
-The persistence adapter lives in [`src/public/persistence.ts`](./src/public/persistence.ts). +--- -## Logging Model + -The SDK emits canonical host-facing log events through `GeneralAgentHostLogger`. +## πŸ— Architecture -Supported log categories: +
+
-- `system_prompt` -- `tool_call` -- `tool_result` -- `assistant` -- `system` -- `provider_debug` +Architecture -The logger can also receive raw structured stream events through `onRawStreamEvent()` when the host wants a low-level audit trail. +

+
-## Plugin and Tool Policy +
+πŸ“ Repository structure -The factory accepts: +``` +general-agent-sdk/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ index.ts β†’ Package entry point +β”‚ β”œβ”€β”€ public/ β†’ Stable public API surface +β”‚ β”‚ β”œβ”€β”€ sdk.ts β†’ createGeneralAgentSdk() +β”‚ β”‚ β”œβ”€β”€ session.ts β†’ GeneralAgentSession +β”‚ β”‚ β”œβ”€β”€ events.ts β†’ GeneralAgentStreamEvent +β”‚ β”‚ β”œβ”€β”€ hooks.ts β†’ 26 hook type definitions +β”‚ β”‚ β”œβ”€β”€ types.ts β†’ Shared types +β”‚ β”‚ β”œβ”€β”€ host-tools.ts β†’ Hosted tool definitions +β”‚ β”‚ └── persistence.ts β†’ Storage adapter interface +β”‚ β”œβ”€β”€ core/ +β”‚ β”‚ β”œβ”€β”€ embedded-runner/ β†’ Session factory + runtime +β”‚ β”‚ β”œβ”€β”€ compaction/ β†’ Context window compaction +β”‚ β”‚ β”œβ”€β”€ mcp/ β†’ MCP client (stdio + HTTP) +β”‚ β”‚ β”œβ”€β”€ model/ β†’ Model context window resolution +β”‚ β”‚ β”œβ”€β”€ plugins/ β†’ Hook runner engine +β”‚ β”‚ β”œβ”€β”€ sessions/ β†’ Metadata index + transcript repair +β”‚ β”‚ └── checkpoints/ β†’ File checkpoint manager +β”‚ β”œβ”€β”€ tools/ β†’ Built-in tool implementations +β”‚ β”œβ”€β”€ loop/ β†’ Agent execution loop +β”‚ └── providers/ β†’ LLM provider adapters +β”œβ”€β”€ SDK DOCS/ β†’ Runnable examples + API reference +β”œβ”€β”€ tests/ β†’ 133 tests (unit / integration / contract / e2e) +└── manifests/ β†’ Upstream provenance tracking +``` -- `pluginMode: "disabled" | "allowlisted" | "full-embedded"` -- `enabledPluginIds?: string[]` -- `hostedTools?: GeneralAgentHostedToolDefinition[]` -- `tools?.web?.fetch?: GeneralAgentWebFetchToolOptions` -- `tools?.web?.search?: GeneralAgentWebSearchToolOptions` +
-This makes the host's trust boundary explicit. The SDK can preserve OpenClaw's plugin and tool semantics, but the host decides how much of that surface is enabled in embedded mode. +--- -`web_search` is now assembled as a built-in tool by default. Internally it follows a source-synced provider runtime: Brave is selected when credentials are available, and the SDK keeps the tool present by falling back to the bundled keyless DuckDuckGo provider when no Brave key is configured. +## πŸ“‹ Event Reference -Plugin scope is intentionally narrow: in this repository, plugin controls are reserved for web-related capabilities only. General-purpose non-web plugin loading is not a product goal for the SDK; other extensibility should go through built-in tools, hosted tools, MCP, or hooks. +| Event | Payload | Description | +|:------|:--------|:------------| +| `assistant_delta` | `{ text }` | Streaming text chunk from the model | +| `reasoning_delta` | `{ text }` | Extended thinking (chain-of-thought) | +| `reasoning_end` | β€” | Thinking phase complete | +| `tool_call` | `{ callId, toolName, input }` | Built-in tool invoked by the agent | +| `tool_result` | `{ callId, toolName, output }` | Tool execution result | +| `tool_error` | `{ callId, toolName, error }` | Tool execution failed | +| `hosted_tool_call` | `{ callId, toolName, input }` | **Your** tool requested β€” SDK suspends | +| `usage_snapshot` | `{ snapshot }` | Token usage update | +| `compaction_started` | `{ reason }` | Context compaction in progress | +| `compaction_finished` | `{ reason, tokensAfter? }` | Compaction complete | +| `turn_complete` | `{ stopReason }` | Turn finished | -## Repository Layout +--- -```text -src/ - index.ts # top-level export surface - public/ # supported public API - core/ # SDK-owned runtime implementation - upstream/openclaw/ # extracted upstream subset only -manifests/ - upstream-provenance.json # machine-readable provenance map -scripts/ - sync-from-openclaw.mjs - verify-upstream-snapshot.mjs -tests/ - contract/ - integration/ - unit/ -docs/ - superpowers/ - specs/ - plans/ -``` +## 🧰 Built-in Tools -## Development +| Tool | Description | Category | +|:-----|:------------|:---------| +| `read` | Read file contents with optional line ranges | File I/O | +| `write` | Create or overwrite files | File I/O | +| `edit` | Surgical string-replace edits | File I/O | +| `apply_patch` | Apply unified diff patches | File I/O | +| `exec` | Execute shell commands | System | +| `web_search` | Search the web (Brave / DuckDuckGo) | Web | +| `web_fetch` | Fetch and parse web pages | Web | +| `subagents` | Spawn scoped child agents | Orchestration | -Install dependencies: +--- -```bash -pnpm install -``` +## πŸ“– Examples -Run the main verification steps: +All examples are **runnable** TypeScript files: ```bash -pnpm run check -pnpm run build -pnpm run test -pnpm run test:e2e -node scripts/verify-upstream-snapshot.mjs +export ANTHROPIC_API_KEY="sk-ant-..." +npx tsx "SDK DOCS/01-hello-world.ts" ``` -## Upstream Provenance +| # | Example | What it covers | +|:--|:--------|:---------------| +| 01 | [`hello-world.ts`](./SDK%20DOCS/01-hello-world.ts) | Minimal agent, first `streamTurn()` call | +| 02 | [`multi-turn-chat.ts`](./SDK%20DOCS/02-multi-turn-chat.ts) | Interactive REPL with memory | +| 03 | [`hosted-tools.ts`](./SDK%20DOCS/03-hosted-tools.ts) | Custom tool with suspend/resume | +| 04 | [`session-lifecycle.ts`](./SDK%20DOCS/04-session-lifecycle.ts) | Create, resume, fork, reset | +| 05 | [`hooks.ts`](./SDK%20DOCS/05-hooks.ts) | Lifecycle hooks in action | +| 06 | [`mcp-servers.ts`](./SDK%20DOCS/06-mcp-servers.ts) | Dynamic MCP integration | +| 07 | [`compaction.ts`](./SDK%20DOCS/07-compaction.ts) | Context window management | +| 08 | [`subagents.ts`](./SDK%20DOCS/08-subagents.ts) | Child agent delegation | + +> πŸ“š Full API documentation: [`SDK DOCS/API-REFERENCE.md`](./SDK%20DOCS/API-REFERENCE.md) -This repository deliberately does not mirror the entire upstream OpenClaw source tree. +--- -Instead: +## πŸ”§ Development -- copied upstream snapshots live under `src/upstream/openclaw/` -- source-synced adapted files may also live in normal SDK paths such as `src/tools/` and `src/security/` -- each extracted file is tracked in [`manifests/upstream-provenance.json`](./manifests/upstream-provenance.json) -- provenance can be revalidated with `node scripts/verify-upstream-snapshot.mjs` +```bash +pnpm install # Install dependencies +pnpm run check # Type check +pnpm run build # Build +pnpm run test # 133 unit + integration tests +pnpm run test:e2e # Package smoke test +node scripts/verify-upstream-snapshot.mjs # Verify provenance +``` -This is a hard boundary, not just documentation. +--- -## Host Integration +
-A host application typically keeps the following responsibilities outside the SDK: +
-- canonical `session.json` -- dual-session switching -- channel ingress and owner notifications -- cross-engine continuity journal -- top-level profile and environment management +**MIT** Β· Built by [BabelCloud](https://github.com/babelcloud) -That design keeps the General Agent SDK as an execution backend rather than turning the host into an OpenClaw runtime shell. +
-## Specifications and Implementation Notes +If this project helps you build something cool, give it a ⭐ β€” it helps others find it too. -- Design spec: [`docs/superpowers/specs/2026-03-31-general-agent-sdk-source-sync-design.md`](./docs/superpowers/specs/2026-03-31-general-agent-sdk-source-sync-design.md) -- Implementation plan: [`docs/superpowers/plans/2026-03-31-general-agent-sdk-source-sync.md`](./docs/superpowers/plans/2026-03-31-general-agent-sdk-source-sync.md) +

-These documents are the source of truth for architecture, boundary rules, continuity requirements, and integration sequencing. +