源码解析 · 第十六份 · v0.62.0

三条 Runtime
一个桌面

CodePilot 不是又一个编程 Agent——它是把 Agent 从终端搬到桌面的形态跃迁。Electron 外壳包裹 Next.js 应用,三条可替换的 Agent 引擎(Claude SDK / Native / Codex),17+ Provider,IM Bridge 远程控制。全文标注 文件:行号,可追溯到具体源码。

1448
TS/TSX 文件
3
可替换 Runtime
17+
AI Provider
5
IM Bridge 渠道

多模型 AI Agent 桌面客户端 · Electron 40 + Next.js 16 + SQLite + Claude Agent SDK + Native Runtime + Codex Runtime

commit v0.62.0 · 约 1266 个产品 TS/TSX(含 资料/ vendor 合计约 1448)· BUSL-1.1

全部标注 文件:行号,可追溯到具体源码。


Part 1

一、项目概览

1.1 CodePilot 是什么

CodePilot 不是又一个编程 Agent。它是一个多模型 AI Agent 桌面客户端——一个 Electron 外壳包裹的 Next.js 应用,把"和 AI 对话"这件事从终端搬到了桌面上,并且把"对话"扩展成了"通用 Agent 平台":

  • 17+ AI Provider(Anthropic / OpenAI / Google / xAI / GLM / Kimi / DeepSeek / Ollama / Bedrock / Vertex…)
  • 三条 Runtime(Claude Code SDK / Native 自研 / Codex app-server)
  • 远程 IM Bridge(Telegram / 飞书 / Discord / QQ / 微信)
  • MCP + Skills 生态
  • 媒体生成(Gemini 图片)
  • 任务调度(cron)
  • 会话回放(rewind 到任意 checkpoint)

它和 Claude Code 的关系:Claude Code 是终端里的编程 Agent,一行一行读你的输入,调用工具改文件。CodePilot 是桌面上的通用 Agent 客户端——它复用 Claude Code 的 SDK 作为三条 Runtime 之一,同时自研了一条 Native Runtime 用来跑非 Claude 模型,还接入了 OpenAI Codex 作为第三条 Runtime。

它和 open-design 的关系:open-design 也是"不写 Agent 主循环的宿主",但它把宿主做的是设计(生成 HTML/PPT)。CodePilot 做的是通用 Agent 桌面——聊天、编程、任务、媒体、远程控制,全都塞在一个 Electron 窗口里。

1.2 数字化的项目形状

维度 数字 说明
源码文件 约 1266 个产品 TS/TSX(含资料约 1448) 不含 node_modules;资料/ 为微信/飞书 vendor
总文件数 2522 含 JSON/MD/图片等
核心引擎 claude-client.ts 3628 行 SDK 封装 + 消息流
Native 循环 agent-loop.ts 1019 行 while 循环驱动
数据库 db.ts 5752 行 SQLite schema + 迁移
API 端点 约 170+ route handlers(约 29 个一级域) 真正跑 Agent 的是 POST /api/chat/api/chat/messages 仅持久化
Bridge 适配器 5 个 Telegram/飞书/Discord/QQ/微信
Provider 预设 17+ provider-catalog.ts 2400 行
技能目录 public/skills/ 兼容 Claude Code 格式
许可证 BUSL-1.1 商业使用受限

1.3 三条 Runtime 的本质差异

这是 CodePilot 最核心的架构决策——同一个前端,三条不同的 Agent 引擎

Runtime 引擎 主循环位置 适用场景
claude-code-sdk @anthropic-ai/claude-agent-sdk SDK 子进程内部 Claude 模型,完整 Claude Code 能力
native 自研 while 循环 + Vercel AI SDK agent-loop.ts:373 非 Claude 模型(GLM/Kimi/DeepSeek…)
codex codex app-server 子进程 Codex 内部 OpenAI Codex 模型

三条 Runtime 共享同一个前端、同一个数据库、同一个权限系统、同一个 Bridge——但主循环的实现完全不同。这是 CodePilot 最独特的架构决策:Runtime 是可替换的

1.4 与 Claude Code 的根本区别

维度 Claude Code CodePilot
界面 终端 TUI Electron 桌面 GUI
模型 锁 Anthropic 17+ Provider
Runtime 单一(自研) 三条可替换
主循环 自研 while 循环 SDK 外包 / Native 自研 / Codex 外包
工具 硬编码内置 标准 AI SDK ToolSet
权限 五关裁决 三层(规则引擎 + 弹窗 + Profile)
会话 终端会话 SQLite 持久化 + rewind
远程 IM Bridge
子 Agent Task 工具 subagent_runs durable lifecycle

Part 2

二、全景架构

