OpenClaw
openclaw/openclaw · 源码级 基于 src/ 源码文本

逐模块还原 OpenClaw 的内核

深入 src/ 源码,逐模块还原真实实现——Gateway 入口、ACP 会话生成、MCP Client、插件系统、沙箱隔离。所有结论来自源码文本,含实际函数签名与类型定义。

# openclaw/openclaw · TypeScript · pnpm monorepo

~1708
acp-spawn.ts 行数
@modelcontextprotocol/sdk
MCP Client
openclaw.plugin.json
Manifest 协议
sandbox mode
隔离策略
向下探索
01
入口 · the entry point

Gateway 入口与主循环

源码:src/entry.ts(Node.js CLI 入口点)。整个系统从 runMainOrRootHelp() 开始。

src/entry.ts
// entry.ts:92-94
const entryFile = fileURLToPath(import.meta.url);
const installRoot = resolveEntryInstallRoot(entryFile);

// entry.ts:285
async function runMainOrRootHelp(argv: string[]): Promise<void> {
    const { runCli } = await gatewayEntryStartupTrace.measure("cli", async () => {
        const parsed = parseCliProfileArgs(parsedContainer.argv);
        // ...profile env applied
        await runCli({ argv: parsed.argv });
    });
}

启动链路:entry.ts → resolveEntryInstallRoot() → respawnWithoutOpenClawCompileCacheIfNeeded()(编译缓存优化)→ parseCliProfileArgs()(加载 profile 环境变量)→ runCli()。Gateway 模式下实际跑的是 runCli 内部的 gateway 子命令。

编译缓存通过 entry.compile-cache.ts 管理,用 OPENCLAW_COMPILE_CACHE_DIR 环境变量定位缓存目录,重 spawn 时不带缓存标志以避免递归。

图 01 · Gateway 冷启动管线
entry.ts
CLI 入口点
resolve
resolveEntryInstallRoot()
定位安装根
respawn?
compile-cache spawn
缓存命中检查
profile
parseCliProfileArgs()
注入 env
cli
runCli() → gateway
主循环
为什么要 respawn?首次进入时若检测到编译缓存可用但当前进程未带缓存标志,respawnWithoutOpenClawCompileCacheIfNeeded() 会以正确标志重启一个干净子进程;重 spawn 时剥掉缓存标志正是为了避免无限递归自我重启。
measure("cli")

启动 trace

gatewayEntryStartupTrace.measure() 把 CLI 解析包成一段可观测的计时区间,冷启动耗时由此打点。

profile env

多 profile 隔离

parseCliProfileArgs() 先于 runCli() 执行,把 profile 对应的环境变量套到进程上,实现多账号/多环境切换。

gateway 子命令

常驻模式

Gateway 模式实际是 runCli 内部的 gateway 子命令,启动后转入 WebSocket 服务长循环。

02
ACP · agent client protocol

ACP 会话生成

源码:src/agents/acp-spawn.ts(1708 行,核心文件)。ACP(Agent Client Protocol)的会话生成逻辑在此文件。

深度阅读 · 导入链 / 会话流程 / 父子 relay / 派生限制 →
acp-spawn.ts · 导入链
import { getAcpSessionManager } from "../acp/control-plane/manager.js";
import {
  cleanupFailedAcpSpawn,
  type AcpSpawnRuntimeCloseHandle,
} from "../acp/control-plane/spawn.js";
import { isAcpEnabledByPolicy, resolveAcpAgentPolicyError } from "../acp/policy.js";
import { readAcpSessionMeta } from "../acp/runtime/session-meta.js";
import { resolveAcpSessionCwd, resolveAcpThreadSessionDetailLines }
  from "@openclaw/acp-core/runtime/session-identifiers";
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";

ACP Core 是一个独立 package(@openclaw/acp-core),包含 session 标识符解析、运行时类型定义(AcpRuntimeSessionMode)以及会话元数据读写(session-meta.js)。

会话生成流程:acp-spawn.ts 调用 getAcpSessionManager() 获取会话管理器 → 通过 spawn.js 中的 cleanupFailedAcpSpawn() 处理失败清理 → 由 policy.js 的 isAcpEnabledByPolicy() 做策略检查。父进程通过 acp-spawn-parent-stream.ts 做 stdout/parent-stream relay,把 ACP harness 的输出打回到 OpenClaw 的会话流中。

