Appearance
扩展系统
扩展是 Pi 应用层的核心扩展点。实现在 packages/coding-agent/src/core/extensions/,主要文件是 types.ts(接口与事件)和 runner.ts(派发)。
组成
text
ResourceLoader 负责发现和加载扩展
ExtensionRunner 由 AgentSession 持有,reload 时整体替换
ExtensionContext 是每个 handler 收到的上下文AgentSession._buildRuntime() 创建 runner,并通过 _bindExtensionCore() 把 AgentSession 的能力暴露给扩展。
扩展拿到的上下文
ExtensionContext 提供:
| 能力 | 说明 |
|---|---|
ui | ExtensionUIContext,对话框、状态、自定义组件、编辑器、主题 |
mode / hasUI | 运行模式(tui/rpc/json/print)和是否有对话框能力 |
cwd / sessionManager / modelRegistry | 环境信息 |
model / scopedModels / thinkingLevel | 当前选择 |
isIdle() / hasPendingMessages() | 运行状态 |
isProjectTrusted() | 项目信任状态 |
signal / abort() | 当前 run 的取消能力 |
shutdown() | 退出 Pi |
getContextUsage() / compact() / getSystemPrompt() | 上下文管理 |
工具执行时拿到的是 ExtensionToolContext,在基础上下文上加:
tools:可调用的工具列表executeTool(name, args, options):走完整校验和钩子的嵌套调用
命令 handler 拿到的是 ExtensionCommandContext,额外提供 waitForIdle()、newSession()、fork()、navigateTree()、switchSession()、reload()、getSystemPromptOptions()。会话被替换后,withSession() 回调收到的是绑定新会话的 ReplacedSessionContext。
事件分类
| 类别 | 事件 |
|---|---|
| 会话生命周期 | session_start、session_shutdown、session_before_switch、session_before_fork、session_before_compact、session_before_tree |
| run/turn | agent_start、agent_end、turn_start、turn_end、agent_before_settle、agent_settled |
| 消息 | message_start、message_update、message_end、before_agent_start |
| 工具 | tool_call、tool_result、tool_execution_start、tool_execution_update、tool_execution_end |
| 选择 | model_select、thinking_level_select |
| 输入 | input、user_bash |
| UI/进度 | ui_prompt_start、ui_prompt_end、bash_execution_update |
| 资源 | resources_discover |
派发语义
runner 用 snapshotEventHandlers() 按加载顺序遍历所有 handler。handler 抛错不会中断 run:错误通过 emitError 报告(带扩展路径、事件名、错误、堆栈),然后继续下一个 handler。
不同事件的返回值语义不同:
| 方法 | 语义 |
|---|---|
emit() | 通用派发;session_before_* 的 cancel 会立即短路 |
emitBeforeAgentStart() | 收集自定义消息;systemPrompt 覆盖会记录到 forceSystemPrompt |
emitMessageEnd() | handler 可以替换消息,但必须保持原 role |
emitToolCall() | 第一个 block: true 生效;改参数靠原地修改 event.input |
emitToolResult() | 逐字段合并;替换 content 时 structuredContent 默认丢弃 |
emitBoundary() | 收集 drafts 和 continue,每个 handler 之后重建一次预览上下文 |
emitResourcesDiscover() | 收集 skill / prompt / theme 路径 |
tool_call 是例外:handler 抛错会向外冒泡,变成阻止工具执行的错误。
边界事件
turn_end 和 agent_before_settle 共享 BoundaryState:
ts
{
entries: SessionBoundaryDraft[];
continue: boolean;
context: BoundaryContextPreview;
outcome: "completed" | "aborted" | "error";
}draft 类型:
| draft | 效果 |
|---|---|
custom | 追加不参与 LLM 上下文的 entry |
custom_message | 追加参与上下文的 custom 消息 |
context_edit | 替换或省略早期 entry 的内容 |
compaction | 追加 compaction entry |
AgentSession._buildBoundaryContext() 负责判断 canContinue:模型上下文必须存在且最后一条不是 assistant,或者有待投递的自定义消息/队列消息。agent_before_settle 更严格:只有最后一条是 assistant 且队列非空时才允许继续。
如果 handler 返回了非法 drafts,runner 会丢弃这一轮所有 drafts,并把 continue 置为 false。
注册型 API
扩展还能注册各种静态资源:
| API | 作用 |
|---|---|
registerTool(tool) | 注册工具,进入工具注册表 |
registerCommand(name, options) | 注册 /command,在 prompt() 入口优先执行 |
registerShortcut(shortcut, options) | 注册键盘快捷键 |
registerFlag(name, options) / getFlag(name) | 注册和读取 CLI flag |
registerMessageRenderer(customType, fn) | 自定义 custom 消息渲染 |
registerEntryRenderer(customType, fn) | 自定义非上下文 entry 渲染 |
registerMarkdownTransformer(fn) | 在渲染前转换 Markdown |
registerToolRenderer(resolver) | 决定工具调用如何渲染 |
registerProvider / registerVirtualModel 等 | 通过绑定的 core 能力扩展模型目录 |
主动动作
text
ctx.sendMessage(msg, { triggerTurn, deliverAs })
ctx.sendUserMessage(content, { deliverAs })
ctx.appendEntry(customType, data)
ctx.setSessionName / getSessionName / setLabel
ctx.executeTool(name, args)deliverAs 有 steer、followUp、nextTurn 三种,分别对应运行中插入、结束后执行、随下一条用户消息一起发送。
建议阅读顺序
packages/coding-agent/src/core/extensions/types.tsExtensionContext/ExtensionToolContext/ExtensionCommandContext- 事件类型和对应的 Result 类型
ToolDefinition
packages/coding-agent/src/core/extensions/runner.tsemit()/emitBoundary()/emitBeforeAgentStart()emitMessageEnd()/emitToolCall()/emitToolResult()bindCore()暴露的能力
packages/coding-agent/src/core/agent-session.ts_bindExtensionCore()_emitExtensionEvent()_dispatchTurnEndBoundary()/_runBeforeSettleBoundary()