2.1 进程拓扑

┌─────────────────────────────────────────────────────────┐
│  Electron 主进程 (electron/main.ts, 2566 行)             │
│  ├── 窗口管理 (BrowserWindow)                            │
│  ├── utilityProcess.fork → Next.js standalone server     │
│  ├── TerminalManager (pty)                               │
│  ├── Tray / 通知 / 文件对话框                             │
│  └── 日志轮转 (50MB×5)                                   │
└────────────────────┬────────────────────────────────────┘
                     │ HTTP/SSE (localhost)
┌────────────────────▼────────────────────────────────────┐
│  Next.js 16 Server (App Router)                          │
│  ├── src/app/api/ — 约 170+ route handlers(约 29 一级域) │
│  │   ├── /api/chat — 聊天(消息/中断/权限)              │
│  │   ├── /api/media — 图片生成                          │
│  │   ├── /api/settings — 设置                           │
│  │   ├── /api/providers — Provider 管理                 │
│  │   ├── /api/tasks — 任务调度                          │
│  │   └── …                                              │
│  ├── src/lib/ — 核心业务逻辑                             │
│  │   ├── claude-client.ts — SDK 封装(3628 行)         │
│  │   ├── agent-loop.ts — Native 循环(1019 行)         │
│  │   ├── stream-session-manager.ts — SSE 流管理         │
│  │   ├── conversation-registry.ts — 会话注册表          │
│  │   ├── db.ts — SQLite(5752 行)                      │
│  │   ├── runtime/ — Runtime 注册表 + 三实现              │
│  │   ├── tools/ — 8 个编码工具                          │
│  │   ├── builtin-tools/ — 平台工具                      │
│  │   ├── permission/ — 三层权限                         │
│  │   ├── bridge/ — IM Bridge 子系统                     │
│  │   ├── channels/ — 渠道插件                           │
│  │   ├── codex/ — Codex 集成                            │
│  │   └── harness/ — 能力契约 + 上下文编译               │
│  └── src/components/ — React 组件                        │
│      ├── chat/ — 聊天界面                               │
│      ├── ai-elements/ — AI 响应渲染                     │
│      ├── layout/ — 布局                                 │
│      └── …                                              │
└────────────────────┬────────────────────────────────────┘
                     │ SQLite (better-sqlite3, WAL)
┌────────────────────▼────────────────────────────────────┐
│  ~/.codepilot/codepilot.db                              │
│  ├── chat_sessions / messages                           │
│  ├── subagent_runs / subagent_run_events                │
│  ├── api_providers / settings / tasks                   │
│  ├── media_generations / media_tags / media_jobs        │
│  └── channel_bindings / channel_offsets                 │
└─────────────────────────────────────────────────────────┘

2.2 四条必须记牢的边界