会话限制由 config/agent-limits.ts 控制:

src/config/agent-limits.ts
DEFAULT_SUBAGENT_MAX_CHILDREN_PER_AGENT  // 每个 agent 最大子会话数
DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH         // 最大 spawn 深度(防止嵌套失控)
manager.ts

会话管理器

getAcpSessionManager() 返回进程级 ACP session manager,负责登记、查找与回收每个 ACP 会话句柄。

spawn.js

失败清理

cleanupFailedAcpSpawn() 在 spawn 半途失败时回滚已分配资源,AcpSpawnRuntimeCloseHandle 是对应的关闭句柄类型。

policy.js

策略闸门

isAcpEnabledByPolicy() 决定该 agent 是否允许走 ACP,resolveAcpAgentPolicyError() 给出被拒原因。

父子进程 relay:ACP harness 跑在子进程里,acp-spawn-parent-stream.ts 负责把子进程 stdout 转回父进程的会话流——resolveAcpThreadSessionDetailLines() / readAcpSessionMeta() 则在 @openclaw/acp-core 一侧解析会话标识与元数据。
嵌套防爆:子会话受 MAX_CHILDREN_PER_AGENT(扇出宽度)与 MAX_SPAWN_DEPTH(递归深度)双重约束,二者共同防止 sub-agent 失控自我繁殖。
03
harness · the contract

AgentHarness 类型系统

源码:src/agents/harness/types.ts。OpenClaw 的外部编程接口通过 AgentHarness 接口抽象——这是 ACP harness(Claude Code / Codex 等)接入的核心协议。

src/agents/harness/types.ts
export type AgentHarness = AgentHarnessRunCapability &
  AgentHarnessSideQuestionCapability &
  AgentHarnessClassificationCapability &
  AgentHarnessCompactionCapability &
  AgentHarnessSessionLifecycleCapability;

// 核心能力:
export type AgentHarnessRunCapability = {
  run(params: AgentHarnessAttemptParams): Promise<AgentHarnessAttemptResult>;
};

export type AgentHarnessSideQuestionCapability = {
  sideQuestion(params: AgentHarnessSideQuestionParams): Promise<AgentHarnessSideQuestionResult>;
};

export type AgentHarnessClassificationCapability = {
  classify?(input: unknown): Promise<AgentHarnessResultClassification>;
};

export type AgentHarnessCompactionCapability = {
  compact(params: AgentHarnessCompactParams): Promise<AgentHarnessCompactResult>;
};

export type AgentHarnessSessionLifecycleCapability = {
  reset?(params: AgentHarnessResetParams): Promise<void>;
};

关键点:run() 是必须方法,sideQuestion()(侧提问)、classify()(结果分类)、compact()(压缩会话)、reset() 都是可选扩展。OpenClaw 通过这个统一接口驱动 Claude Code、Cursor、Gemini CLI 等外部 harness。

run() · 必须

主运行能力

每次 attempt 走 run(),输入 AttemptParams,输出 AttemptResult。

compact() · 可选

会话压缩

把长 transcript 压成摘要,配合 classify() 做结果分类。

reset() · 可选

生命周期

会话级重置,让外部 harness 回到干净起点。

04
mcp · model context protocol

MCP 运行时

源码:src/agents/agent-bundle-mcp-runtime.ts。OpenClaw 的 MCP 客户端实现,基于 @modelcontextprotocol/sdk。

深度阅读 · BundleMcpSession / 三种传输 / 超时重试 / 配置指纹 →
agent-bundle-mcp-runtime.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";

// Session-scoped MCP session 结构
type BundleMcpSession = {
  serverName: string;
  client: Client;
  transport: Transport;
  transportType: "stdio" | "sse" | "streamable-http";
  requestTimeoutMs: number;
  supportsParallelToolCalls: boolean;
  detachStderr?: () => void;
};

// 全局 session manager 符号(保证每个进程只有一个 manager 实例)
const SESSION_MCP_RUNTIME_MANAGER_KEY = Symbol.for("openclaw.sessionMcpRuntimeManager");

