版本: v2.0 (综述版) 分析对象: openclaw/openclaw
HEAD(2026-02-27 clone) 代码规模: 3,940 TypeScript 文件 / 726,408 行 / ~70 个子模块 技术栈: TypeScript (核心) + Swift (iOS) + Kotlin (Android) 分析方法: 5 个并行分析 Agent 读取 200+ 源文件, 含行号级引用
- 代码结构总览与模块度量
- Memory 模块 — 三层记忆系统
- Agents 模块 — Agent 运行时与工具体系
- Gateway 模块 — 常驻网关核心
- Cron 模块 — 定时任务与隔离 Session
- Hooks 模块 — 内部事件总线
- Channels 模块 — 多渠道插件体系
- Compaction — 上下文压缩与记忆保全
- Provider 层 — 多模型路由与缓存
- Config 模块 — 配置体系与热重载
- Security 模块 — 安全审计与内容防护
- Secrets 模块 — 凭证管理与加密
- Auto-Reply 模块 — 消息处理全链路
- Infrastructure 模块 — 基础设施服务
- Browser 模块 — 浏览器自动化
- Media & TTS 模块 — 多媒体理解与语音合成
- Plugin 系统 — 插件架构与 SDK
- ACP 模块 — Agent Communication Protocol
- CLI / Commands / TUI — 命令行与终端界面
- Sessions & Routing — 会话与路由
- Daemon 模块 — 守护进程管理
- 跨模块架构总结
src/
├── memory/ # 82 文件, 10,123 行 — 三层记忆: 索引、检索、embedding、衰减、MMR
├── agents/ # 348 文件, 70,690 行 — Agent 运行时: system prompt、tools、skills、sandbox、compaction
│ ├── tools/ # 25+ 内置工具实现
│ ├── skills/ # Skill 发现/加载/过滤
│ ├── sandbox/ # Docker 沙箱
│ └── pi-extensions/ # Compaction safeguard 等扩展
├── gateway/ # 187 文件, 35,582 行 — WebSocket 网关: 服务端、chat loop、hooks HTTP、cron 集成
│ ├── server-methods/# 200+ WS RPC 方法
│ └── protocol/ # Zod schema 定义
├── cron/ # 36 文件, 6,555 行 — 定时任务: 调度、隔离 session、投递
│ ├── service/ # CronService 核心(ops/jobs/timer/state)
│ └── isolated-agent/# 隔离 cron agent 运行器
├── hooks/ # 23 文件, 3,809 行 — 内部事件总线 + 生命周期 hooks
│ └── bundled/ # 内置 hooks(boot-md, session-memory 等)
├── channels/ # 98 文件, 11,526 行 — 渠道插件体系(27 adapter 接口)
│ └── plugins/ # 插件类型定义 + 加载器
├── config/ # 120 文件, 23,555 行 — Zod-based 配置 schema + 热重载 + 迁移
├── security/ # 19 文件, 6,593 行 — 审计、ACL、内容防护、ReDoS 检测
├── secrets/ # 11 文件, 3,848 行 — 三种 provider (env/file/exec) 凭证管理
├── auto-reply/ # 179 文件, 30,915 行 — 消息处理全链路: 去重、分发、队列、TTS
├── infra/ # 199 文件, 34,601 行 — SSRF 防护、网关锁、心跳调度、成本分析
├── browser/ # 77 文件, 12,973 行 — Playwright/CDP 浏览器自动化
├── media/ # 18 文件, 2,594 行 — 媒体处理 + MIME 检测
├── media-understanding/# 35 文件, 4,016 行 — 音视频理解 (Whisper/Gemini/Vision)
├── tts/ # 2 文件, 1,620 行 — 三 Provider TTS (Edge/ElevenLabs/OpenAI)
├── plugins/ # 36 文件, 7,515 行 — 插件注册表、加载器、生命周期
├── plugin-sdk/ # 25 文件, 2,199 行 — 插件开发 SDK 公共 API
├── acp/ # 30 文件, 5,276 行 — Agent Communication Protocol (IDE 集成)
├── cli/ # 174 文件, 25,004 行 — CLI 入口、懒加载命令、快速路由
├── commands/ # 214 文件, 37,038 行 — 35+ 顶层命令实现
├── tui/ # 28 文件, 5,161 行 — 终端 UI (Ink 风格)
├── web/ # 47 文件, 6,424 行 — WhatsApp Web 渠道集成
├── sessions/ # 7 文件, 487 行 — Session 身份、Provenance、SendPolicy
├── routing/ # 5 文件, 855 行 — 7 级路由绑定、身份链接
├── daemon/ # 28 文件, 3,898 行 — macOS/Linux/Windows 守护进程
├── providers/ # 6 文件, 612 行 — LLM provider 注册 + Copilot token 交换
├── telegram/ # 54 文件, 12,279 行 — Telegram 渠道实现
├── discord/ # 78 文件, 20,763 行 — Discord 渠道实现
├── slack/ # 59 文件, 8,759 行 — Slack 渠道实现
├── signal/ # 28 文件, 3,235 行 — Signal 渠道实现
├── imessage/ # 16 文件, 2,227 行 — iMessage 渠道实现
├── line/ # 43 文件, 5,603 行 — LINE 渠道实现
└── ... # logging, markdown, pairing, process, shared, terminal, types, utils, wizard
| 模块 | 文件数 | 代码行 | 职责 |
|---|---|---|---|
| agents/ | 348 | 70,690 | Agent 运行时 + 工具 |
| commands/ | 214 | 37,038 | CLI 命令 |
| gateway/ | 187 | 35,582 | WebSocket 网关 |
| infra/ | 199 | 34,601 | 基础设施 |
| auto-reply/ | 179 | 30,915 | 消息处理 |
| cli/ | 174 | 25,004 | CLI 框架 |
| config/ | 120 | 23,555 | 配置体系 |
| discord/ | 78 | 20,763 | Discord 渠道 |
| browser/ | 77 | 12,973 | 浏览器自动化 |
| telegram/ | 54 | 12,279 | Telegram 渠道 |
路径: src/memory/ (82 文件, 核心 18 文件约 5,766 行)
三级继承分离关注点:
MemoryManagerSyncOps (1,216 行) ← 数据库、文件监听、同步编排
└── MemoryManagerEmbeddingOps (807 行) ← embedding 生成、批量、缓存
└── MemoryIndexManager (640 行) ← 公共 API: search, readFile, status, close
构造函数为 private (L141)。通过静态工厂 MemoryIndexManager.get() (L103-139) 创建:
- 解析
ResolvedMemorySearchConfig - 计算缓存 key =
agentId + workspaceDir + settings - 命中
INDEX_CACHE(模块级Map) 则复用 - 否则调
createEmbeddingProvider()创建 provider,构造实例并缓存
关键常量 (L33-37):
const SNIPPET_MAX_CHARS = 700;
const VECTOR_TABLE = "chunks_vec";
const FTS_TABLE = "chunks_fts";
const EMBEDDING_CACHE_TABLE = "embedding_cache";
const BATCH_FAILURE_LIMIT = 2;query
│
├─ warmSession() ← 每 session 首次同步
│
├─ if dirty && sync.onSearch → 非阻塞后台同步
│
├─ 解析 minScore, maxResults, candidates (= maxResults × candidateMultiplier, 上限 200)
│
├─── [无 embedding provider] ─→ FTS-only 模式 (L234-267)
│ extractKeywords() → 多关键词并行搜索 → 去重取最高分
│
├─── [有 provider + hybrid 启用] ─→ Hybrid 模式 (L269-293)
│ FTS 关键词搜索 ∥ 向量搜索 → mergeHybridResults()
│
└─── [有 provider + hybrid 禁用] ─→ Vector-only 模式 (L279-281)
| 表 | 类型 | 核心列 |
|---|---|---|
meta |
常规 | key TEXT PK, value TEXT |
files |
常规 | path TEXT PK, source TEXT, hash TEXT, mtime INT, size INT |
chunks |
常规 | id TEXT PK, path, source, start_line, end_line, hash, model, text, embedding TEXT (JSON), updated_at |
chunks_fts |
FTS5 虚拟表 | text (索引), id/path/source/model/start_line/end_line (UNINDEXED) |
chunks_vec |
sqlite-vec 虚拟表 | 二进制 float 向量 |
embedding_cache |
常规 | (provider, model, provider_key, hash) 复合 PK, embedding TEXT, dims INT |
索引: idx_chunks_path, idx_chunks_source, idx_embedding_cache_updated_at
Schema 迁移: ensureColumn() (L85-95) 用 PRAGMA table_info() + ALTER TABLE ADD COLUMN 做增量升级。
FTS Query 构建 — buildFtsQuery() (L33-44):
// 输入 "hello world" → 输出 '"hello" AND "world"'
// Unicode-aware: /[\p{L}\p{N}_]+/guBM25 Rank→Score — bm25RankToScore() (L46-49):
return 1 / (1 + Math.max(0, rank)); // rank 0 = 1.0, rank 9 = 0.1融合算法 — mergeHybridResults() (L51-149):
- Union by chunk ID (L73-119):
Map<chunkId, {vectorScore, textScore}>, 非 Intersection - 加权线性组合 (L121-131):
score = vectorWeight × vectorScore + textWeight × textScore - Temporal Decay (L133-139):
applyTemporalDecayToHybridResults() - 降序排序 (L140)
- MMR 去重 (L143-146):
applyMMRToHybridResults()
衰减函数:
lambda = Math.LN2 / halfLifeDays; // L17-22
multiplier = Math.exp(-lambda * ageDays); // L24-34
decayedScore = score * multiplier; // L36-42默认 halfLifeDays = 30 (默认关闭, enabled: false)。
日期提取优先级 — extractTimestamp() (L82-114):
- 文件路径匹配
memory/YYYY-MM-DD.md→ 从路径取日期 - Evergreen 路径 (
MEMORY.md,memory/下非日期命名文件) → 不衰减 (返回 null) - 其他文件 → fallback 到
mtime
Evergreen 判定 — isEvergreenMemoryPath() (L71-80): MEMORY.md 和 memory/*.md (非日期格式) 永不衰减。
公式 (L101, Carbonell & Goldstein 1998):
MMR = λ × relevance − (1 − λ) × max_similarity_to_selected
默认 lambda = 0.7(0 = 最大多样性, 1 = 最大相关性)。
相似度: Jaccard on token sets — tokenize() (L32-35) 提取 [a-z0-9_]+; jaccardSimilarity() (L41-61) = |A∩B| / |A∪B|。
算法 — mmrRerank() (L116-183):
- 预分词,缓存所有 token sets
- Score 归一化到 [0,1]
- 贪心迭代选择: 每轮选 MMR 最高的候选, tie-break 取原始分更高者
- λ 钳位到 [0, 1]
sqlite-vec 路径 (L34-69):
SELECT c.*, vec_distance_cosine(v.embedding, ?) AS dist
FROM chunks_vec v JOIN chunks c ON v.rowid = c.rowid
WHERE c.model = ? AND c.source IN (?)
ORDER BY dist ASC LIMIT ?
-- score = 1 - dist内存 fallback (L71-93): 加载所有 chunks → JSON 解析 embedding → cosineSimilarity() → 排序。
关键词搜索 (L136-191):
SELECT *, bm25(chunks_fts) AS rank
FROM chunks_fts WHERE chunks_fts MATCH ?
ORDER BY rank ASC LIMIT ?5 个 Provider:
| Provider | 默认模型 |
|---|---|
local (node-llama-cpp) |
embeddinggemma-300m-qat-Q8_0 (GGUF) |
| openai | text-embedding-3-small |
| gemini | Gemini embeddings |
| voyage | Voyage AI |
| mistral | Mistral embeddings |
"auto" 模式解析 (L174-211):
- 磁盘上有本地模型 → 优先 local
- 依次尝试
["openai", "gemini", "voyage", "mistral"] - API key 缺失 → 跳过; 网络错误 → 直接 fatal
- 全部无 key →
provider: null(FTS-only 模式)
批量重试: 指数退避 + jitter, base 500ms, max 8000ms, 3 次重试, 连续失败 2 次后永久禁用 batch mode。
全量重索引触发条件 (L859-868): force 标记、无已有元数据、Model/provider 变更、配置的 sources 变更、chunk token/overlap 设置变更、向量可用但无 vectorDims。
原子重索引 — runSafeReindex() (L996-1103):
- 创建临时 DB
{dbPath}.tmp-{uuid} - 从原 DB 种入 embedding cache
- 在临时 DB 同步所有文件
- 原子替换 (swap) 临时 DB → 原 DB
- 失败则从 backup 恢复
文件监听 (L356-398): chokidar 监听 MEMORY.md, memory.md, memory/**/*.md, 加上 extraPaths。变更后 debounce → dirty = true → 调度同步。
Session Delta 追踪 (L400-560): 监听 onSessionTranscriptUpdate, debounce 5s, 按字节/消息数阈值增量同步, 64KB chunk 读取。
支持 8 种语言 的停用词和分词: English, Spanish, Portuguese, Arabic, Korean (助词剥离), Japanese (假名+汉字 bigram), Chinese (字 unigram + bigram)。
FallbackMemoryManager: 首选 QMD backend; 首次 search() 失败后 永久切换 到内置 MemoryIndexManager。
路径: src/agents/ (348 文件, 70,690 行)
buildAgentSystemPrompt() (L189-664) 在 每次 Agent turn 动态装配 system prompt,共 21 个 section:
| # | Section | 行号 | 说明 |
|---|---|---|---|
| 1 | Identity | L419 | "You are a personal assistant running inside OpenClaw." |
| 2 | Tooling | L421-453 | 可用工具列表 + 摘要, 按 toolOrder 排序 |
| 3 | Tool Call Style | L456-461 | 叙述风格指导 |
| 4 | Safety | L390-396 | 反权力追求、遵守 stop/pause、不操纵 |
| 5 | CLI Quick Ref | L464-471 | OpenClaw 子命令 |
| 6 | Skills | L473 | buildSkillsSection() — 按需读取 |
| 7 | Memory Recall | L474 | memory_search/memory_get 指引 |
| 8 | Self-Update | L476-486 | 仅 full mode |
| 9 | Model Aliases | L489-498 | 仅 full mode |
| 10 | Workspace | L502-506 | 工作目录 + sandbox 指引 |
| 11 | Docs | L507 | 本地文档路径 |
| 12 | Sandbox | L508-552 | 容器 workdir, browser bridge |
| 13 | User Identity | L553 | Owner numbers |
| 14 | Time | L554-556 | 仅时区 (利于缓存) |
| 15 | Bootstrap Files | L557-558 | SOUL.md, IDENTITY.md, AGENTS.md |
| 16 | Messaging | L561-568 | Session 路由 |
| 17 | Reasoning | L601-603 | <think>...</think> tags |
| 18 | Project Context | L605-625 | 遍历 contextFiles[] |
| 19 | Silent Replies | L628-643 | SILENT_REPLY_TOKEN 规则 |
| 20 | Heartbeats | L646-656 | HEARTBEAT_OK ack |
| 21 | Runtime Footer | L658-664 | Agent ID, host, OS, model, channel |
Prompt Mode: "full" (主 agent) / "minimal" (子 agent, 跳过 memory/docs/self-update) / "none" (仅 identity)。
入口: runEmbeddedPiAgent() (L192)
- 解析 session lane + global lane
- Auth Profile 解析: 按优先级轮转 API key, 支持 cooldown/failure 追踪
- Context Window Guard: 硬最小 16,000 tokens, 警告阈值 32,000 tokens
- 重试循环: 基础 24 次, 每个 auth profile 加 8 次, 最小 32, 最大 160 次
- 错误分类与恢复:
isAuthAssistantError→ 轮转到下一个 auth profileisBillingAssistantError→ 格式化计费错误isLikelyContextOverflowError→ 触发 compactionisRateLimitAssistantError→ 退避isFailoverAssistantError→ fallback 到备选模型
Active Run 追踪 — runs.ts:
const ACTIVE_EMBEDDED_RUNS = new Map<string, EmbeddedPiQueueHandle>();
// queueMessage, isStreaming, isCompacting, abort25+ 核心工具 按 section 分组:
| Section | 工具 |
|---|---|
| fs | read, write, edit, apply_patch |
| runtime | exec, process |
| web | web_search, web_fetch |
| memory | memory_search, memory_get |
| sessions | sessions_list, sessions_history, sessions_send, sessions_spawn |
| ui | browser, canvas |
| messaging | message |
| automation | cron, gateway |
| agents | subagents |
| media | image, tts |
- Shell-bleed 检测 (L55-149): 解析命令字符串,检测 Python/JS 文件包含裸 shell 变量语法 (
$HOME),引导使用os.environ.get()或process.env[] - 三种执行宿主: Local (直接 shell), Gateway (代理), Node (远程节点)
- 审批工作流:
bash-tools.exec-approval-request.ts管理工具调用审批
5 个搜索 Provider: Brave, Perplexity, Grok, Gemini, Kimi
每个 provider 有独立实现路径,搜索结果经 wrapWebContent() 安全包装防注入。缓存层: 15 分钟 TTL。
DEFAULT_FETCH_MAX_CHARS = 50_000
DEFAULT_FETCH_MAX_RESPONSE_BYTES = 2_000_000
DEFAULT_FETCH_MAX_REDIRECTS = 3支持 Firecrawl 集成 (可选高级抓取)。所有请求经 SSRF 防护 (fetchWithWebToolsNetworkGuard)。
createMemorySearchTool(): 语义搜索MEMORY.md+memory/*.md,结果附path#L{start}-L{end}引用,受maxInjectedChars预算限制createMemoryGetTool(): 安全片段读取器,按行号范围读取
支持操作: browserStart, browserStop, browserSnapshot, browserTabs, browserAct, browserNavigate, browserScreenshotAction, browserPdfSave。可路由到远程 Node。所有浏览器提取内容经 wrapExternalContent() 防注入。
跨渠道富消息投递: Discord (按钮/菜单/embeds), Telegram (内联按钮/引用), 通用 (send/reply/thread-reply/broadcast)。
Spawn 流程 (L166-550):
- 深度守卫:
callerDepth >= maxSpawnDepth→ 拒绝 - 并发守卫:
maxChildrenPerAgent(默认 5) - Agent 授权: 检查
subagents.allowAgents列表 (支持*) - Session 创建:
agent:${targetAgentId}:subagent:${crypto.randomUUID()} - Gateway 调用: 3 次
sessions.patch(spawnDepth, model, thinking) - Thread 绑定:
ensureThreadBindingForSubagentSpawn() - System Prompt:
buildSubagentSystemPrompt()注入请求者上下文 - 执行:
callGateway({ method: "agent" }), AGENT_LANE_SUBAGENT lane - 注册:
registerSubagentRun()记录到注册表 - 生命周期 Hook: 触发
subagent_spawned
Spawn 模式: "run" (一次性, 自动公告结果) / "session" (持久线程, 支持跟进)
6 层发现来源 (优先级从低到高): extraDirs < Bundled < Managed < Personal < Project < Workspace
限制:
maxCandidatesPerRoot: 300
maxSkillsLoadedPerSource: 200
maxSkillsInPrompt: 150
maxSkillsPromptChars: 30_000
maxSkillFileBytes: 256_000Prompt 生成: 二分搜索截断到 maxSkillsPromptChars。
- Owner-only 强制: 非 owner 调用受限工具抛 "Tool restricted to owner senders."
- Allow/Deny 策略:
ToolPolicyLike支持allow[]/deny[]+ 组展开 (group:plugins) - Sandbox 工具策略: 沙箱内允许 fs/runtime/memory/sessions, 禁止 browser/canvas/cron/gateway
CONTEXT_WINDOW_HARD_MIN_TOKENS = 16_000
CONTEXT_WINDOW_WARN_BELOW_TOKENS = 32_000解析链: config override → model reported → agents.defaults.contextTokens cap → fallback。
路径: src/gateway/ (187 文件, 35,582 行)
startGatewayServer(port=18789, opts) 的完整 21 步启动序列:
| 步骤 | 行号 | 操作 |
|---|---|---|
| 1 | L212-233 | Config 校验, 遗留格式迁移 |
| 2 | L248-260 | Plugin 自动启用 (env vars) |
| 3 | L262-371 | Secrets 激活 (锁 + 级联) |
| 4 | L347-365 | Auth bootstrap (生成 gateway token) |
| 5 | L376-379 | SIGUSR1 重启策略 |
| 6 | L380 | Subagent registry init |
| 7 | L383-401 | 加载 plugins, 合并 channel + base methods |
| 8 | L403-428 | Runtime config 解析 |
| 9 | L494-518 | 创建 runtime state |
| 10 | L520-538 | Node registry (IoT/mobile) |
| 11 | L540-545 | Cron service 构建 |
| 12 | L547-553 | Channel manager 启动 |
| 13 | L555-570 | Discovery (mDNS/Bonjour, Tailscale) |
| 14 | L572-595 | Skills remote registry (debounced 30s) |
| 15 | L620-633 | Agent event handler 订阅 |
| 16 | L641-646 | Heartbeat runner |
| 17 | L658 | Cron start |
| 18 | L662-673 | Delivery recovery (crash 恢复) |
| 19 | L696-768 | 挂载所有 WS handlers |
| 20 | L814-822 | 触发 gateway_start hook |
| 21 | L824-884 | Config reloader (watch openclaw.json) |
export const coreGatewayHandlers = {
...connectHandlers, // 连接握手
...chatHandlers, // chat send/abort/history/inject
...sessionsHandlers, // session CRUD
...agentHandlers, // agent run/wait
...nodeHandlers, // node pairing/invoke
...channelsHandlers, // channel status/logout
...configHandlers, // config get/set/apply/patch
...cronHandlers, // cron CRUD
...modelsHandlers, // model catalog
...skillsHandlers, // skills listing
...browserHandlers, // browser operations
...sendHandlers, // outbound delivery
...usageHandlers, // usage stats
...ttsHandlers, // text-to-speech
// ...还有 health, wizard, push, talk, web, system, update, device, logs 等
};请求分发 (L97-149):
authorizeGatewayMethod()检查 role + scopes- 控制面写方法 (
config.apply,config.patch,update.run) 限流: 3 次/60s - Handler 查找: extra handlers 优先, 再查 core handlers
- 输入清洗: NFC 归一化, 去 null 字节, 移除禁止控制字符
- 历史清洗: 截断文本 12,000 字符, 去 thinking 签名, 去 base64 图片
- 分发: 通过
dispatchInboundMessage()进入 auto-reply 系统
- sessions.list: 从 store 列出, 支持 derived titles + last message preview
- sessions.patch: 15+ 可 patch 字段 (model, thinkingLevel, verboseLevel, execHost, sendPolicy)
- sessions.reset: 归档 transcript, 重置状态, 解绑 thread
- sessions.compact: 触发 context compaction
- agent (主方法): 接收消息, 解析 session, 通过 auto-reply 分发。支持
/new,/reset - agent.wait: 阻塞等待 agent run 完成
三种帧类型 (discriminated union on type):
RequestFrame: { type: "req", id, method, params? }
ResponseFrame: { type: "res", id, ok, payload?, error? }
EventFrame: { type: "event", event, payload?, seq?, stateVersion? }连接握手 — ConnectParams: protocol version 协商, client identification, 设备签名, auth credentials, capabilities。
Hello 响应 — HelloOk: 协商后 protocol version, server version, feature advertisement, full state snapshot, canvas host URL, auth result, policy limits。
Auth 模式: "none" | "token" | "password" | "trusted-proxy"
authorizeGatewayConnect() (L364-469) 多策略认证:
- Trusted-proxy: 验证远程地址 + 可信代理头部
- Tailscale: whois 查找 + header 三方验证
- Token/Password: 常时间比较 (
safeEqualSecret()) - Rate limiting: 按 IP 追踪失败次数
- ChatRunRegistry: 队列式 run 追踪 (per-session queue)
- Delta 节流: 150ms 最小间隔
- Heartbeat 隐藏: 根据
resolveHeartbeatVisibility()配置压制心跳输出 - 安全: strip heartbeat tokens, 压制 silent replies
路径: src/cron/ (36 文件, 6,555 行)
type CronSchedule =
| { kind: "at", at: string } // 一次性绝对时间
| { kind: "every", ms: number } // 间隔
| { kind: "cron", expression: string } // cron 表达式 + 时区 + staggerSession 目标: "main" (注入主 session) | "isolated" (一次性隔离 session)
载荷: systemEvent (文本注入) | agentTurn (完整 agent turn)
run() (L336-451):
- 加锁阶段: 设置
runningAtMs, 持久化, 释放锁 - 无锁执行: 在锁外运行, 保持 service 对读请求的响应能力
- 重新加锁: 应用结果, 处理
deleteAfterRun, 重算 nextRunAtMs
防雷群: SHA-256(jobId) % stagger window — 确定性散列到不同时间偏移。
安全阈值:
MAX_SCHEDULE_ERRORS = 3→ 自动 disableSTUCK_RUN_MS = 2 * 60 * 60 * 1000(2 小时卡死检测)MAX_TIMER_DELAY_MS = 60_000,MIN_REFIRE_GAP_MS = 2_000
runCronIsolatedAgentTurn() (L90):
- 解析 agent config, workspace, model catalog
resolveCronSession()— 评估新鲜度, stale 则新建resolveDeliveryTarget()— 解析投递目标- 运行 agent (CLI or embedded Pi) + model fallback
dispatchCronDelivery()— 投递结果
- direct: 结构化内容 →
deliverOutboundPayloads() - announce: 文本 → 主 session system-message 注入 →
runSubagentAnnounceFlow()
路径: src/hooks/ (23 文件, 3,809 行)
type InternalHookEventType = "command" | "session" | "agent" | "gateway" | "message"| 事件 | Action | 用途 |
|---|---|---|
agent:bootstrap |
"bootstrap" |
修改注入文件 |
gateway:startup |
"startup" |
启动时逻辑 |
command:new |
"new" |
新会话事件 |
command:reset |
"reset" |
会话重置 |
message:received |
"received" |
消息入站 |
message:sent |
"sent" |
消息出站 |
triggerInternalHook(event) (L192): 同时 触发通用类型 (如 "command") 和具体 action (如 "command:new")。错误被 catch + log, 不阻止后续。
来源优先级: extra < bundled < managed < workspace
发现流程: 扫描 HOOK.md → 定位 handler → 解析 frontmatter → 资格检查 (OS/binary/env) → 路径边界校验 → 动态 import → 注册
| Hook | 事件 | 功能 |
|---|---|---|
| boot-md | gateway:startup |
启动时执行 BOOT.md checklist |
| session-memory | command:new/reset |
保存记忆到 memory/<date>-<slug>.md (LLM 生成 slug) |
| bootstrap-extra-files | agent:bootstrap |
加载额外 bootstrap 文件 |
| command-logger | command |
JSON 日志到 logs/commands.log |
路径: src/channels/ (98 文件, 11,526 行)
CHAT_CHANNEL_ORDER: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage
注: 代码库中 未找到 Feishu/Lark 原生实现。
ChannelPlugin 定义了 27 个可选 adapter 槽位:
| Adapter | 职责 |
|---|---|
config (必需) |
账号列表/解析 |
setup |
账号验证 |
pairing |
设备配对 |
security |
DM 策略, 安全警告 |
groups |
群组 mention/intro |
outbound |
消息发送 (direct/gateway/hybrid) |
status |
探测/审计/快照 |
gateway |
启停账号, QR 登录 |
streaming |
流式回复合并 |
threading |
线程/回复模式 |
directory |
peer/group 列表 |
actions |
send/edit/unsend/react |
heartbeat |
就绪检查 |
| ...等 | 共 27 个 |
ChannelDock: 轻量级元数据/行为层, 与重量级 plugin 实现分离。buildDockFromPlugin() 从 plugin 提取 dock 兼容字段。
| 特性 | Telegram | Discord | Slack | IRC | Signal | |
|---|---|---|---|---|---|---|
| Chat Types | direct, group, channel, thread | direct, channel, thread | direct, group | direct, channel, thread | direct, group | direct, group |
| Reactions | - | yes | yes | yes | - | yes |
| Commands | yes | yes | - | yes | - | - |
| Threads | - | yes | - | yes | - | - |
| Block Streaming | yes | - | - | - | yes | - |
| Text Limit | 4000 | 2000 | 4000 | 4000 | 350 | 4000 |
Dock (dock.ts L321-363): capabilities 含 polls, reactions, media, nativeCommands, threads, textChunkLimit=2000。
Outbound (plugins/outbound/discord.ts): Webhook 优先 → 降级到直接发送。支持 persona (username + avatar)。
Agent Tools (agents/tools/discord-actions*.ts, ~50KB): 5 个文件覆盖 guild 管理、消息操作、审核、存在状态。
Dock (dock.ts L234-277): textChunkLimit=4000, blockStreaming=true。
Threading 细节: 只使用 MessageThreadId (topic ID), 不使用 ReplyToId (避免 DM 中的 invalid message_thread_id 错误)。
HTML 格式化: markdownToTelegramHtmlChunks(), textMode="html"。
| 函数 | 算法 |
|---|---|
estimateMessagesTokens() |
逐消息估算, stripToolResultDetails() 安全过滤 |
splitMessagesByTokenShare() |
按 token 预算等分 N 份 |
chunkMessagesByMaxTokens() |
SAFETY_MARGIN = 1.2 (20% buffer) |
computeAdaptiveChunkRatio() |
大消息 >10% window → 从 BASE=0.4 降至 MIN=0.15 |
summarizeWithFallback() |
完整摘要 → 排除超大消息 → 尺寸描述 fallback |
summarizeInStages() |
分段 → 独立摘要 → merge prompt 合并 |
pruneHistoryForContextShare() |
maxHistoryShare=0.5, 最旧优先丢, 修复 tool_use/result 配对 |
Hook 到 session_before_compact:
- 无真实对话 → 取消
- 收集文件操作 + tool 失败 (最多 8 条, 240 字截断)
- 自适应 chunk ratio +
summarizeInStages() - 追加 tool failure section + AGENTS.md "Red Lines"
- 任何失败 →
{ cancel: true }(不冒损坏风险)
触发条件: totalTokens > contextWindow - reserve - softThreshold (默认 softThreshold = 4000)
Flush Prompt: "Store durable memories now (use memory/YYYY-MM-DD.md)... If nothing to store, reply with NO_REPLY."
从 AGENTS.md 提取 "Session Startup" + "Red Lines" sections (max 3000 字符), 注入系统消息: "Session was just compacted. Execute your Session Startup sequence now."
超时 → fallback 到 pre-compaction snapshot (不丢数据)。
modelRegistry.find(provider, modelId)— 内置注册表cfg.models.providers— 用户自定义resolveForwardCompatModel()— 版本兼容- OpenRouter passthrough — 任何 model ID 直通
- Config provider fallback
applyExtraParamsToAgent() 链式组合 10+ wrapper:
| Wrapper | 功能 |
|---|---|
| Cache retention | "short" (5min Anthropic 直连) / "long" / "none" |
| Anthropic betas | context1m (Claude Opus 4/Sonnet 4 1M context) |
| OpenRouter cache_control | system message 注入 cache_control: { type: "ephemeral" } |
| Google thinking | sanitize thinkingBudget, map to thinkingLevel |
| Z.AI | tool_stream=true |
| 等 | SiliconFlow, Bedrock, OpenAI Responses... |
- 检查本地缓存
~/.openclaw/credentials/github-copilot.token.json - 有效 (>5min) 则复用
- 否则:
https://api.github.com/copilot_internal/v2/token - 从 token 提取
proxy-ep→api.*作为 base URL - 缓存到磁盘
路径: src/config/ (120 文件, 23,555 行)
OpenClawSchema (L131-813): ~40 个顶层 section 的 z.object().strict():
meta, env, wizard, diagnostics, logging, update, browser, ui, secrets, auth, acp, models, nodeHost, agents, tools, bindings, broadcast, audio, media, messages, commands, approvals, session, cron, hooks, web, channels, discovery, canvasHost, talk, gateway, memory, skills, plugins
使用 .superRefine() (L783-813) 交叉验证 broadcast agent 引用。
文件读取 → JSON5 解析 → $include 解析 (递归, 深度限制 10) →
${ENV_VAR} 替换 → 遗留格式迁移 → Zod 验证 → 默认值应用 → 返回类型化 OpenClawConfig
MAX_INCLUDE_DEPTH = 10MAX_INCLUDE_FILE_BYTES = 2MB- 循环引用检测 (visited set)
- 路径遍历阻止
deepMerge(): 数组拼接, 对象递归合并
Model Aliases:
opus → anthropic/claude-opus-4-6
gpt → openai/gpt-5.2Compaction 默认模式: "safeguard"
- Part 1 (571 行): 10 次迁移 — bindings/providers/routing 重构
- Part 2 (427 行): agent model-config-v2, routing-agents-v2
- Part 3 (222 行): memorySearch, tools.bash→exec, identity→agents.list
RFC 7396 风格, 扩展支持 id-based 数组合并 (元素有 id 字段时 merge 而非替换)。
路径: src/security/ (19 文件, 6,593 行)
runSecurityAudit() 编排 20+ 审计收集器:
同步收集器 (audit-extra.sync.ts): 仅配置分析
异步收集器 (audit-extra.async.ts): I/O-bound 检查
Gateway 配置检查 15+ 项: Bind 地址、Auth 方法、CORS、Tailscale funnel、Rate limiting、TLS。
wrapExternalContent() (L219-245):
- 生成随机 boundary ID
- 清洗伪造 boundary 标记
- 折叠 Unicode 同形字 (全角 ASCII, 角括号变体)
- 检测可疑模式 (prompt injection 指标)
- 包裹
[BEGIN_EXTERNAL_{id}]...[END_EXTERNAL_{id}]
12 个可疑模式 (L17-30): system:, <|endoftext|>, [INST], @assistant 等。
hasNestedRepetition(): 栈式解析器检测嵌套量词。compileSafeRegex(): 缓存编译 (max 256 条)。
resolveDmGroupAccessDecision(): 状态机解析 group/DM 策略: open → pairing → allowlist → disabled。
8 个 reason code: ALLOWED_DEFAULT, BLOCKED_BY_POLICY, BLOCKED_PAIRING_REQUIRED 等。
静态分析规则:
dangerous-exec: child_processdynamic-code-execution: eval/new Functioncrypto-mining: stratum/coinhive/xmrigpotential-exfiltration: readFile + fetchobfuscated-code: 长 hex/base64 序列env-harvesting: process.env + fetch
fixSecurityFootguns(): 启用 redactSensitive, 设置限制性 group 策略, 修复文件权限 (Unix: chmod 700/600, Windows: icacls)。
路径: src/secrets/ (11 文件, 3,848 行)
| Provider | 实现 | 安全措施 |
|---|---|---|
| env | process.env[id] |
- |
| file | fs.readFile() |
assertSecurePath() 验证权限/所有权/symlink |
| exec | 外部二进制 + JSON 协议 | stdin/stdout, protocolVersion: 1 |
resolveSecretRefValues() (L604-676):
- 按 provider type 分组所有
SecretRef - env refs → 读
process.env - file refs →
assertSecurePath()→fs.readFile() - exec refs → spawn binary, JSON stdin
{protocolVersion:1, ids}→ JSON stdout{values} - 并发限制
assertSecurePath() (L99-179): 检查所有权、权限 (非 world/group 可读)、symlink 目标。
prepareSecretsRuntimeSnapshot() → 收集赋值 → 解析所有 refs → 返回快照
activateSecretsRuntimeSnapshot() → 原子写入 config + auth-store
扫描: config 文件明文、auth-profiles 明文、.env 暴露、legacy auth.json、ref 可解析性、shadowing 检测。
审计码: PLAINTEXT_FOUND, REF_UNRESOLVED, REF_SHADOWED, LEGACY_RESIDUE。
路径: src/auto-reply/ (179 文件, 30,915 行)
Inbound Layer: inbound-debounce.ts → 聚合快速消息
↓
Dispatch Layer: dispatch-from-config.ts → 去重, hooks, abort, send policy, ACP, 跨渠道路由, TTS
↓
Reply Generation: get-reply.ts → 解析 model/skills/directives/commands → runPreparedReply
↓
Delivery Layer: reply-dispatcher.ts → 序列化投递, reservation-based idle 检测, 人类延迟
↓
Queue Layer: queue/ → 6 种队列模式, drop policy, debounce, 跨渠道 drain
↓
Memory Layer: memory-flush.ts + post-compaction-context.ts → 保全 agent 身份
GetReplyOptions: 主选项包, 含 15+ 回调 hooks (onAgentRunStart, onPartialReply, onToolResult, onModelSelected...)ReplyPayload: 通用回复单元: text, mediaUrl, replyToId, channelData, isReasoningTypingPolicy:"auto" | "user_message" | "system_event" | "heartbeat"
dispatchReplyFromConfig() 完整流水线:
- 检查重复消息 (L155)
- 触发 plugin hooks (
message_received) (L182-243) - 跨渠道路由 (L252-260): 如果来源渠道与当前 surface 不同, 路由回原渠道
- Fast abort
/stop(L300-334) - Send policy 检查 (L338-358)
- ACP 分发尝试 (L361-379)
- Block reply + TTS 累积 (L384)
- 调用
getReplyFromConfig()(L406-468) - 迭代 reply payloads, 应用 TTS, 路由投递 (L470-511)
序列化投递队列, reservation-based idle 检测:
- pending counter 初始为 1 (reservation)
markComplete()通过Promise.resolve().then()延迟释放, 让 in-flight enqueue 完成- 人类延迟:
800ms ~ 2500ms随机间隔 (block replies)
6 种模式: steer | followup | collect | steer-backlog | interrupt | queue
Drop Policy: old | new | summarize
默认: debounce 1000ms, cap 20, drop=summarize
缓存式命令注册表, 支持多 text alias + 参数解析模式 + 作用域 (text/native/both)。
ThinkLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh"
VerboseLevel: "off" | "on" | "full"
ElevatedLevel: "off" | "on" | "ask" | "full"XHIGH_MODEL_REFS: openai/gpt-5.2, openai-codex/gpt-5.3-codex 等。
路径: src/infra/ (199 文件, 34,601 行)
两阶段验证:
- Phase 1 (pre-DNS): 检查字面主机/IP
- Phase 2 (post-DNS): 检查解析后地址
DNS Pinning: 创建 pinned lookup 函数, 返回预解析地址, round-robin 轮转。防 TOCTOU 攻击。
阻止列表: localhost, localhost.localdomain, metadata.google.internal + .localhost, .local, .internal 后缀。
fetchWithSsrFGuard(): SSRF 安全 HTTP fetch + 手动重定向跟随:
- 验证 URL scheme (http/https)
- 解析 pinned hostname
- 创建 dispatcher (环境代理或 pinned)
redirect: "manual"→ 跨域重定向去敏感 header (authorization, cookie)- 重定向循环检测 (visited Set)
文件式单例锁, 防多实例:
- 排他文件创建 (
wxflag) - 多策略存活检测: port probe →
kill(0)→ Linux/proc/pid/statstartTime →/proc/pid/cmdline - 默认超时 5s, 轮询 100ms, 过期 30s
Session 级临时事件队列 (max 20), 连续去重, 不持久化。事件前缀到下次 agent prompt, 然后 drain。
流式 JSONL 分析引擎:
readJsonlRecords(): async generator 逐行解析loadCostUsageSummary(): 跨 session 聚合 + 日桶loadSessionCostSummary(): 单 session 详细: message 统计, tool 使用, model 分布, 延迟百分位 (p50/p90/p95/p99)
多 Agent 心跳调度器:
- 可配置 per-agent intervals
- HEARTBEAT.md 文件门控 (无内容则跳过)
- 24h 窗口重复抑制
- Transcript 修剪 (HEARTBEAT_OK 时)
- 活跃时间段限制
- 多渠道投递 (channel plugin 就绪检查)
文件式跨进程消息传递: 重启进程写哨兵 → 新进程消费 → 报告状态。投递上下文跨重启保留。
路径: src/browser/ (77 文件, 12,973 行)
CDP Discovery (chrome.ts, cdp.ts)
→ 连接运行中的 Chrome 实例
↓
Session Management (pw-session.ts)
→ Playwright Browser 连接池, WeakMap per-page 状态
↓
Tool Implementation (pw-tools-core.*.ts)
→ navigate, click, type, snapshot, screenshot...
↓
HTTP Server (server.ts, routes/)
→ 作为 REST 端点暴露
↓
Client (client.ts)
→ 类型化 HTTP client
ConnectedBrowser: Browser + CDP WS URL + disconnect callbackPageState(WeakMap per-page): console messages, errors, network requests, snapshot ref cachegetPageForTargetId(): 通过 CDP 连接, 按 targetId 查找 page, 缓存连接refLocator(): 将 snapshot ref ("e1") 解析为 Playwright Locator
统一模式: resolve page → ensure state → resolve element → apply action → return result
navigateViaPlaywright(): SSRF guard →page.goto()→ 重定向后二次检查clickViaPlaywright(): 支持 button/modifiers/double-clicktypeViaPlaywright(): 支持 slow (pressSequentially) / fast (fill)fillFormViaPlaywright(): 批量填充 (textbox/checkbox/radio/combobox/slider)evaluateViaPlaywright(): 任意 JS 执行armDialogViaPlaywright(): 预注册 dialog handler
AI agent 的浏览器交互范式:
- 拍 accessibility snapshot → 获得元素 refs (e1, e2...)
- 用 ref 调用交互工具 (click, type...)
- 重新 snapshot 观察结果
大多操作用 Playwright 高层 API; locale/timezone/device emulation fallback 到 CDP session commands。
策略模式 + 回退链: 多个 backend 按优先级尝试, 首次成功胜出。
Auto-discovery: 检测 API key + 本地二进制:
- 当前 agent 的 active model (如有 vision 能力)
- 本地 CLI:
whisper-cli,whisper,sherpa-onnx-offline - Gemini CLI
- API key 检测 (OPENAI_API_KEY 等)
Provider: OpenAI, Google, Anthropic + 本地 Whisper/Sherpa
三 Provider: Edge / ElevenLabs / OpenAI
Directive 系统: LLM 可在输出中嵌入 [[tts:...]] 控制:
[[tts:provider=openai]]— 切换 provider[[tts:voice=coral]]— 切换声音[[tts:stability=0.7 speed=1.2]]— 调节参数[[tts:text]]...[[/tts:text]]— 自定义 TTS 文本
自动模式: off | always | inbound | tagged
长文本处理: 超过 maxLength → LLM 自动摘要 → TTS → 临时文件 → 5 分钟后清理
OpenAI 14 声音: alloy, ash, ballad, cedar, coral, echo, fable, juniper, marin, onyx, nova, sage, shimmer, verse
路径: src/plugins/ (36 文件, 7,515 行) + src/plugin-sdk/ (25 文件, 2,199 行)
24 个生命周期 Hook: before_model_resolve, before_prompt_build, before_agent_start, llm_input, llm_output, agent_end, before_compaction, after_compaction, before_reset, 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
Plugin API (OpenClawPluginApi): registerTool, registerHook, registerHttpHandler, registerHttpRoute, registerChannel, registerGatewayMethod, registerCli, registerService, registerProvider, registerCommand
发现 (扫描 workspace/global/config-paths)
→ Manifest 解析
→ 边界文件检查 (openBoundaryFileSync, 防 symlink 穿越)
→ Jiti 动态加载 (TypeScript JIT 编译)
→ Config 校验 (JSON Schema)
→ register(api) 调用
→ 注册到 PluginRegistry
安全: 边界文件检查、provenance 追踪 (未安装来源的插件告警)、配置 JSON Schema 校验。
"Kitchen sink" 风格公共 API, 暴露:
- Channel adapters (Discord/Telegram/Slack/等)
- ACP runtime
- 安全工具 (SSRF, allowlist, DM policy)
- 媒体工具 (MIME, store, web media)
- 基础设施 (file locks, webhook, dedup, JSON store)
路径: src/acp/ (30 文件, 5,276 行)
ACP = Agent Client Protocol — 开放协议, 用于 IDE/编辑器与 AI Agent 集成 (类似 LSP 之于语言服务器)。
ACP Client (IDE 扩展) ←→ stdin/stdout ndjson ←→ AcpGatewayAgent ←→ WebSocket ←→ Gateway
AcpGatewayAgent 实现 Agent 接口:
- initialize() (L122): 返回能力: loadSession, image prompts, embedded context
- newSession() (L145): 限流 → UUID → 解析 session key → 创建
- prompt() (L252): 提取 text + image → 2MB 限制 (CWE-400) →
chat.send→ Promise 等待完成 - cancel() (L311): abort controller →
chat.abort - handleDeltaEvent() (L435): 增量文本流 (delta = fullText.slice(sentSoFar))
- handleAgentEvent() (L331): tool call 事件 →
inferToolKind()映射 (read/edit/execute/fetch)
内存 LRU store: max 5000 sessions, 24h idle TTL, active run 保护 (不会被淘汰)。
extractAttachmentsFromPrompt(): 仅接受 base64 imageescapeInlineControlChars(): 转义 NUL/CR/LF/TAB/Unicode 行分隔符 — 防日志注入- Prompt 大小限制: 2MB (CWE-400)
- Session 创建限流
分层启动:
entry.ts: 进程级 — respawn (加--disable-warning), profile 解析, 环境归一化cli/run-main.ts: 快速路由 (9 个只读命令绕过 Commander) + Commander program 构建cli/program/: Commander.js 框架 + 懒加载命令注册
懒加载模式: 命令注册为轻量级 placeholder (allowUnknownOption(true)), 首次调用时动态 import 真正的模块, 然后重新解析 argv。显著降低启动时间。
10 个核心命令组:
setup— 初始化 config/workspaceonboard— 交互式引导configure— 凭证/渠道设置config— 非交互式 get/setdoctor— 20+ 步诊断修复message— 消息发送/轮询memory— 搜索/重索引agent— Agent turn 执行 (828 行主函数)status/health/sessions— 渠道健康/会话列表browser— Chromium 管理
24 个子命令组: acp, gateway, daemon, logs, system, models, approvals, nodes, sandbox, tui, cron, dns, docs, hooks, plugins, channels, security, secrets, skills, update, completion...
核心执行入口:
- 验证消息体和目标
- 加载 config, 解析 agent ID
- 解析 model + thinking/verbose levels
- 解析 session (支持 ACP session)
- 构建 skills snapshot
runWithModelFallback()→runAgentAttempt()(CLI provider 或 embedded Pi)- 生命周期事件 + session store 更新 + 结果投递
20+ 步全面诊断: CLI 更新, config 迁移, auth token, legacy state, sandbox, 安全, hooks, systemd linger, gateway 健康, 记忆探测...
Component-Tree + Event-Driven 架构, 基于 @mariozechner/pi-tui:
Root Container
├── Header (Text)
├── ChatLog (Container) — 消息流, max 180 组件, LRU 裁剪
├── StatusBar (Container)
├── Footer (Text)
└── Editor (CustomEditor) — 多行编辑器 + 自动补全
18 个 Slash 命令: /help, /status, /agent, /model, /think, /verbose, /reasoning, /elevated, /new, /reset, /usage, /settings, /exit...
快捷键: Escape (abort), Ctrl+C (clear/warn/exit), Ctrl+L (model 选择), Ctrl+G (agent 选择), Ctrl+P (session 选择), Ctrl+T (toggle thinking)
Gateway Chat Client (tui/gateway-chat.ts): WebSocket RPC 封装, 支持 chat.send/abort/history, sessions.list/patch/reset, models.list。
主题: 20 色调色板, 语法高亮 (cli-highlight), Markdown 渲染, 超链接 (OSC 8)。
Input Provenance: "external_user" | "inter_session" | "internal_system" — 追踪每条消息来源, 支持多 agent 场景的信任分级。
Session Key 结构: agent:<agentId>:<channel>:<chatType>:<peerId>
辅助函数:
parseAgentSessionKey(): 解析agent:id:restderiveSessionChatType(): 提取direct/group/channelisCronRunSessionKey(): 匹配cron:<name>:run:<id>isSubagentSessionKey(): 检查subagent:前缀getSubagentDepth(): 计数:subagent:层级
Send Policy — send-policy.ts (124 行): 多层规则匹配 (channel, chatType, keyPrefix), 首个 deny 即拒, 有 allow 则放行, 默认 allow。
7 级绑定解析 — resolve-route.ts (444 行):
| 优先级 | Tier | 匹配条件 |
|---|---|---|
| 1 | binding.peer |
精确 peer ID |
| 2 | binding.peer.parent |
线程父 peer 继承 |
| 3 | binding.guild+roles |
Guild ID + Discord 角色 |
| 4 | binding.guild |
Guild ID |
| 5 | binding.team |
Team ID (Slack/Teams) |
| 6 | binding.account |
特定 account (非通配) |
| 7 | binding.channel |
通配 account (*) |
首次匹配的 tier 胜出; 无匹配则 fallback 到 default agent。
DM 范围: 控制 session 粒度:
"main": 所有 DM 折叠到agent:<agentId>:main"per-peer":agent:<agentId>:direct:<peerId>"per-channel-peer":agent:<agentId>:<channel>:direct:<peerId>"per-account-channel-peer": 全隔离
身份链接 (identityLinks): 跨平台身份折叠 → 统一 session。
缓存: WeakMap<OpenClawConfig, EvaluatedBindingsCache>, max 2000 条目。
路径: src/daemon/ (28 文件, 3,898 行)
resolveGatewayService() → platform 分发:
darwin → LaunchAgent service
linux → systemd user service
win32 → Scheduled Task serviceinstallLaunchAgent(): plist 写入 → legacy 卸载 → bootstrap → kickstart
restartLaunchAgent(): bootout → PID 等待 (10s, 200ms poll) → bootstrap → kickstart
installSystemdService(): unit 文件 → backup → write → legacy 清理 → daemon-reload → enable → restart
所有操作通过 systemctl --user (非 root)。
Profile-aware 命名 + 3 代遗留兼容 (openclaw, clawdbot, moltbot):
GATEWAY_LAUNCH_AGENT_LABEL = "ai.openclaw.gateway"
GATEWAY_SYSTEMD_SERVICE_NAME = "openclaw-gateway"
GATEWAY_WINDOWS_TASK_NAME = "OpenClaw Gateway"
跨平台扫描额外/遗留 gateway 实例: macOS (plist), Linux (systemd units), Windows (schtasks)。
[Gateway] resolveSessionAgentIds()
│
▼
[Agents] resolveBootstrapFilesForRun()
│ ← agent:bootstrap hook 可修改 files
▼
[Agents] loadWorkspaceSkillEntries() → buildWorkspaceSkillsPrompt()
│
▼
[Agents] buildSystemPromptParams() → resolveModel, timezone, runtime
│
▼
[Agents] buildAgentSystemPrompt()
│ ← 21 个 section 动态拼装
▼
System Prompt (注入 LLM)
[Channel Plugin] 接收消息
│
├─ inbound-debounce (聚合快速消息)
│
▼
[Auto-Reply] dispatchReplyFromConfig()
├─ 去重检查
├─ plugin hooks (message_received)
├─ 跨渠道路由检查
├─ /stop fast abort
├─ send policy 检查
├─ ACP 分发尝试
│
▼
[Auto-Reply] getReplyFromConfig()
├─ 解析 model, skills, directives
├─ media understanding + link understanding
├─ command/status/skill 内联处理
│
▼
[Agents] runEmbeddedPiAgent()
├─ auth profile 轮转
├─ context window guard
├─ 重试循环 (32-160 次)
│
▼
[Reply Dispatcher] 序列化投递
├─ reservation-based idle 检测
├─ 人类延迟 (800-2500ms)
├─ TTS 应用
│
▼
[Channel Plugin] 发送消息
[Memory Flush] ← softThresholdTokens 前触发
│ agent 写 memory/YYYY-MM-DD.md
▼
[Compaction] ← 83.5% context window 触发
│ safeguard → summarizeInStages()
│ 任何失败 → cancel (保留原文)
▼
[Post-Compaction] ← 注入 "Session Startup" 指令
│
▼
[Session Memory Hook] ← /new 或 /reset 时
│ LLM 生成 slug → memory/<date>-<slug>.md
▼
[Memory Search] ← Agent turn 中按需调用
│ FTS5 ∥ sqlite-vec → hybrid merge → decay → MMR → Top-K
[Timer fires]
│
▼
[ops.run()] ← 加锁: 设 runningAtMs, 持久化
│
▼ (释放锁)
[executeJobCoreWithTimeout()]
│
├── systemEvent → enqueueSystemEvent (注入主 session)
│
└── agentTurn → runCronIsolatedAgentTurn()
│ resolveCronSession() → 新建或复用
│ resolveDeliveryTarget()
│ runWithModelFallback() → CLI or embedded Pi
│ dispatchCronDelivery() → direct or announce
▼
[Timer re-arms] ← 重新加锁: 应用结果, recompute, arm
| 模式 | 应用位置 | 说明 |
|---|---|---|
| 三级类继承 | Memory Manager | SyncOps → EmbeddingOps → IndexManager 分离关注点 |
| 原子替换 | Memory reindex | 临时 DB → 原子 swap, 失败从 backup 恢复 |
| 两阶段执行 | Cron ops.run() | 加锁预约 → 无锁执行 → 加锁应用 |
| StreamFn 装饰链 | Provider extra-params | 10+ 可组合 wrapper |
| 两级事件分发 | Hooks | 通用 type + 具体 type:action 同时触发 |
| 防御性 cancel | Compaction safeguard | 任何失败 cancel 而非冒损坏风险 |
| Pre-compaction snapshot | Compaction timeout | 超时恢复到前快照 |
| 确定性散列 stagger | Cron jobs | SHA-256(jobId) % window 防雷群 |
| Union 非 Intersection | Hybrid search | 任一通道命中即保留 |
| Evergreen 文件豁免 | Temporal decay | MEMORY.md 永不衰减 |
| 懒加载命令 | CLI | Commander placeholder → 动态 import → reparse |
| 快速路由 | CLI | 9 个只读命令绕过完整 Commander 解析 |
| Reservation-based idle | Reply dispatcher | pending counter=1 → markComplete → settle |
| Snapshot-Ref-Action | Browser | accessibility snapshot → element ref → interact |
| 协议桥接 | ACP | IDE ←→ ndjson ←→ AcpGatewayAgent ←→ WS ←→ Gateway |
| Discovery-Manifest-Load-Register | Plugins | 目录扫描 → manifest → 边界检查 → Jiti 加载 → register() |
| 7 级绑定解析 | Routing | peer > parent > guild+roles > guild > team > account > channel > default |
| 文件式单例锁 | Gateway lock | exclusive wx create + 多策略存活检测 |
| 跨进程哨兵 | Restart sentinel | 写 JSON → 消费 → 报告 (投递上下文保留) |
| 三 Provider 凭证模型 | Secrets | env / file / exec, 原子快照激活 |
| Findings-Based 审计 | Security | 20+ 收集器 → 标准化 Finding 对象 → 自动修复 |
PaperBot (Python/FastAPI) 通过 TypeScript 薄层插件接入 OpenClaw 生态, 保留独立性的同时利用 OpenClaw 基础设施。
OpenClaw Gateway ←→ paperbot-openclaw plugin (TS shim, ~300 行)
│ HTTP
PaperBot FastAPI (Python, 不改核心)
| 模块 | 适配度 | 说明 |
|---|---|---|
| Plugin/Skill | 9/10 | 24 lifecycle hooks, registerTool API 完善, 仅需 TS/Python 桥接 |
| Memory | 7/10 | 用户记忆用 OpenClaw, 领域记忆 (paper/code) 保留 PaperBot |
| Cron | 9/10 | 大幅升级 PaperBot 的 asyncio 朴素调度器 |
| Channels | 10/10 | 从 console-only 升级到 8 渠道 (Telegram/Discord/Slack/...) |
| ACP | 7/10 | 适合交互式查询, 不适合批量 pipeline |
| Subagents | 8/10 | PaperBot 8 种 Agent 自然映射, 隔离 session 解决上下文污染 |
| 综合 | 8/10 | 推荐实施 |
Plugin 注册 6 个工具: paper_search, paper_analyze, paper_track, gen_code, review_paper, research_context
4 个 Cron Job: paper-monitor (每日), weekly-digest (每周), conference-deadlines (每日), citation-monitor (每小时)
记忆分层: OpenClaw 管 Layer 0-1 (用户 profile + session), PaperBot 管 Layer 2-3 (query-relevant + paper-scoped)