① Electron 只做壳。 electron/main.tsutilityProcess.fork 启动打包的 Next.js standalone server(serverProcess),监听空闲端口,GET /api/bridge 健康检查通过后 mainWindow.loadURL(http://127.0.0.1:${port})。Electron 不感知任何 Agent 逻辑——它只负责窗口、终端(pty)、通知、文件对话框。

② 三条 Runtime 共享同一套基础设施。 runtime/registry.ts:72-152resolveRuntime()(文件共 198 行;L21 仅为 Map 声明)按"Codex 显式 → 显式 override → cli_enabled=false → 全局设置 → auto"五级选择 Runtime。选完后,前端、DB、权限、Bridge 全部复用。

③ SDK 路径的"主循环"是事件泵,不是真循环。 claude-client.ts:2239for await (const message of conversation) 只是把 SDK 子进程的事件翻译成 SSE。真正的"模型→工具→回灌→模型"循环发生在 SDK 子进程(claude CLI)内部。CodePilot 应用层只做反应式转发 + 护栏。

④ Native 路径才是真循环。 agent-loop.ts:373while (step < maxSteps) 每一步手动调用 streamText(),在步骤之间插入权限检查、DB 持久化、超时预算、上下文裁剪、SSE 转发——这些在 AI SDK 的自动模式里无法挂钩。

2.3 数据流:从用户输入到 AI 响应

用户输入 → MessageInput / startStream
  → POST /api/chat          ← 真正触发 Agent(chat/route.ts)
  → stream-session-manager 会话锁(chat 路径常传 600s TTL;锁默认常量另有 300s)
  → streamClaude()(claude-client.ts)解析 Provider + Runtime
  → addMessage 入库 + assembleContext(含 context-compressor / context-compiler)
  → runtime.stream() 分流:
    ├── SDK: streamClaudeSdk() → SDK query() 子进程
    ├── Native: runAgentLoop() → while (step < maxSteps) → streamText()
    └── Codex: getCodexAppServer() → app-server → thread/start → turn/start
  → SSE 流式返回 → useSSEStream → MessageList
  → db.ts 持久化到 SQLite

说明:POST /api/chat/messages 只持久化消息、不跑模型(图片生成等模式用)。

Bridge 数据流(远程 IM 控制):

Telegram/飞书消息
  → Adapter 长轮询/WebSocket 接收
  → channelRouter 路由到 CodePilot session
  → conversationEngine 调用 SDK
  → SDK SSE 响应
  → deliveryLayer 格式化 + 分片
  → Adapter 发送回 IM

Part 3

三、启动流程

3.1 Electron 主进程启动

electron/main.ts(2566 行)的启动序列:

  1. Sentry 初始化(可 opt-out)
  2. utilityProcess.fork 启动打包的 Next.js standalone server
  3. 监听空闲端口(serverProcess
  4. GET /api/bridge 健康检查
  5. SIGTERM → 3s → SIGKILL 关停
  6. mainWindow.loadURL(http://127.0.0.1:${port})
  7. TerminalManager(pty)初始化
  8. Tray / 通知 / 文件对话框 注册
  9. 日志轮转(50MB×5)

关键设计:Electron 主进程和 Next.js server 是两个独立进程,通过 HTTP/SSE 通信。Dev 模式下直接连 next dev 的 dev server;Prod 模式下用 utilityProcess.fork 启动 standalone server。

3.2 Next.js Server 启动

Next.js 16 App Router 启动时: 1. src/lib/db.ts 初始化 SQLite(WAL + 外键 + busy_timeout=5000) 2. src/lib/runtime-log.ts 初始化 console 环形缓冲(200 条,自动脱敏) 3. src/lib/bridge/bridge-manager.ts 初始化 Bridge 管理器(globalThis 单例,抗 HMR) 4. src/lib/conversation-registry.ts 初始化活跃会话注册表

3.3 数据库初始化

db.ts:186initDb()

-- 核心表
chat_sessions (id, title, model, provider_id, sdk_session_id, …)
messages (id, session_id, role, content, …)
subagent_runs (id, parent_session_id, status, …)
subagent_run_events (id, run_id, event_type, …)
settings (key, value)
tasks (id, session_id, title, status, …)
api_providers (id, name, protocol, base_url, …)

-- 媒体表
media_generations / media_tags / media_jobs / media_job_items / media_context_events

-- Bridge 表
channel_bindings / channel_offsets / permission_link / …

迁移机制withMigrationLockdb.ts:84)用 O_CREAT|O_EXCL 文件锁 + 10s 重试,防多 Next.js worker 并发迁移。migrateDbdb.ts:482)用 PRAGMA table_info 探测 + safeAddColumn 做幂等 ALTER TABLE 增量加列。


Part 4

四、输入捕获与分流

4.1 聊天输入

src/components/chat/MessageInput.tsx 捕获用户输入 → 经 startStreamPOST /api/chat(真正触发 Agent)。POST /api/chat/messages 仅持久化、不跑模型。

4.2 API 路由处理

src/app/api/chat/route.ts(990 行 POST)的完整流程:

  1. 校验 body(session_id, content, model, …)
  2. 前置检查hasCodePilotProvider(412 NEEDS_PROVIDER_SETUP)
  3. 会话锁acquireSessionLock(409 SESSION_BUSY,600s TTL + 续期/watchdog)
  4. 解析 Provider + RuntimeresolveRuntime() 五级选择
  5. 消息入库addMessage
  6. 上下文组装assembleContext(含 context-compressor)
  7. 分流到 Runtime
  8. SDK: streamClaudeSdk()
  9. Native: runAgentLoop()
  10. Codex: codex app-server
  11. SSE 流式返回
  12. 后台持久化collectStreamResponse(renderer 断开也继续)
  13. 释放锁

4.3 会话锁

session_runtime_locks 表(db.ts:891)实现会话级互斥:

  • acquireSessionLock:获取锁,600s TTL
  • isLockOwner:校验锁所有权(lockId
  • 续期/watchdog:防止锁过期导致并发问题

为什么需要锁:同一个会话不能同时有两个流在跑——否则消息顺序会乱。

4.4 中断

/api/chat/interrupt: - 显式 Stop:abort 父 turn 并向 child 传播 - stream-session-manager.ts:1046stopStreamWith:先无条件武装 2s force-abort 安全网(#578 修复),再 best-effort 调 /api/chat/interrupt


Part 5

五、上下文组装

5.1 assembleContext

上下文组装是"把用户输入 + 历史消息 + 系统提示词 + 工具描述"打包成发给模型的请求。

关键组件: - agent-system-prompt.ts:构建系统提示词 - harness/context-compiler.ts:纯函数 compileContext(input)——给定 enabled capabilities + 用户/外部扩展扫描结果,产出有序去重的 system prompt + 工具面 + artifact 契约 - context-compressor:上下文压缩

5.2 Harness 能力契约

harness/ 是 CodePilot 最独特的子系统之一——能力契约 + 上下文编译层

  • capability-contract.ts:声明所有能力(memory/widget/dashboard/tasks_and_notify/cli_tools/media_import/image_generation…)的 canonical prompt 片段与工具描述符
  • context-compiler.ts:纯函数 compileContext(input)——不 IO、不执行工具、不做权限决策,只产出编译后的上下文
  • mutation-level.ts:工具变更级别分类(safe_read / safe_write / dangerous),权限 allowlist 之源
  • builtin-event-bus.ts:工具媒体结果侧信道
  • auto-invoke-accounting.ts:上下文记账

为什么需要 Harness:三条 Runtime 需要给模型看一致的能力描述。Harness 保证 Native/SDK/Codex 的 system prompt 不漂移。

5.3 上下文压缩

当上下文超过模型窗口时,context-compressor 会触发压缩。SDK 路径的压缩在 SDK 子进程内部完成;Native 路径在 agent-loop.ts 的步骤之间做 pruneOldToolResults 裁剪。


Part 6

六、Agent 主循环

6.1 三条 Runtime 的主循环对比

这是 CodePilot 最核心的章节。三条 Runtime 的主循环实现完全不同:

SDK 路径(claude-client.ts)

claude-client.ts:2239for await (const message of conversation)

// 这不是真循环,是事件泵
for await (const message of conversation) {
  switch (message.type) {
    case 'assistant': // 提取 tool_use → SSE
    case 'user':      // tool_result / 媒体块 / TodoWrite 同步
    case 'stream_event': // 文本 delta
    case 'system':    // init 元数据
    case 'result':    // extractTokenUsage + terminal_reason
  }
}

关键点: - 真正的 Agent 循环在 SDK 子进程(claude CLI)内部 - CodePilot 只做事件转发 + 护栏 - 护栏包括:两级 idle 预算(首 token 前 10min,之后 5.5min)、PTL 压缩重试、#577 result 权威性

Native 路径(agent-loop.ts)

agent-loop.ts:373while (step < maxSteps)(默认 50):

while (step < maxSteps) {
  // 1. 清洗模型参数
  sanitizeClaudeModelOptions()
  buildAnthropicProviderOptions()

  // 2. 上下文裁剪
  pruneOldToolResults()

  // 3. 超时预算上膛
  timeoutCtl.onStepRequest()

  // 4. 单次 streamText()
  const result = streamText({ model, tools, messages, … })

  // 5. 逐事件翻译为 SSE
  for await (event of timeoutCtl.guardStream(result.fullStream)) {
    // text-delta→text, reasoning-delta→thinking,
    // tool-call→tool_use, tool-result→tool_result, …
  }

  // 6. 终止判断
  if (!hasToolCalls) break
  if (doomLoopDetected) break

  // 7. 进入下一轮
  messages += response.messages
  step++
}

关键点: - 手动循环是为了在每步之间插入权限检查、DB 持久化、超时预算、上下文裁剪 - 这些在 AI SDK 的自动模式里无法挂钩 - doom-loop 检测:同工具连续调用

Codex 路径(codex/runtime.ts)

codex/runtime.ts: - 每次对话 thread/resume|thread/start - 订阅通知 - turn/start - event-mapper.translateCodexNotification 翻成 canonical 事件再发 SSE

6.2 主循环的本质差异

维度 Claude Code CodePilot SDK CodePilot Native CodePilot Codex
循环位置 CLI 内部 SDK 子进程内部 agent-loop.ts while Codex 内部
循环驱动 自研 SDK query() 手动 while + streamText Codex turn
事件转发 无(终端直出) for await 事件泵 for await 翻译 event-mapper
护栏 idle 预算 + PTL 重试 超时预算 + doom 检测
上下文压缩 内部 SDK 内部 pruneOldToolResults Codex 内部

6.3 流管理

stream-session-manager.ts(1439 行)管理 SSE 流的生命周期:

  • startStream:创建流,fetch('/api/chat')consumeSSEStream
  • 两级 idle 预算(#635):首 token 前 10min、之后 5.5min,setInterval 检测超时即 abort
  • 完成buildFinalMessageContent → phase=completed → scheduleGC(5min 后回收)
  • 错误分支:idle 超时 / tool 超时自动重试 / 用户停止 / 一般错误
  • 停止stopStreamWith 先武装 2s force-abort 安全网,再调 /api/chat/interrupt

6.4 会话注册表

conversation-registry.ts(70 行)管理活跃 SDK 会话:

  • globalThis __activeConversationsV2__(V2 key 防 HMR 拿到旧形状 Map)
  • Map<sessionId, {query, lockId, abortController}>
  • unregisterConversation 用 lockId 门控——只有 lockId 匹配才删除,防止被取代的旧 turn 的迟到 teardown 驱逐新 turn 注册的 Query(I1 竞态)

Part 7

七、工具系统与权限

7.1 两层工具源

CodePilot 的工具系统分两层:

编码核心工具(tools/)

8 个工具:Read / Write / Edit / Bash / Glob / Grep / Skill / Agent

每个是工厂函数 createXxxTool(ctx)tools/index.ts:45 createBuiltinTools() 聚合成 AI SDK ToolSet

// 标准 Vercel AI SDK ToolSet
{
  name: {
    description: string,
    inputSchema: zodSchema,
    execute: async (input) => result
  }
}

直接喂给 streamText({ tools })

平台工具(builtin-tools/)

7 组平台工具: - notification:notify / schedule_task / list / cancel / hatch_buddy - memory-search:3 个记忆搜索工具 - dashboard:5 个 Dashboard 工具 - media:import + generate_image - widget-guidelines:Widget 指南 - session-search:会话搜索 - cli-tools:6 个 CLI 工具 - ask-user-question:用户问答

builtin-tools/index.ts:134 getBuiltinTools()always / workspace / keywords 三种 gating 条件挂载,经 harness/runtime-adapter.ts adaptForNative() 生成对应 system prompt 片段。

7.2 工具装配

agent-tools.ts:114 assembleTools()

const tools = {
  ...createBuiltinTools(ctx),        // 编码工具
  ...getBuiltinTools(ctx),           // 平台工具
  ...buildMcpToolSet(mcpServers),    // 外部 MCP 工具
}

// 权限包裹
if (permissionContext) {
  tools = wrapWithPermissions(tools, permissionContext)
}

plan 模式只保留 PERMISSION_SAFE_TOOLSagent-tools.ts:53,由 harness/mutation-level.tsmutationLevel==='safe_read' 派生,fail-safe:未声明工具默认 ask)。

7.3 与 Claude Code 工具系统的本质区别

维度 Claude Code CodePilot
工具格式 硬编码在 bundle 内 标准 AI SDK ToolSet
工具注册 配置式 permission 规则 zod schema + execute 函数
工具组合 固定 可自由组合、gating、运行时包裹
平台工具 builtin-tools(通知/记忆/Dashboard/媒体…)
MCP 集成 有(mcp-tool-adapter.buildMcpToolSet)

7.4 三层权限系统

第一层:规则引擎

permission-checker.ts:127 checkPermission

  • 三模式:explore(只读)/ normal(写放行、Bash ask)/ trust(全放行)
  • 规则数组 findLast 语义(具体规则覆盖通用)
  • glob 通配匹配命令/路径
  • 硬底线:DANGEROUS_PATTERNS(rm -rf/sudo/kill/dd…)与 ALWAYS_ASK_TOOLS(AskUserQuestion/ExitPlanMode)任何模式都必问

第二层:执行时弹窗

agent-tools.ts:236-341

  • 非 safe_read 工具 execute 前 checkPermission
  • ask 时写 DB permission_requests、发 permission_request SSE(带 HMAC approvalToken 防伪造)
  • registerPendingPermission 挂起等 /api/chat/permission 回包(5 分钟超时自动 deny)
  • 支持用户改输入(updatedInput)和"本会话不再询问"(sessionApprovals)

第三层:会话 Profile

permission/profile.ts:23

  • default:问用户
  • auto_review:交给受限 reviewer 模型审批,human-only 类别(凭据/计费/外发/高影响/交互问答)绝不代批,fail-closed
  • full_access:完全跳过

review-event.ts + review-audit.ts 统一三 Runtime 的代批审计事件。

7.5 沙箱

CodePilot 本身无 OS 沙箱: - Codex 路径依赖 codex app-server 自带沙箱 - Native 靠规则引擎 + 人工确认 - 这是与 Claude Code(Seatbelt/Landlock/seccomp)的本质区别


Part 8

八、上下文压缩与记忆

8.1 上下文压缩

  • SDK 路径:压缩在 SDK 子进程内部完成
  • Native 路径agent-loop.ts 步骤之间 pruneOldToolResults 裁剪
  • PTL 压缩重试claude-client.ts:2859-3152CONTEXT_TOO_LONG 触发自动压缩重试,ptlRetryAttempted 防死循环

8.2 SQLite 持久化

db.ts(5752 行): - WAL 模式 + 外键约束 + busy_timeout=5000 - 消息 content 为 JSON 数组 - sdk_session_id 用于 SDK 会话 resume

8.3 会话回放(rewind)

CodePilot 支持 rewind 到任意 checkpoint: - file-checkpoint.createCheckpoint 在关键节点创建快照 - 用户可以回滚到之前的 checkpoint 继续对话

8.4 记忆系统

  • memory-search:builtin-tools 中的 3 个记忆搜索工具
  • Assistant Workspace:Persona 文件、持久记忆、onboarding 流程、daily check-ins

Part 9

九、子 Agent

9.1 三种 spawn 拓扑

Runtime spawn 方式 隔离级别
CodePilot Runtime 同一 Next/Electron server 进程内独立 runAgentLoop 非 OS 子进程
Claude Runtime Agent SDK query() 起独立 OS 子进程 隔离 shadow HOME/凭据
Codex Runtime 复用 app-server 连接,新建隔离 thread/start 线程级隔离

9.2 Durable Lifecycle

subagent_runs 表实现持久化生命周期:

running → settling → completed/failed/partial/cancelled/timed_out
  • 启动 child 前先写 subagent_runs.running(写不进去就 fail-closed 不启动)
  • 结束后 settling → terminal,terminal 只原子收口一次
  • 用户任务用 logical_run_id 聚合,每次执行/重试是递增 attempt

9.3 依赖编排

workflow_id + task_key + depends_on 声明式 DAG:

  • 下游先 queued,不占并发位不调 Provider
  • resolver 轮询 durable 表等上游 terminal(30 分钟 deadline、5 秒 missing 宽限)
  • 上游 result_text<codepilot_dependency_results> 标记为"不可信数据"编译进下游 prompt
  • 拒绝自依赖/循环/占位式"等待"prompt

9.4 约束

  • depth 固定为 1:child 内硬移除 Agent/spawn 工具
  • 每父 session 并发 2
  • Claude child 5 分钟 idle + 30 分钟硬顶
  • 父 abort 向下传播

9.5 多模型 Sub-agent

subagent-models.ts 从所有 Provider resolver + runtime-compat 矩阵生成精确 providerId + modelId route。child 的 assembleToolsrunAgentLoop 都改用目标 Provider。


Part 10

十、生态

10.1 MCP 集成

  • mcp-tool-adapter.buildMcpToolSet:把外部 MCP server 的工具聚合进 ToolSet
  • 支持 stdio / sse / http 三种传输
  • 运行时监控

10.2 技能系统

  • 格式兼容 Claude Code:YAML frontmatter + Markdown body
  • 扫描路径.claude/skills.claude/commands~/.claude/skills~/.claude/commands~/.agents/skills
  • marketplace:安装/卸载 API
  • fork 模式:落到 subagent_runs durable 体系

10.3 Bridge 子系统

见第十二章"Bridge 与远程控制"。

10.4 Codex 集成

  • 直接集成codex/app-server-manager.ts:603 getCodexAppServer() 查找 codex 二进制并 spawn codex app-server(stdio JSON-RPC,缓存单例)
  • codex/proxy:本地 Responses API 代理(/api/codex/proxy/v1/responses),让 Codex CLI 引擎跑在用户自配的任意 provider 上

Part 11

十一、功能特性

11.1 Provider 管理

provider-catalog.ts(2400 行): - VENDOR_PRESETS 数组定义 17+ 厂商预设 - VendorPreset 含 protocol、authStyle、baseUrl、defaultEnvOverrides、defaultModels、roleModels、计费模型、sdkProxyOnly、claudeCodeVerified 兼容分级 - 启动时 Zod 校验全表

provider-resolver.ts(1793 行): - resolveProvider() 优先级 providerId > sessionProviderId > default_provider_id > active - 虚拟 provider(env/openai-oauth/xai-oauth/codex_account) - resolveExactProvider() fail-closed 供后台任务

11.2 任务调度

  • tasks 表存储任务
  • ensureSchedulerRunning 在首个 chat 请求时启动
  • cron 表达式或间隔调度

11.3 媒体生成

  • image-generator.ts:Gemini/Anthropic 图片生成
  • job-executor.ts:批量图片生成任务执行器
  • Gallery:图片画廊、标签、批量任务

11.4 错误分类与诊断

error-classifier.ts(589 行): - 28 类结构化错误 - ERROR_PATTERNS 每类定义 patterns + errno codes + userMessage + actionHint + retryable - PROCESS_CRASH 含 session 关键词时重分类为 SESSION_STATE_ERROR

provider-doctor.ts(1093 行): - 5 个快探针并行(CLI 安装/版本、凭据/auth、默认 provider/模型、特性兼容性、DNS/连通性) - 第 6 个 live probe:真实 spawn CLI 发最小请求 - 修复动作:REPAIR_ACTIONS + computeRepairs


Part 12

十二、Bridge 与远程控制

12.1 Bridge 架构

bridge-manager.ts(1371 行,globalThis 单例)编排四大模块:

模块 职责
channel-adapter.ts BaseChannelAdapter 抽象基类 + 工厂注册表
channel-router.ts 消息路由(IM → session)
conversation-engine.ts 消费 SSE 流、保存消息
delivery-layer.ts 出站可靠投递

12.2 渠道适配器

5 个适配器: - telegram:长轮询 + draft 流式预览 - discord:Discord.js - qq:QQ Bot - weixin:微信(auth/media/session-guard) - feishu:飞书(WS 长连接 + 卡片流式)

12.3 权限转发

permission-broker.ts

  1. full_access profile 且非 human-only 工具 → 直接 resolvePendingPermission(allow)
  2. 30 秒去重窗口防重复卡片
  3. 按渠道能力分流:无按钮渠道(QQ/微信)拒绝 AskUserQuestion
  4. 发出带 inlineButtons 的卡片,写 permission_link
  5. 用户点按钮 → handlePermissionCallback()resolvePendingPermission() 解开阻塞的流

12.4 Markdown → IR → 渠道格式

三段式管线: 1. ir.ts(829 行):markdown-it token 流 → MarkdownIR = {text, styles[], links[]} 2. render.ts:通用算法——按 span 边界切分文本、按 STYLE_ORDER 排序嵌套样式 3. telegram.ts/discord.ts/feishu.ts:只提供 styleMarkers + escapeText + buildLink 配置


Part 13

十三、设计哲学与取舍

13.1 为什么做三条 Runtime

CodePilot 最核心的架构决策是Runtime 可替换。为什么?

  • Claude Code SDK:Claude 模型的最佳体验,但锁 Anthropic
  • Native:非 Claude 模型需要自己的循环,但不能复制 Claude Code 的全部能力
  • Codex:OpenAI 模型需要自己的引擎

三条 Runtime 共享前端、DB、权限、Bridge——但主循环完全不同。这是"产品定位决定技术形态"的典型案例。

13.2 为什么用 Electron + Next.js

  • Electron:跨平台桌面(macOS/Windows/Linux)、系统能力(pty/通知/文件对话框)
  • Next.js:App Router 同时做前端和 API 层、SSR/SSG、React 19 生态
  • utilityProcess.fork:Next.js server 跑在独立进程,主进程只做壳

13.3 为什么用 SQLite

  • better-sqlite3:同步 API、WAL 模式、零配置
  • 本地优先:所有数据在 ~/.codepilot/,不依赖云端
  • 会话回放:SQLite 持久化支持 rewind

13.4 与 Claude Code 的取舍对比

维度 Claude Code CodePilot 取舍理由
界面 终端 TUI Electron GUI 桌面用户 vs 终端用户
模型 锁 Anthropic 17+ Provider 开放性 vs 深度优化
Runtime 单一 三条可替换 灵活性 vs 复杂度
工具 硬编码 标准 ToolSet 可组合性 vs 一致性
权限 五关裁决 三层 GUI 弹窗 vs 终端交互
会话 终端会话 SQLite + rewind 持久化 vs 轻量
远程 IM Bridge 移动场景 vs 桌面场景
子 Agent Task 工具 durable lifecycle 可靠性 vs 简单性

13.5 三个最独特设计

  1. Runtime 可替换:同一个前端,三条不同的 Agent 引擎。这是 CodePilot 与所有其他 Agent 客户端的本质区别。

  2. Harness 能力契约harness/context-compiler.ts 是纯函数编译层,保证三条 Runtime 给模型看的能力描述不漂移。这是"统一基础设施"和"模块独立演进"之间的精妙平衡。

  3. Bridge 权限转发permission-broker.ts 把权限请求转成 IM 内联按钮,打破"Claude 流阻塞等权限"的死锁。这是"桌面 Agent 如何远程控制"的关键创新。


Part 14

横向对比:CodePilot 在十三家之外的位置

CodePilot 是本仓库分析的第十六个项目。它与前面十五家的关系不是"又一个编程 Agent",而是"把 Agent 从终端搬到桌面"的形态跃迁。

与 Claude Code 的对比(最直接的对标)

维度 Claude Code CodePilot 差异本质
产品定位 终端编程 Agent 桌面通用 Agent 客户端 终端 vs 桌面
界面 Ink TUI Electron + React 终端 vs GUI
模型 锁 Anthropic 17+ Provider 封闭 vs 开放
Runtime 单一自研 三条可替换 统一 vs 灵活
主循环 自研 while 循环 SDK 外包 / Native 自研 / Codex 外包 自主 vs 混合
工具 硬编码 43 个 标准 ToolSet 8+7 组 私有 vs 标准
权限 五关裁决 三层(规则+弹窗+Profile) 终端 vs GUI
会话 终端会话 SQLite + rewind 临时 vs 持久
远程 IM Bridge 5 渠道 无 vs 有
子 Agent Task 工具 durable lifecycle 简单 vs 可靠
沙箱 Seatbelt/Landlock/seccomp 无(Codex 自带) 有 vs 无

与 open-design 的对比(同为"宿主"形态)

维度 open-design CodePilot 差异本质
宿主对象 25 个 CLI Agent 3 条 Runtime 数量 vs 深度
核心创新 适配器即数据 Runtime 可替换 广度 vs 深度
主循环 不写(外包给 CLI) 三条(外包+自研+外包) 纯宿主 vs 混合
内容 设计系统/品牌/模板 聊天/任务/媒体/远程 设计 vs 通用
输出 HTML/PPT/PDF 对话/代码/图片 文档 vs 交互
权限 无(CLI 自带) 三层 无 vs 有

与 openworker 的对比(同为"通用任务同事")

维度 openworker CodePilot 差异本质
定位 无人值守同事 桌面 Agent 客户端 后台 vs 前台
交互 Slack/邮件 桌面 GUI + IM Bridge 异步 vs 同步
模型 provider 无关 17+ Provider 类似
工具 40 个连接器 8+7 组 + MCP 连接器 vs 标准
权限 五档模式 三层 类似
持久化 SQLite SQLite 相同

与 Codex 的对比(第三条 Runtime 的来源)

维度 OpenAI Codex CLI CodePilot Codex Runtime 差异本质
形态 终端 CLI 桌面 GUI 内嵌 独立 vs 集成
模型 锁 OpenAI 锁 OpenAI(但通过 proxy 可跑任意 provider) 封闭 vs 半开放
主循环 run_turn 两层循环 Codex 内部 相同
沙箱 三平台 OS 沙箱 Codex 自带 相同
权限 AskForApproval × 沙箱 复用 CodePilot 三层 独立 vs 复用

CodePilot 的独特贡献

CodePilot 给 Agent 工程带来了三个前面十五家都没有的设计:

  1. Runtime 可替换:同一个前端,三条不同的 Agent 引擎。这不是"多 Provider"(那是模型层面的开放),而是"多引擎"(循环层面的开放)。

  2. Harness 能力契约harness/context-compiler.ts 是纯函数编译层,保证三条 Runtime 给模型看的能力描述不漂移。这是"统一基础设施"和"模块独立演进"之间的精妙平衡。

  3. Bridge 权限转发permission-broker.ts 把权限请求转成 IM 内联按钮,打破"Claude 流阻塞等权限"的死锁。这是"桌面 Agent 如何远程控制"的关键创新。


Part 15

附录:关键文件索引

文件 行数 说明
src/lib/claude-client.ts 3628 SDK 封装 + 消息流
src/lib/agent-loop.ts 1019 Native 循环
src/lib/db.ts 5752 SQLite schema + 迁移
src/lib/stream-session-manager.ts 1439 SSE 流管理
src/lib/conversation-registry.ts 70 会话注册表
src/lib/error-classifier.ts 589 错误分类
src/lib/provider-doctor.ts 1093 Provider 诊断
src/lib/runtime/registry.ts 198(resolveRuntime @72-152) Runtime 注册表
src/lib/tools/index.ts 67(createBuiltinTools @45) 工具聚合
src/lib/builtin-tools/index.ts 351(getBuiltinTools @134) 平台工具
src/lib/permission-checker.ts checkPermission @127;文件总行数以仓库为准) 权限规则引擎
src/lib/bridge/bridge-manager.ts 1371 Bridge 管理器
src/lib/harness/context-compiler.ts 上下文编译
src/lib/codex/app-server-manager.ts 769(getCodexAppServer @603) Codex 集成
src/lib/provider-catalog.ts 2400 Provider 预设
src/lib/provider-resolver.ts 1793 Provider 解析
electron/main.ts 2566 Electron 主进程
src/app/api/chat/route.ts 990 聊天 API

本文基于 CodePilot v0.62.0 源码逐文件分析写成。所有 文件:行号 引用已用 grep -n / sed -n 回验。