工具调用链:Client.listTools() → 获取工具列表 → Client.callTool() 调用工具 → 结果通过 CallToolResult 类型返回。超时由 BUNDLE_MCP_CATALOG_LIST_TIMEOUT_MS = 1500ms 控制,失败重试用指数退避(BUNDLE_MCP_FAILURE_COOLDOWN_MS = 60000ms)。

关键函数签名
export function createBundleMcpJsonSchemaValidator(): jsonSchemaValidator;
export function resolveSessionMcpConfigSummary(params: {...}): {...};
export function resolveSessionMcpConfigFingerprint(params: {...}): string;
function resolveSessionMcpRuntimeIdleTtlMs(cfg?: OpenClawConfig): number;
  // 默认值:10 分钟 idle TTL

MCP 服务器配置通过 loadEmbeddedAgentMcpConfig() 加载(embedded-agent-mcp.ts),由 mcp-config-shared.ts 的 isMcpConfigRecord() 做类型校验,传输解析在 mcp-transport.ts 中。

表 01 · 三种传输与会话级常量
transportType承载方式典型场景
stdio子进程 stdin/stdout 管道本地 MCP server 可执行文件
sseServer-Sent Events 长连接远端只读流式 server
streamable-httpStreamableHTTPClientTransportHTTP 双向 / 可恢复会话
常量值作用
BUNDLE_MCP_CATALOG_LIST_TIMEOUT_MS1500mslistTools() 目录拉取超时
BUNDLE_MCP_FAILURE_COOLDOWN_MS60000ms失败后指数退避冷却窗
idle TTL10 分钟resolveSessionMcpRuntimeIdleTtlMs() 空闲回收
Symbol.for(...)

进程单例

SESSION_MCP_RUNTIME_MANAGER_KEY 用全局 Symbol 注册表锚定 manager,保证每进程唯一一个 MCP runtime manager,跨模块复用同一实例。

fingerprint

配置指纹

resolveSessionMcpConfigFingerprint() 把会话级 MCP 配置哈希成指纹;配置不变则复用已建连接,变更才重建 session。

05
plugins · the public api

插件系统公共接口

源码:src/plugins/types.ts(定义了 OpenClawPluginApi 所有公共注册方法)。插件通过 buildPluginApi() 构建的 API 对象注册自己的能力。

深度阅读 · 插件系统全景:API / Manifest / Runtime / 冷控制面 →
api-builder.ts · BuildPluginApiParams
export type BuildPluginApiParams = {
  id: string;
  name: string;
  version?: string;
  source: string;
  registrationMode: OpenClawPluginApi["registrationMode"];
  config: OpenClawConfig;
  runtime: PluginRuntime;
  logger: PluginLogger;
  handlers?: Partial<Pick<OpenClawPluginApi,
    | "registerTool"          // 注册工具
    | "registerHook"          // 注册生命周期钩子
    | "registerHttpRoute"     // 注册 HTTP 路由
    | "registerChannel"       // 注册消息渠道
    | "registerGatewayMethod" // 注册 Gateway 方法
    | "registerCli"           // 注册 CLI 命令
    | "registerProvider"      // 注册 Provider(模型/语音/图像等)
    | "registerAgentHarness"  // 注册外部 Agent Harness(ACP)
    | "registerMcpServer"     // 注册 MCP 服务器
    | ...(共 30+ 项)
  >>;
};

每个注册方法对应一个具体的功能领域。例如 registerTool 让插件声明自己的工具,registerHook 注入生命周期回调(start/stop/enable/disable 等),registerAgentHarness 是 ACP 外部 harness 接入的正式入口。

表 02 · 注册方法按能力域分组(节选自 30+ 项)
能力域注册方法插件能做什么
模型与执行registerTool · registerProvider · registerMcpServer暴露工具、接入模型/语音/图像 provider、挂载 MCP server
外部 AgentregisterAgentHarness把 Claude Code / Codex 等 ACP harness 接进来
消息与路由registerChannel · registerHttpRoute · registerGatewayMethod新增渠道、HTTP 端点、Gateway RPC 方法
生命周期与 CLIregisterHook · registerCli注入 start/stop/enable/disable 钩子、扩展 CLI 命令
handlers 是 Partial:BuildPluginApiParams.handlers 用 Partial<Pick<OpenClawPluginApi, …>> 描述——插件只实现自己关心的注册方法,未提供的能力域自动缺省,宿主据此裁剪 API 表面。
06
manifest · plugin metadata

