Monorepo 分层与工程理念
复核基线:Pi
main的提交8ce69e9d2b171d173fe4b6b2b6256f1f4411e69d。 本文区分当前 coding-agent 主路径、agent 包中的AgentHarness与独立的pi-durableHarness。
完成本阶段后,需要能够:
- 解释 Pi 是什么,以及它与普通聊天 CLI 的区别
- 画出 monorepo 的包级依赖图
- 说明
ai、agent、coding-agent、tui的职责边界 - 追踪 CLI 从入口到
AgentSession创建完成的启动链 - 解释 minimal core、extension-first 和事件驱动 UI
- 区分产品设计理念与仓库开发规范
英文原文:
README.mdpackages/coding-agent/README.mdAGENTS.md- 根目录及各 package 的
package.json
中文整理:资料待整理。
中文整理用于学习,发生差异时以仓库英文原文为准。
一、Pi 到底是什么
Section titled “一、Pi 到底是什么”当前 coding-agent README 的定位是“minimal, extensible AI agent for the terminal”,而不是将一套固定工作流写死的 AI 编程产品。
这个定位直接来自
packages/coding-agent/README.md。
从运行时工程的角度,harness 可以理解为:
用工具、状态、事件、会话和扩展边界,组织概率性的模型输出。
这是本文的工程化理解,不是仓库对 harness 的逐字定义。 其中也有两套不能混为一谈的 Session 路径:
- 当前 coding-agent 主路径使用
AgentSession + SessionManager,将会话保存为 JSONL Tree; packages/agent/src/harness保留 AgentHarness 实现;当前还新增packages/durable/src/harness的 Harness,两者都需与 coding-agent 主路径分开阅读。
Pi 同时提供两类能力:
- 通用底层能力:
- 多 Provider 模型访问
- Agent Loop
- Tool Calling
- 事件流
- Session
- coding-agent 产品能力:
- CLI 和 TUI
- 文件与 Shell 工具
- 项目上下文
- Skills 和 Extensions
- 会话分支与压缩
- Print、JSON、RPC、SDK 等运行模式
因此,不能把 Pi 简化成:
CLI + 一个很长的 System Prompt + 模型 API更准确的模型是:
产品表面 + 产品编排 + Agent Runtime + 模型协议适配 + 持久化与扩展二、Monorepo 包级架构
Section titled “二、Monorepo 包级架构”当前根 README 列出 chord、telemetry、ai、durable、agent、coding-agent、tui。client、protocol、server 与 session-backends/sqlite-node 仍存在于 workspace/build 链;根 README 的展示清单并非全部目录。
flowchart LR
Coding["coding-agent 产品"] --> Agent["agent 执行引擎"]
Coding --> AI["ai 模型协议"]
Coding --> TUI["tui 终端组件"]
Agent --> AI这张图只画本系列追踪的主要依赖。完整依赖以各包的 package.json 为准;chord 提供应用组合运行时,durable 是新的持久化 conversation/task/document 运行时,不能因为它们存在就认定 CLI 已切换到它们。
CLI → createAgentSessionRuntime() → AgentSession → Agent → runAgentLoop() → SessionManager(coding-agent JSONL)学习边界由实际调用链决定。agent 不反向依赖 coding-agent 产品,TUI 组件也不负责控制 Tool Loop。
三、各 Package 的职责
Section titled “三、各 Package 的职责”packages/ai
Section titled “packages/ai”定位:统一多 Provider 的模型运行时。
主要职责:
- Model 和 Provider 注册
- API Key、OAuth 和 Provider 认证
- 模型目录和能力信息
- 统一
Context、Message和Tool - 统一流式事件
- thinking/reasoning 适配
- Tool Call 参数流
- 跨 Provider 消息兼容
- Faux Provider 测试能力
它不负责:
- 决定何时执行工具
- 保存 Agent Session
- 构造 coding-agent 系统提示词
- 渲染 TUI
核心抽象:
Model + Context + StreamOptions → AssistantMessageEventStream → AssistantMessagepackages/agent
Section titled “packages/agent”定位:通用 Agent Runtime。
主要职责:
- Agent 状态
- LLM 与 Tool 的循环
- Tool 参数校验与执行
- steering 和 follow-up 队列
- abort 和 settlement
- Agent 生命周期事件
- 正在演进的 AgentHarness 和通用 Session 存储底座
它不负责:
- CLI 参数
- 项目
AGENTS.md - coding-agent 的默认 Prompt
- 内置 coding tools 的产品配置
- 终端 UI
核心抽象:
Agent = State + Agent Loop + Queues + Awaited Events + AbortAgent 是当前 coding-agent 真正调用的执行引擎。
AgentHarness
现在通过 createAgentHarness() 委托到 harness/runtime;已有执行与恢复代码,但不在本文追踪的 coding-agent 主路径。
packages/coding-agent
Section titled “packages/coding-agent”定位:基于 Agent Core 构建的完整 coding-agent 产品。
主要职责:
- CLI 启动与参数解析
- Interactive、Print、JSON、RPC、SDK
- ModelRuntime 和认证选择
- AgentSession 产品编排
- 默认 coding tools
- ResourceLoader
- AGENTS.md、Skills、Prompt Templates
- Extension Runtime
- JSONL Session Tree
- Retry、Compaction 和 Branch Summary
- Project Trust
核心抽象:
AgentSession = Agent + Product Policy + Persistence + Resources + Extensions + Recoverypackages/tui
Section titled “packages/tui”定位:与 Agent 业务无关的终端 UI 框架。
主要职责:
- Component 树
- 输入和焦点管理
- Editor、Markdown、Selector、Overlay
- 终端能力检测
- Kitty/iTerm 图片
- 差量渲染
- 同步终端输出
它不理解:
- 什么是 Agent Turn
- 什么是 Tool Result
- 什么是 Session Compaction
这些业务概念由 coding-agent 转换成普通 TUI Component。
其他运行时与当前 CLI 的边界
Section titled “其他运行时与当前 CLI 的边界”旧模块 packages/agent/src/harness 中 AgentHarness 的 create() 委托给 harness/runtime/harness.ts 的 createAgentHarness()。执行进一步拆入 runtime/drive/*,恢复进入 runtime/restore.ts。旧文“prompt/resume 都是 scaffold、create.restore 固定抛错”的判断已经失效。
coding-agent: AgentSession → Agent → agentLoop SessionManager → JSONL Tree
agent-core Harness: AgentHarness.create → createAgentHarness → runtime / lane / drive / restore → harness/session
另有 packages/durable 与 harness/pico3 的演进实现这些路径的 Session、Entry、恢复与副作用语义不应互相套用。本章只核对当前 CLI 的入口与对象装配,不把另一路 runtime 的实现存在当作其完整生产恢复能力的证明。
四、静态架构与运行时对象的区别
Section titled “四、静态架构与运行时对象的区别”Package 是发布和依赖边界,运行时对象是一次进程中真正协作的组件。
flowchart LR
Main["main()"]
Services["AgentSessionServices"]
Runtime["AgentSessionRuntime"]
Session["AgentSession"]
Agent["Agent"]
Loop["agentLoop"]
StreamFn["injected streamFunction"]
ModelRuntime["ModelRuntime"]
ResourceLoader["ResourceLoader"]
SessionManager["SessionManager"]
Mode["Interactive / Print / RPC"]
Main --> Services
Services --> ModelRuntime
Services --> ResourceLoader
Main --> Runtime
Runtime --> Session
Session --> Agent
Session --> SessionManager
Agent --> Loop
Loop --> StreamFn
StreamFn --> ModelRuntime
Mode --> Runtime关键对象:
| 对象 | 生命周期 | 核心职责 |
|---|---|---|
AgentSessionServices |
与 cwd 绑定 | Settings、Models、Resources |
AgentSessionRuntime |
当前宿主持有 | 管理当前 Session 及 Session 替换 |
AgentSession |
一次 coding session | 产品级编排 |
Agent |
一次 Agent 状态实例 | 消息、工具、队列、事件 |
SessionManager |
持久化或内存会话 | JSONL、分支、上下文重建 |
InteractiveMode |
一次 TUI 生命周期 | 输入与事件展示 |
源码导读:Agent 与 AgentSession 的区别
Section titled “源码导读:Agent 与 AgentSession 的区别”只记住“Agent 是执行引擎,AgentSession 是产品编排层”还不够。下面用一次真实请求说明两者如何协作。
先区分三个容易混淆的概念:
| 概念 | 源码位置 | 保存什么 | 是否理解 coding-agent 产品 |
|---|---|---|---|
Agent |
packages/agent/src/agent.ts |
当前模型、System Prompt、工具、消息、流式状态、消息队列 | 否 |
AgentSession |
packages/coding-agent/src/core/agent-session.ts |
对 Agent 的产品级控制,以及扩展、重试、压缩、工具和资源状态 |
是 |
SessionManager |
packages/coding-agent/src/core/session-manager.ts |
JSONL Session Tree、当前 leaf、分支和历史恢复 | 是,但只负责持久化 |
这里最容易误解的一点是:
Agent.state.messages是当前进程中供 Agent Loop 使用的消息状态;SessionManager保存的是能够退出后恢复、分支和回放的持久化 Session。AgentSession负责让两者保持一致。
Agent 的边界
Section titled “Agent 的边界”打开 packages/agent/src/agent.ts,先看 Agent 类的字段和公开方法。
它持有的核心状态是:
Agent├─ state│ ├─ systemPrompt(从 system messages 重放的只读视图)│ ├─ model / thinkingLevel│ ├─ tools│ ├─ messages│ ├─ isStreaming / streamingMessage│ └─ pendingToolCalls / errorMessage├─ steeringQueue├─ followUpQueue├─ activeRun / AbortController└─ lifecycle listeners它提供的核心动作是:
prompt():加入新消息并启动 Agent Loop;continue():从已有上下文继续执行;steer():当前执行过程中,在下一次合适的轮次插入消息;followUp():Agent 原本准备结束时,再启动后续请求;abort():终止当前运行;waitForIdle():等待 Loop 和异步事件监听器全部完成;subscribe():订阅 Agent 生命周期事件。
Agent.prompt() 不会读取项目文件,也不会保存 JSONL。它最终只做一件核心工作:
规范化输入 → 创建本次运行的 Context Snapshot → 调用 runAgentLoop() → 处理模型与工具产生的事件 → 更新内存中的 AgentState因此,单独使用 Agent 时,宿主程序必须自己决定:
- System Prompt 从哪里来;
- 提供哪些工具;
- API Key 如何获取;
- 消息如何持久化;
- Context 过长时怎么办;
- UI 如何展示事件。
这正是它能够被其他产品复用的原因。
AgentSession 的边界
Section titled “AgentSession 的边界”再打开 packages/coding-agent/src/core/agent-session.ts。构造函数直接接收一个已经创建好的 Agent:
AgentSession├─ agent├─ sessionManager├─ settingsManager├─ modelRuntime├─ resourceLoader├─ extensionRunner├─ tool registry├─ retry / compaction state└─ bash / branch / product event stateModelRuntime 如何进入 Agent
Section titled “ModelRuntime 如何进入 Agent”ModelRuntime 是 coding-agent 的模型运行服务。它在 pi-ai 的 Provider 能力之上,组织认证、模型目录、
动态 Provider、重试配置和 streamSimple() 调用。它不是 Agent 自己创建的。
创建链是:
main() → createAgentSessionServices() → ModelRuntime.create() → createAgentSessionFromServices() → createAgentSession() → new Agent({ streamFn })createAgentSession() 在 sdk.ts
中构造一个 streamFn closure:
new Agent({ streamFn: async (model, context, options) => { return modelRuntime.streamSimple(model, context, options); },});上面是简化代码。真实 closure 还会合并 timeout、retry、Provider headers 和 Extension hooks。
因此 Agent 只依赖一个“给定 model/context,返回流式事件”的函数契约,
它不需要理解 API Key、Provider 注册或 coding-agent Settings。
这是依赖注入:产品层决定怎样调模型,通用 Agent Loop 只调用被注入的能力。
它不重新实现模型与工具循环,而是在 Agent 的前后增加 coding-agent 产品策略。
调用 AgentSession.prompt("修复这个 bug") 时,源码依次做:
AgentSession.prompt() ├─ 判断是否为 Extension Command ├─ 触发 Extension input hook ├─ 展开 Skill Command 和 Prompt Template ├─ 若 Agent 正在运行,则转成 steer / followUp ├─ 合并待注入的 Bash 和 Custom 消息 ├─ 检查 Model 与认证 ├─ 必要时先做 Compaction ├─ 构造 user/custom messages ├─ 触发 before_agent_start ├─ 更新本轮 System Prompt └─ _runAgentPrompt() └─ Agent.prompt(messages)进入 Agent.prompt() 后,控制权才交给通用运行时:
Agent.prompt() → runAgentLoop() → 调用模型 → 收到 Tool Call → 校验并执行 Tool → 写入 Tool Result → 再次调用模型 → 发出 agent_endAgentSession 在构造时通过下面这行订阅 Agent:
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);所以 Loop 每产生一个事件,AgentSession._handleAgentEvent() 都会收到。它会:
- 把事件先发送给 Extension;
- 把事件转发给 Interactive、Print、JSON 或 RPC 等消费者;
- 在
message_end时调用SessionManager持久化消息; - 记录最后一条 Assistant Message,供重试和自动压缩判断使用。
当 Agent 发出 agent_end 后,AgentSession 仍可能继续工作:
Agent 已结束一次运行 → 是否遇到可重试错误? → 是否需要自动 Compaction? → agent_end hook 是否又加入了消息? → 如果是,调用 Agent.continue() → 全部结束后发出 agent_settled因此:
agent_end表示通用 Agent Loop 的一次运行结束;agent_settled表示产品层的重试、压缩和后续 continuation 也全部处理完毕。
这也是为什么 UI 等待“Pi 完全空闲”时,不能只看 agent_end。
两者的真正边界
Section titled “两者的真正边界”可以用一个判断标准区分某项能力属于哪一层:
把 coding-agent 换成客服 Agent、数据分析 Agent 或后台服务后,这项能力是否仍然成立?
仍然普遍成立的机制放在 Agent:
- 模型调用;
- Tool Loop;
- Tool 参数校验和执行;
- streaming;
- steering / follow-up;
- abort;
- 生命周期事件。
只属于 Pi coding-agent 产品的策略放在 AgentSession:
- 加载项目
AGENTS.md; - 展开 Skills 和 Prompt Templates;
- Extension hooks;
- 文件、Shell 等 coding tools 的组合;
- Provider 认证和模型切换;
- JSONL Session Tree;
- Retry 和 Compaction;
- Branch Summary;
- 面向 TUI、Print、JSON、RPC 的产品事件。
持久化操作则进一步委托给 SessionManager。因此不是简单的两层,而是:
AgentSession:决定产品应该怎么运行 ├─ Agent:真正执行模型与工具循环 └─ SessionManager:保存和恢复会话树为什么不把它们合成一个类
Section titled “为什么不把它们合成一个类”如果 Agent 直接加载 AGENTS.md、写 JSONL、处理 TUI 和 Compaction:
- 通用 Agent Runtime 会绑定 coding-agent 的目录结构和产品策略;
- SDK 或 Server 无法只复用执行循环;
- Loop 测试会被文件系统、认证和 UI 状态干扰;
- 更换持久化方式或宿主产品会变得困难。
分层之后,Agent 可以独立测试概率性执行过程,AgentSession 可以测试确定性的产品编排,两者的变化原因也不同。
建议按以下断点阅读
Section titled “建议按以下断点阅读”第一次阅读不需要通读两个大文件,按这条路径即可:
packages/agent/src/agent.ts
Agent的字段;constructor();prompt();runPromptMessages();createContextSnapshot()。
packages/coding-agent/src/core/agent-session.ts
AgentSession的字段;constructor()中的agent.subscribe(...);prompt();_runAgentPrompt();_handlePostAgentRun();_handleAgentEvent()。
packages/coding-agent/src/core/session-manager.ts
appendMessage();buildSessionContext()。
读完后,用下面三个问题自测:
- 删除
SessionManager后,Agent能否完成一次模型与工具循环?能,但不能持久化和恢复。 - 删除
AgentSession后,能否直接调用Agent.prompt()?能,但要由新宿主自己提供 Prompt、工具、认证、持久化和恢复策略。 - 删除
Agent后,AgentSession能否工作?不能,因为它自身不实现 Agent Loop。
五、启动调用链
Section titled “五、启动调用链”第一阶段只需要理解启动骨架,不深入每个函数。
入口:
packages/coding-agent/src/cli.ts → main(process.argv.slice(2))主链路:
main() → 解析 CLI 参数 → 运行 migrations → 选择或创建 SessionManager → 确定最终 session cwd → 解析 project trust → createAgentSessionServices() ├─ SettingsManager ├─ ModelRuntime └─ DefaultResourceLoader.reload() → createAgentSessionFromServices() → createAgentSession() ├─ 恢复 model/thinking/messages ├─ new Agent() └─ new AgentSession() → createAgentSessionRuntime() → 选择运行模式 ├─ InteractiveMode ├─ PrintMode / JSON └─ RpcMode为什么先确定 Session cwd
Section titled “为什么先确定 Session cwd”Session 可以来自另一个项目。以下内容都与 cwd 绑定:
.pi/settings.json.pi/extensions.pi/skills.pi/prompts- 项目
AGENTS.md - Project Trust
因此不能先按启动目录加载资源,再切换到其他项目的 Session。Pi 会先确定最终 cwd,然后创建一组 coherent cwd-bound services。 Session 保存目录有默认值,但可由 CLI、环境变量或 Settings 覆盖,不能简化为始终与 cwd 固定绑定。
为什么使用 AgentSessionRuntime
Section titled “为什么使用 AgentSessionRuntime”用户执行 /new、/resume、/fork 或 import 时,可能需要:
- 关闭旧 Extension;
- 释放旧 Session;
- 切换 cwd;
- 重新加载 Settings、Models 和 Resources;
- 重建 AgentSession;
- 让 TUI 重新绑定到新 Session。
AgentSessionRuntime 专门负责这种“整个运行时替换”,避免把切换逻辑塞进 AgentSession。
六、核心工程理念
Section titled “六、核心工程理念”1. Minimal core
Section titled “1. Minimal core”Pi 核心只保留普遍适用的机制,不内建存在大量策略分歧的工作流。
当前必须区分“通用 Agent Core”与“coding-agent 随包提供的产品能力”。CLI 已内置
mcp、codemode、tool-search 等 Extension;不能再把“不内建 MCP”作为 Pi 当前的产品结论。
// extensions/index.ts 的缩写:省略 llama.cpp 与类型。const builtInExtensions = [ { name: "codemode", factory: codemodeExtension, replaceable: true, builtin: true }, { name: "tool-search", factory: toolSearchExtension, replaceable: true, builtin: true }, { name: "mcp", factory: mcpExtension, replaceable: true, builtin: true },];这些能力复用 Extension 的加载、生命周期和工具接口,没有要求裸 agentLoop() 理解 MCP transport。
可替换内置扩展与第三方扩展的工具、命令或 flag 冲突时,可由后者接管。具体审批与执行环境策略仍需
宿主选择;pi-durable 的任务所有权与子会话能力也不能等同于 CLI 默认带一个子 Agent 工具。
2. Mechanism 与 Policy 分离
Section titled “2. Mechanism 与 Policy 分离”例如 Agent Core 提供:
beforeToolCallafterToolCall- Tool registry
- 生命周期事件
但不规定:
- 哪些命令危险;
- 是否需要用户确认;
- 是否在容器中执行;
- 是否允许访问网络。
这些属于宿主环境的 policy。
3. Extension-first
Section titled “3. Extension-first”与其把所有功能加入核心,Pi 当前提供广泛的扩展面:
- 注册 Tool
- 注册 Command
- 注册 Provider
- 拦截 Context
- 修改 Provider Request
- 自定义 Compaction
- 注入 TUI
- 发现额外资源
目标是“适配用户工作流”,而不是要求用户适配固定产品流程。
4. Prompt 只承担模型需要理解的内容
Section titled “4. Prompt 只承担模型需要理解的内容”可以由程序保证的约束,不只依靠 Prompt:
- Tool 参数由 schema 校验;
- Tool 的 parallel/sequential 执行语义由 Loop 控制;parallel 模式下完成事件按实际完成顺序,Tool Result 按模型源顺序回写;
- Session 由持久化层保存;
- Context Overflow 由 Compaction 处理;
pi-ai提供统一上层协议,同时保留必要的 provider-specific compatibility;- Project Trust 由启动流程控制。
5. 事件驱动 UI
Section titled “5. 事件驱动 UI”AgentEvent 包含下列事件类型。这是类型集合,不是一次调用的严格时序:
agent_startturn_startmessage_startmessage_updatetool_execution_starttool_execution_updatetool_execution_endmessage_endturn_endagent_endInteractive、JSON 和 RPC 会订阅 AgentSession 事件。Print 的 JSON 模式输出事件,普通文本模式则在运行后读取最终 Session State。
好处:
- Agent Core 不依赖 TUI;
- 流式输出天然可观察;
- RPC 和测试复用同一生命周期;
- UI 更换不会改变 Agent Loop。
需要精确表述的是:
packages/tui只负责渲染机制;InteractiveMode既把用户输入提交给AgentSession,也订阅 AgentSession 事件更新组件。TUI 不直接控制 Agent Loop。
6. Session 是产品的一等公民
Section titled “6. Session 是产品的一等公民”当前 coding-agent 不把对话只保存成线性 message 数组,而是通过 SessionManager 保存为 JSONL Session Tree。
这为后续能力提供基础:
- resume
- fork
- clone
- tree navigation
- compaction
- branch summary
- session export
7. 供应链安全是工程设计的一部分
Section titled “7. 供应链安全是工程设计的一部分”根 README 和 AGENTS.md 强调:
- 外部依赖锁定精确版本;
- 安装时默认
--ignore-scripts; - lockfile 和 shrinkwrap 需要显式审核;
- 发布前从仓库外进行隔离 smoke test;
- npm 发布使用 CI OIDC;
- 不降级代码来绕过过期依赖引起的类型错误。
这与 Agent 项目尤其相关,因为:
- Extension 能执行任意代码;
- Tool 会调用本地程序;
- Agent 会读取凭据和项目文件;
- 自动化发布扩大供应链风险。
七、产品理念与开发规范不能混淆
Section titled “七、产品理念与开发规范不能混淆”产品定位与实现证据的来源
Section titled “产品定位与实现证据的来源”新版 packages/coding-agent/README.md 已精简,重点是 minimal/extensible terminal agent、多种运行模式,以及让用户创建或安装 Prompt、Skill、Extension、Theme。旧版“不内建 MCP”等清单已不适用;当前产品能力要同时核对 builtInExtensions 与入口装配。
本文关于主循环、工具、Session、扩展和权限边界的判断分别以 src/core、sdk.ts、tools/index.ts 及 docs/security.md 为依据。产品方向可以解释源码,但不能替代实际装配与调用证据。
开发规范的主要来源
Section titled “开发规范的主要来源”AGENTS.md:
- TypeScript 代码质量规则
- 测试命令
- Git 并发安全
- 依赖与 lockfile 规则
- Issue、PR、Changelog 和 Release 流程
AGENTS.md 同时也是阶段 5 的一个实例:Pi 会把它作为 project context 加载到 System Prompt。
八、关键设计问题
Section titled “八、关键设计问题”为什么 coding-agent 直接依赖 ai,而不只依赖 agent
Section titled “为什么 coding-agent 直接依赖 ai,而不只依赖 agent”因为 coding-agent 还需要直接使用:
- ModelRuntime
- Provider 注册
- Auth
- 模型查询和切换
- Compaction 的模型调用
- Provider 请求 hook
这些不是 Agent Loop 独占的能力。
为什么 TUI 是单独的 package
Section titled “为什么 TUI 是单独的 package”- 可以独立测试终端渲染;
- 可用于非 Agent 程序;
- Agent Core 不需要依赖终端环境;
- coding-agent 负责把业务事件适配成通用组件。
Server 与 coding-agent 是什么关系
Section titled “Server 与 coding-agent 是什么关系”server 不依赖 coding-agent。它围绕 PiServerService + protocol 组织 Session Server,
由宿主应用提供 Session 列表、模型列表、创建和打开 Session 的实现。
因此它是一条实验性远程协议链的基础包,不是当前 CLI 主路径的上层入口。
为什么 SQLite 后端独立发布
Section titled “为什么 SQLite 后端独立发布”避免 Agent Core 强制依赖:
- Node 内建 SQLite;
- 原生模块;
- 特定数据库运行时。
这体现 dependency inversion:Core 定义接口,Storage package 提供实现。
九、本阶段必须掌握的图
Section titled “九、本阶段必须掌握的图”当前 workspace 直接依赖图
Section titled “当前 workspace 直接依赖图”client → protocolai → telemetryagent → ai + telemetryserver → ai + protocolsession-backends/sqlite-node → agent + aicoding-agent → agent + ai + client + protocol + tuitui → 无 Pi workspace 依赖Mode → AgentSessionRuntime → AgentSession → Agent → agentLoop → 注入的 streamFunction → ModelRuntime.streamSimple() → pi-ai → ProviderSettings + Project Trust → ResourceLoader ├─ Extensions ├─ Skills ├─ Prompt Templates ├─ Themes └─ AGENTS.md / System Prompt → AgentSession十、面试表达
Section titled “十、面试表达”Pi 是一个 minimal terminal coding harness,核心特征是分层和可扩展。
packages/ai 将不同 Provider 转成统一的模型、消息和流式协议;
packages/agent 使用这套协议实现 Agent State、Tool Loop、队列、abort 和生命周期事件;
packages/coding-agent 通过 AgentSession 加入项目资源、coding tools、Extensions、认证、重试、Compaction 和 JSONL Session Tree;
packages/tui 只提供终端渲染机制,不理解 Agent 业务。
一次请求的主链是:
Mode → AgentSession.prompt() → Agent.prompt() → runAgentLoop() → streamFunction → ModelRuntime.streamSimple() → pi-ai ProviderAgent 事件会回到 AgentSession,先进入 Extension hooks,再通知 UI/RPC 等消费者,
并在 message_end 时交给 SessionManager 持久化。
agent_end 只是底层 Loop 结束,产品层仍可能重试、压缩或继续队列消息,直到 agent_settled 才表示真正空闲。
Pi 的 minimal core 不是功能少,而是将普遍成立的 mechanism 留在核心, CLI 已随包提供 MCP、codemode 和工具搜索扩展;通用 Loop 仍通过工具与 hooks 工作。权限策略及产品工作流由 Extension 或宿主组织。
- 为什么
AgentSession不等于Agent?请从 prompt 前处理、Loop 和 post-run 三段回答。 - 为什么
agent_end之后 Pi 仍可能没有完全空闲? - 当前 coding-agent 的 JSONL
SessionManager与新 AgentHarness 的 Session Storage 是同一套实现吗? server为什么不能画成server → coding-agent?它要求宿主注入什么接口?Agent到 Provider 的真实调用链里,ModelRuntime是由谁注入的?
当前还要区分两套 Harness
Section titled “当前还要区分两套 Harness”packages/agent/src/harness 的 AgentHarness 与 packages/durable/src/harness 的 Harness 是不同实现。
最新 pi-durable 已提供 Harness.open → root/submit → pi.generation → pi.tool 的可运行链、恢复调度、
compaction、扩展注册表和每会话 Agent 配置。CLI 的 createAgentSession() 主路径仍使用 AgentSession,
不能因为仓库出现新 Harness 就说 CLI 已迁移。第十章以新 pi-durable 为主线,单列旧实现边界。