01
协议先行:所有模块间通过精确定义的 protocol 类型交互,核心数据结构以 Canonical(标准化)形态贯穿全链路。
依赖注入:关键模块通过工厂函数创建,外部依赖以参数注入,测试可通过替换依赖实现端到端模拟。
热重载:配置文件变更通过 PilotConfigStore 监听 → 分类 → 传播,无需重启进程即可生效。
02

03
src/cli/pilotdeck.ts是系统入口,支持四个命令:server:启动完整服务模式,初始化所有子系统
tui :启动终端交互界面
cron:管理定时任务(list/create/delete/stop)
skills:迁移技能文件
buildAlwaysOn→createAlwaysOnManager:构建Always-On后台 Agent buildCron→createCronRuntime:构建定时任务运行时
src/cli/createLocalGateway.ts是系统最核心的组装文件(1158 行),职责是:ProjectRuntimeRegistry为每个项目构建完整的运行时栈:loadPilotConfig→ 配置快照createModelRuntime→ 模型运行时createRouterRuntime→ 路由运行时PluginRuntime→ 插件运行时createBuiltinRegistry→ 内置工具注册BackgroundTaskRuntime→ 后台任务运行时DefaultContextRuntime→ 上下文运行时McpRuntime→ MCP 运行时(延迟初始化)EdgeClawMemoryProvider→ 记忆系统(可选)
04
Gateway接口定义了系统对外暴露的核心操作:submitTurn | |
abortTurn | |
listSessions | |
newSession | |
closeSession | |
respondElicitation | |
permissionDecide | |
cronCreate/cronList/cronDelete/cronStop | |
readSessionMessages | |
listProjects/describeProject | |
reloadConfig |
InProcessGateway是 Gateway 协议的生产实现。核心机制:事件泵:submitTurn内部启动后台协程将 Agent 事件推入AsyncQueue,消费者通过for await迭代读取
事件重放:维护activeTurnReplays缓冲区(最多 500 条事件 / 256KB),支持 Web UI 后连接时回放
Elicitation 总线:GatewayElicitationBus实现askUserQuestion
Permission 总线:GatewayPermissionBus实现 Web UI 权限交互
SessionRouter管理会话实例的生命周期:会话缓存:Map<sessionKey, SessionRecord>维护活跃会话
空闲回收:默认 30 分钟空闲后自动驱逐(可配置)
并发控制:inFlightTurns确保每个会话同时只有一个活跃轮次
脏标记重建:markAllDirty标记所有会话需要在下次访问时重建(配置变更后触发)
项目忙碌检测:hasActiveUserTurn检测项目中是否有用户发起的活跃轮次(排除 always-on/cron)
05
AgentSession(会话生命周期管理)└── TurnRunner(轮次输入处理 + 抄本写入)└── AgentLoop(模型调用 + 工具执行循环)
管理会话状态(messages、usage、permissionDenials)
分发 Lifecycle 钩子(SessionStart、Setup、SessionEnd)
管理 AbortController
提供状态快照(用于运行时重建)
处理用户输入(TurnInputProcessor.accept)
写入抄本(transcript.recordAcceptedInput)
分发UserPromptSubmit钩子
调用AgentLoop.run执行核心循环
记录轮次结果
invalid_tool_arguments | jsonSelfCorrect开启) |
prompt_too_long | |
max_output_reached | maxOutputTokens重试(单次,上限 64000) |
SubAgentSession实现代理分叉机制:从父代理继承配置(parentConfig),递增subagentDepth
默认最大深度为 1(只允许一级分叉)
构建分叉消息(buildForkedMessages),子代理仅看到指令文本
独立运行完整的 AgentLoop,返回 Markdown 报告
支持超时控制(composeAbortSignal组合父信号和超时信号)
抄本通过subagentTranscriptResolver写入独立的{subagentId}.jsonl文件
default | |
plan | |
acceptEdits | |
bypassPermissions | |
dontAsk |
06
ModelRuntime是模型调用的底层抽象,支持两种协议:Anthropic:Anthropic Messages API
OpenAI:OpenAI Chat Completions API
stream(request)→AsyncIterable<CanonicalModelEvent>:流式调用complete(request)→CanonicalModelResponse:同步调用getCapabilities(provider, model)→ 模型能力查询getMultimodal(provider, model)→ 多模态约束查询
CanonicalMessage:消息(role + content blocks)CanonicalToolCall:工具调用CanonicalToolResultBlock:工具结果CanonicalToolResultReferenceBloc:磁盘引用的工具结果(大文件优化)CanonicalModelRequest:模型请求CanonicalModelEvent:模型事件流(text_delta、thinking_delta、error 等)
RouterRuntime是模型调用的核心路由器,实现了decide/execute 分离:替换系统提示为编排者角色提示
限制工具白名单为agent, read_file, grep, glob, read_skill
主代理仅负责规划和分派子任务
子代理继承完整工具权限执行具体操作
支持通过skillExtensionId加载技能模板
07
DefaultContextRuntime是上下文管理的完整实现,组合以下子系统:指令发现(InstructionDiscovery):按层级发现 PILOTDECK.md 文件(全局 → 用户 → 项目 → 本地)
扩展指令(ExtensionResolver):从插件中提取 MCP 指令
记忆附件(MemoryAttachmentBuilder):从 EdgeClaw 记忆系统中检索相关上下文
将大型工具结果替换为磁盘引用
保持 tool_call/tool_result 的配对完整性
跟踪当前 token 使用量
提供预算快照(ratio、state、tokens、maxContextTokens)
MicroCompactionEngine | ||
CachedMicroCompactionEngine | ||
SnipEngine | ||
CompactionEngine |
当 token 使用率达到 80% 时发出警告
当 token 使用率达到 95% 时触发压缩
优先使用微压缩,渐进升级到全量压缩
ContextOverflowRecovery处理prompt_too_long错误:首次截断:保持尾部 50% 消息
二次截断:保持尾部 25% 消息
每个轮次仅触发一次截断重试
08
ToolRegistry管理所有可用工具的注册和查找:内置工具(createBuiltinRegistry):read_file、write_file、edit_file、bash、glob、grep、agent、web_fetch、web_search、ask_user_question、mcp_tool、plan_mode、todo_write 等 22 个
MCP 工具:通过createMcpToolDefinitionsFromRuntime从 MCP 运行时动态注册
Always-On 工具:discovery_plan、discovery_report、discovery_workspace
Cron 工具:task_create、task_list、task_output、task_stop
ToolRuntime是工具执行的统一入口,执行流程:工具查找:从 ToolRegistry 获取工具定义 输入校验: validateToolInput验证 JSON SchemaPreToolUse 钩子:分发生命周期钩子,检查权限决策 权限检查: PermissionRuntime.decide评估权限规则执行工具:调用工具实现函数 PostToolUse 钩子:分发后置钩子 结果限制: 截断超大结果applyResultSizeLimit
ConcurrentToolScheduler:并发执行所有工具调用(默认)
SequentialToolScheduler:顺序执行工具调用
PilotDeckReadFileStateMap:追踪文件读取状态,检测文件变更PilotDeckWriteSnapshotMap:记录写入快照,用于编辑冲突检测FileHistoryStore:管理文件编辑历史,支持按轮次回滚
09
~/.pilotdeck/├── pilotdeck.yaml # 全局配置├── projects/│ └── {projectId}/│ ├── .cwd # 项目根路径标记│ └── chats/│ ├── {sessionId}.jsonl # 会话抄本│ ├── {sessionId}.lite # 会话摘要│ └── subagents/│ └── {subagentId}.jsonl # 子代理抄本├── plugins/ # 全局插件├── skills/ # 全局技能├── memory/ # EdgeClaw 记忆数据├── router/│ ├── events.jsonl # 路由事件日志│ └── stats.json # Token 统计└── permissions.json # 权限设置
JsonlTranscriptWriter按行写入 JSONL 文件,每条记录包含:accepted_input:用户输入durable_message:持久化消息(助手消息、工具结果)turn_result:轮次结果subagent_started/completed:子代理生命周期control_boundary:压缩边界标记
resumeAgentSession通过以下步骤恢复会话:读取 JSONL 抄本文件 通过 TranscriptReplay回放条目重建消息列表定位最后一个压缩边界( findLastCompactBoundaryIndex)使用压缩后的消息 + 后续条目构建初始状态 创建新的 AgentSession 并注入恢复的状态
10
PluginRuntime管理插件的生命周期:HookContribution | |
McpContribution | |
ToolContribution | |
CommandContribution | |
PromptContribution | |
PermissionRuleContribution | |
RouterContribution |
HookRuntime管理钩子的匹配和执行:CommandHookExecutor:执行外部命令
PromptHookExecutor:注入提示文本
HttpHookExecutor:调用 HTTP 端点
AgentHookExecutor:调用代理执行
CallbackHookExecutor:调用回调函数
SessionStart/SessionEnd:会话开始和结束Setup:初始化设置UserPromptSubmit:用户提交输入PreModelRequest:模型调用前Stop:Agent 停止时StopFailure:Agent 失败时PreToolUse/PostToolUse:工具执行前后SubagentStart/SubagentStop:子代理生命周期ConfigChange:配置变更
McpRuntime管理 MCP(Model Context Protocol)服务器:项目级共享 MCP:所有会话共享,延迟初始化(ensureMcpReady)
会话级隔离 MCP:标记perSession: true的服务器为每个会话创建独立实例
工具桥接:createMcpToolDefinitionsFromRuntime将 MCP 工具转换为ToolRegistry中的标准工具
资源暴露:list_mcp_resources和read_mcp_resource工具提供 MCP 资源访问
Unicode 清理:sanitizeUnicodeString清理 MCP 通信中的非法字符
指令注入:插件贡献的mcpServers.*.instructions注入到系统提示
SkillManager管理存储在~/.pilotdeck/skills/中的技能文件:支持 CRUD 操作(list/read/write/create/delete)
支持导入和验证(import/validate/scan)
技能文件为 Markdown 格式,通过read_skill工具在运行时被代理读取
11
AlwaysOnManager:顶层管理器,绑定 Gateway,协调各子系统
DiscoveryScheduler:定时触发发现任务
SignalWatcher:监听文件系统变更信号
DiscoveryFire:执行发现-计划-执行-报告的完整工作流
ChannelLeaseRegistry:管理通道租约,避免冲突
信号检测 → DiscoveryGates 评估 → DiscoveryFire│┌───────────┼───────────┐▼ ▼ ▼DiscoveryWorkspaceExecution(发现变更) (确定范围) (执行变更)│ │ │└───────────┼───────────┘▼Report (生成报告)│▼Apply (应用变更)
evaluateAlwaysOnDiscoveryGates评估以下条件:是否启用
是否有活跃的用户轮次(agent_busy门控)
距离上次执行的最小间隔
每日执行次数限制
GitWorktreeProvider:通过 Git worktree 创建隔离工作目录
SnapshotCopyProvider:通过文件快照创建工作副本
SessionConfigOverrides允许 Always-On 会话使用与普通会话不同的配置(如不同的权限模式、工作目录等)。12
CronRuntime管理定时任务的生命周期:调度器:computeNextCronRunAt支持 cron 表达式和一次性执行
存储:CronTaskStore持久化任务定义和执行历史
执行:CronFire通过 Gateway 创建会话并执行预设消息
管理工具:task_create、task_list、task_output、task_stop
13
Channel:管理连接生命周期和消息收发
SessionMapper:将外部用户/群组映射到 PilotDeck 会话键
Renderer:将 GatewayEvent 渲染为通道特定的展示格式
14
~/.pilotdeck/pilotdeck.yaml # 全局配置{project}/.pilotdeck/pilotdeck.yaml # 项目配置(覆盖全局)环境变量 # 环境变量覆盖
PilotConfig包含以下顶级字段:agent | PilotAgentConfig | |
model | ModelConfig | |
extension | PilotExtensionConfig | |
memory | PilotMemoryConfig | |
gateway | PilotGatewayConfig | |
adapters | PilotAdaptersConfig | |
router | RouterConfig | |
alwaysOn | AlwaysOnConfig | |
cron | CronConfig | |
tools | PilotToolsConfig |
PilotConfigStore通过文件系统监听实现热重载:将变更分为:runtime-live:立即生效(如 apiKey)next-request:下一请求生效(如模型参数)next-runtime:需重建运行时(如路由配置)restart-required:需重启进程
subscribe通知所有订阅者registry.invalidate()+router.markAllDirty()15
捕获:captureTurn在每轮结束时将对话快照写入 EdgeClaw
检索:retrieve在prepareForModel阶段根据当前上下文检索相关记忆
维护:scheduleMemoryMaintenance在轮次完成后触发后台维护
PilotMemoryConfig支持:captureStrategy:捕获策略(last_turn / full_session)includeAssistant:是否包含助手消息retrievalTimeoutMs:检索超时schedule:自动索引和自动 Dream 的调度配置
16
框架:React 19 + TypeScript
构建:Vite
样式:Tailwind CSS
包管理:pnpm workspace(ui/ 子项目)
ui/server/提供独立的 Express 服务,负责:静态资源托管
WebSocket 代理到 Gateway
API 路由桥接
17
Builder 阶段:安装依赖、编译 edgeclaw-memory-core、构建 TypeScript、构建 UI
Runtime 阶段:仅包含运行时依赖,启动 Gateway + UI 服务
docker-entrypoint.sh职责:创建目录结构 从环境变量生成配置文件(若未挂载) 配置代理 通过concurrently启动 Gateway 和 UI 服务
pilotdeck server | ||
pilotdeck tui | ||
pilotdeck <message> |
18
用户消息→ ChannelAdapter.start()→ Gateway.submitTurn(input)→ InProcessGateway→ refreshConfigBeforeTurn()→ SessionRouter.beginTurn()→ SessionRouter.getOrCreate()→ ProjectRuntimeRegistry.createSession()→ createAgentSessionWithStorage()→ ToolRuntime + ToolRegistry + Scheduler→ AgentLoop(config, dependencies)→ TurnRunner(loop, transcript)→ AgentSession(turnRunner)→ AgentSession.submit(input)→ TurnRunner.run()→ TurnInputProcessor.accept()→ TranscriptWriter.recordAcceptedInput()→ Lifecycle.dispatch("UserPromptSubmit")→ AgentLoop.run()→ ContextRuntime.tryAutoCompact()→ createModelRequest()→ ContextRuntime.prepareForModel()→ InstructionDiscovery → PromptAssembler→ MemoryResolver → MemoryAttachmentBuilder→ MessageProjector → ToolResultBudget→ RouterRuntime.decide()→ CustomRouter → Scenario → TokenSaver → Orchestration→ RouterRuntime.execute()→ Fallback → ZeroUsageRetry → TransientRetry→ ModelRuntime.stream()→ ProviderAdapter → HTTP → Stream normalization→ assembleAssistantMessage()→ collectToolCalls()→ executeToolsWithEventPump()→ ToolRuntime.execute() [per tool]→ validateToolInput()→ Lifecycle.dispatch("PreToolUse")→ PermissionRuntime.decide()→ Tool implementation→ Lifecycle.dispatch("PostToolUse")→ projectToolResults()→ ContextRuntime.applyToolResults()→ check termination / continue loop→ Lifecycle.dispatch("SessionEnd")→ mapAgentEvent() → GatewayEvent→ AsyncQueue → yield to consumer→ ChannelAdapter renders event
pilotdeck.yaml 文件变更→ fs.watch / polling→ PilotConfigStore.reload()→ parseYaml + classifyChanges()→ subscribe callback→ registry.invalidate() → 销毁旧运行时→ router.markAllDirty("config_changed")→ AlwaysOnManager/CronRuntime 重建(如涉及)→ broadcastNotification("config_changed")
19
pilotdeck.ts (CLI入口)├── adapters/ (通道适配器)│ └── gateway (通过 Gateway 协议)├── cli/createLocalGateway.ts (组装核心)│ ├── pilot/config (配置加载)│ ├── gateway/ (网关层)│ │ └── SessionRouter│ ├── agent/ (Agent 执行)│ │ ├── AgentSession│ │ ├── TurnRunner│ │ └── AgentLoop│ │ ├── router/ (路由层)│ │ │ └── RouterRuntime│ │ │ └── model/ (模型层)│ │ │ └── ModelRuntime│ │ ├── tool/ (工具层)│ │ │ ├── ToolRuntime│ │ │ ├── ToolRegistry│ │ │ └── ToolScheduler│ │ ├── context/ (上下文管理)│ │ │ └── DefaultContextRuntime│ │ ├── lifecycle/ (生命周期钩子)│ │ │ └── LifecycleRuntime│ │ │ └── HookRuntime│ │ └── permission/ (权限系统)│ │ └── PermissionRuntime│ ├── extension/ (扩展系统)│ │ ├── PluginRuntime│ │ └── SkillManager│ ├── mcp/ (MCP 集成)│ │ └── McpRuntime│ ├── session/ (会话持久化)│ │ └── ProjectSessionStorage│ ├── task/ (后台任务)│ │ └── BackgroundTaskRuntime│ ├── always-on/ (Always-On)│ │ └── AlwaysOnManager│ └── cron/ (定时任务)│ └── CronRuntime└── cli/pilotdeckServer.ts (HTTP 服务)
20
Gateway 中心化:所有外部交互通过 Gateway 协议统一入口,实现了通道无关性和执行解耦。
三层执行模型:AgentSession(状态)/ TurnRunner(转录)/ AgentLoop(推理),每层职责单一,边界清晰。
decide/execute 分离:路由器将决策和执行分为两步,允许在决策后插入后处理(如针对小窗口模型的二次压缩)。
Canonical 统一格式:全链路使用标准化的消息格式,模型适配器负责协议转换,上层模块无需感知供应商差异。
渐进式压缩:微压缩 → 裁剪 → 全量摘要的三级压缩策略,兼顾性能和质量。
插件化扩展:通过 PluginRuntime 的贡献点机制,插件可以注入钩子、工具、MCP 服务器、权限规则和路由逻辑。
热重载友好:配置变更通过分类传播机制实现最小化重建,脏标记机制确保会话在下次访问时重建。
子代理隔离:通过深度限制、超时控制和独立抄本,实现安全可控的代理分叉。
Always-On 自主执行:通过门控机制和工作空间隔离,实现安全的后台自主代码变更。
多通道统一:20+ 通道适配器通过统一的 ChannelAdapter 协议和 SessionMapper 映射,实现一致的 Agent 体验。
夜雨聆风