Manifest 解析

源码:src/plugins/manifest.ts。插件的元数据描述文件是 openclaw.plugin.json(PLUGIN_MANIFEST_FILENAME 常量)。

深度阅读 · 解析缓存 SLRU 512 / 256 KiB 上限 / mtime 失效 →
src/plugins/manifest.ts
export const PLUGIN_MANIFEST_FILENAME = "openclaw.plugin.json";
export const MAX_PLUGIN_MANIFEST_BYTES = 256 * 1024;

// manifest 解析缓存(SLRU,512 条目)
const pluginManifestLoadCache = new PluginLruCache<PluginManifestLoadCacheEntry>(
  MAX_PLUGIN_MANIFEST_LOAD_CACHE_ENTRIES,
);

// 缓存 key 生成
export function createPluginCacheKey(...): PluginCacheKey;

// 清除缓存(测试用)
export function clearPluginManifestLoadCache(): void;

Manifest 中包含插件版本、入口点、config schema、commands、providers、hooks 等元数据。解析后缓存 512 条,缓存条目包含 mtimeMs/ctimeMs 以跟踪文件变化。Secret provider exec 参数长度限制等安全策略也在此文件校验。

文件名

固定约定

PLUGIN_MANIFEST_FILENAME = openclaw.plugin.json,发现插件即按此名查找。

256 KiB

体积上限

MAX_PLUGIN_MANIFEST_BYTES = 256 * 1024,超限直接拒绝解析。

SLRU · 512

解析缓存

PluginLruCache 缓存 512 条解析结果,重复加载走缓存。

mtime/ctime

失效跟踪

缓存条目记录 mtimeMs/ctimeMs,文件一变即作废重读。

测试钩子:createPluginCacheKey() 生成稳定缓存键,clearPluginManifestLoadCache() 供测试清空——保证用例之间不串缓存。
07
runtime · registry coordinator

Plugin 运行时协调器

源码:src/plugins/runtime.ts。Plugin 运行时协调器管理全局 plugin registry 状态。

深度阅读 · globalThis RegistryState / 三视角 pin / 安全清理 →
runtime.ts · 全局 RegistryState
// runtime.ts — 全局 RegistryState 保存在 globalThis 上
const state: RegistryState = (() => {
  const globalState = globalThis as typeof globalThis & {
    [PLUGIN_REGISTRY_STATE]?: RegistryState;
  };
  let registryState = globalState[PLUGIN_REGISTRY_STATE];
  if (!registryState) {
    registryState = {
      activeRegistry: null,
      activeVersion: 0,
      httpRoute: { registry: null, pinned: false, version: 0 },
      channel: { registry: null, pinned: false, version: 0 },
      importedPluginIds: new Set<string>(),
      runtimeSubagentMode: "default",
    };
    globalState[PLUGIN_REGISTRY_STATE] = registryState;
  }
  return registryState;
})();

// 关键函数:
function asPluginRegistry(registry: RegistryState["activeRegistry"]): PluginRegistry | null;
async function cleanupPreviousPluginHostRegistry(params: {...}): Promise<void>;
function isRegistryLive(registry: PluginRegistry): boolean;
function isRegistryPinned(registry: PluginRegistry): boolean;

运行时状态设计:每个进程只有一个全局 registry(存在 globalThis 上),通过 activeRegistry / httpRoute / channel 三个视角观察同一份 registry。pinned 标记用于防止正在使用的 registry 被意外清理。

activeRegistry

主视角

当前生效的 registry 与 activeVersion 版本号;热替换时版本自增,旧引用据此识别过期。

httpRoute

路由视角

HTTP 路由专属的 { registry, pinned, version } 三元组——独立 pin,避免请求中途路由表被换。

channel

渠道视角

渠道注册同样独立 pin/version,让消息渠道在重载窗口内保持稳定指向。

安全清理:isRegistryLive() / isRegistryPinned() 双判后,cleanupPreviousPluginHostRegistry() 才回收旧 host registry——pinned 为真时跳过,确保正在服务请求的 registry 不被拔掉。
08
sandbox · isolation model

沙箱隔离模型

源码:src/agents/sandbox/runtime-status.ts。沙箱模式由 sandbox.mode 配置控制。

深度阅读 · shouldSandboxSession / tool-policy / Docker·SSH / 失败模式 →
sandbox/runtime-status.ts
function shouldSandboxSession(
  cfg: SandboxConfig,
  sessionKey: string,
  mainSessionKey: string,
) {
  if (cfg.mode === "off") return false;   // 关闭沙箱
  if (cfg.mode === "all") return true;    // 所有 session 都沙箱
  // 默认:main session 不沙箱,其他沙箱
  return sessionKey.trim() !== mainSessionKey.trim();
}

export function resolveSandboxRuntimeStatus(params: {
  cfg?: OpenClawConfig;
  sessionKey?: string;
  agentId: string;
}): SandboxRuntimeStatus;

沙箱策略通过 tool-policy.ts 的 classifyToolAgainstSandboxToolPolicy() 对每个工具调用做检查。实际 Docker/SSH 隔离在 sandbox/config.ts 中配置。

sandbox.mode行为
off不隔离,所有 session 直接跑在宿主环境。
all全部隔离,每个 session 都进沙箱。
non-main (默认)只对非主 session 隔离;main session 保持原生权限。
resolveSandboxRuntimeStatus()

状态解析

按 cfg / sessionKey / agentId 算出该 agent 的 SandboxRuntimeStatus——是否隔离、用哪种隔离。

tool-policy.ts

逐工具放行

classifyToolAgainstSandboxToolPolicy() 对每次工具调用分类,决定在沙箱内是允许、降权还是拦截。

判定优先级:off → 永不隔离;all → 永远隔离;默认 non-main 只比较 sessionKey.trim() !== mainSessionKey.trim()——主会话保持原生,派生会话进沙箱。真正的 Docker/SSH 隔离细节落在 sandbox/config.ts。
09
memory · pluggable backends

记忆插件:LanceDB / Wiki / Active

源码:packages/memory-host-sdk + extensions/memory-*。OpenClaw 的记忆系统通过可插拔的记忆后端实现,三种内置后端共用一份 MemorySearchManager 契约——满足语义搜索、结构化 vault、即时会话状态三类不同需求。

深度阅读 · MemorySearchManager 契约 / 三后端 / agent_end 沉淀 / 0.95 判重 →

所有后端实现统一的 MemorySearchManager 接口——search / readFile / status / probeEmbeddingAvailability / probeVectorAvailability。两个 probe* 探针是降级逻辑的支点:嵌入或向量存储不可用时,宿主自动退回纯全文检索而非抛错。记忆自动沉淀挂在 agent_end 钩子上,按 agentId + sessionKey 检索,因此可跨 Telegram / Discord / Slack… 共享同一份记忆。

memory-lancedb

向量嵌入检索

把文本 embed 成向量存进 LanceDB,按 L2 距离找最近邻,相似度 1/(1+d)。agent_end 自动沉淀,0.95 相似即判重跳过。

memory-wiki

Markdown vault

每条记忆是一个 .md 文件(YAML frontmatter + 正文),content_store 原子写入(temp→fsync→rename)保证崩溃安全,全文检索后注入 prompt 段。

active-memory

会话间活跃上下文

按会话滑动窗口的即时上下文,SessionMemoryState 三增量门控——token+4000 & tool+8 & turn+4 同时越线才触发抽取。

extensions/memory-lancedb/index.ts — agent_end 自动沉淀 + 判重
const vector = await embeddings.embed(text);
const existing = await db.search(vector, 1, 0.95);  // 0.95 相似 = 重复
if (existing.length > 0) return;                  // 判重命中 → 跳过
await db.store({ text, vector, importance: 0.7, category });
// embedding 端点挂了 → probeEmbeddingAvailability=false → 退回全文检索,沉淀暂停
三后端 + 跨平台 sessionKey:记忆是可插拔后端而非单一向量库,且天生跨平台——这是 OpenClaw 记忆设计区别于"compaction 为主"或"单一 ChromaDB 混合检索"方案的核心。完整契约、沉淀闭环与降级路径见 记忆插件深度解析 →
10
tree · the source map

完整目录树

以下为 src/ 顶层目录(基于仓库主分支),把前面各节的源码文件放回它们的位置。

openclaw/src/
openclaw/src/
├── entry.ts                  # CLI 入口,runMainOrRootHelp()
├── entry.compile-cache.ts    # 编译缓存管理
├── entry.respawn.ts          # 进程重 spawn 逻辑
│
├── agents/                   # Agent 运行时核心
│   ├── acp-spawn.ts         # ACP 会话生成(1708 行)
│   ├── acp-runtime-overlay.ts
│   ├── agent-bundle-mcp-runtime.ts   # MCP Client 实现
│   ├── agent-bundle-mcp-tools.ts    # MCP 工具封装
│   ├── agent-command.ts      # Agent 命令处理
│   ├── harness/types.ts     # AgentHarness 接口定义
│   ├── sandbox/
│   │   ├── runtime-status.ts    # 沙箱模式判断
│   │   ├── config.ts           # 沙箱配置
│   │   └── tool-policy.ts      # 工具策略分类
│   └── agent-scope.ts       # Session / Agent 作用域解析
│
├── plugins/                  # 插件系统
│   ├── types.ts             # OpenClawPluginApi 公共接口
│   ├── manifest.ts          # openclaw.plugin.json 解析
│   ├── api-builder.ts       # PluginApi 构建器
│   ├── runtime.ts           # PluginRegistry 运行时协调器
│   ├── registry-types.ts    # Registry 类型定义
│   ├── registry-lifecycle.ts # Registry 生命周期管理
│   ├── host-hook-runtime.ts  # 钩子运行时分发
│   └── hook-types.ts        # 钩子类型定义
│
├── channels/                 # 消息渠道接入
│   ├── plugins/
│   │   ├── types.plugin.ts  # ChannelPlugin 接口
│   │   └── types.core.ts   # 核心 channel 类型
│   ├── route-projection.ts  # 消息路由映射
│   └── thread-bindings-*.ts # 线程/话题绑定
│
├── config/
│   ├── sessions/
│   │   ├── store.ts        # Session 存储
│   │   └── paths.ts        # 路径解析
│   ├── agent-limits.ts     # 子 agent 限制
│   └── types.openclaw.ts   # OpenClaw 配置类型
│
├── acp/                     # ACP 协议实现
│   ├── control-plane/
│   │   ├── manager.ts      # ACP Session 管理器
│   │   └── spawn.ts        # ACP Spawn 逻辑
│   └── policy.ts           # ACP 策略检查
│
├── cli/                     # CLI 相关
│   ├── argv.ts             # 参数解析
│   ├── profile.ts          # Profile 环境变量
│   └── program/            # CLI 程序结构
│
├── gateway/                 # Gateway WebSocket API
│   ├── server-methods/     # Gateway 方法处理
│   └── call.ts             # Gateway 调用封装
│
└── packages/                # 内部共享包
    ├── acp-core/           # ACP 核心类型与工具
    ├── agent-core/         # Agent 核心逻辑
    ├── plugin-sdk/         # 插件 SDK(public)
    ├── gateway-protocol/   # Gateway 协议定义
    ├── llm-core/           # LLM 接口
    ├── memory-host-sdk/    # MemorySearchManager 记忆契约
    └── model-catalog-core/ # 模型目录

extensions/                 # 141 个可插拔扩展(与 src/ 同级)
├── memory-lancedb/         # 向量嵌入检索(L2 距离)
├── memory-wiki/            # Markdown vault + 全文检索
└── active-memory/          # 会话滑动窗口即时上下文
OpenClaw · 源码即文档
openclaw/openclaw · TypeScript · pnpm monorepo
基于 src/ 源码文本撰写 · 含实际函数签名与类型定义
github.com/openclaw/openclaw ↗