TUI、RPC 与 SDK 产品表面
一、本章只建立一个中心认识
Section titled “一、本章只建立一个中心认识”Pi 的 Interactive、Print、RPC 和 SDK 不是四套 Agent,也不是四套业务逻辑。它们共享同一个
AgentSession,区别只在宿主如何接收输入、如何消费事件,以及如何呈现结果:
外部输入 ├─ Interactive:键盘、编辑器、快捷键、斜杠命令 ├─ Print:命令行参数、stdin、连续消息 ├─ RPC:stdin 中的一行一个 JSON 命令 └─ SDK:同进程 TypeScript 方法调用 ↓宿主适配层 把外部动作翻译成 prompt / steer / followUp / abort / compact / setModel ... ↓AgentSessionRuntime 持有当前 AgentSession;负责 new / resume / fork / import 后的整体替换 ↓AgentSession 统一业务核心:Prompt、Agent Loop、Tool、Session、Retry、Compaction、事件 ↓AgentSessionEvent ├─ Interactive:更新 Component,再请求 TUI 渲染 ├─ Print text:只取最终 Assistant 文本 ├─ Print json:逐行输出事件 ├─ RPC:逐行输出事件,并额外输出命令响应 └─ SDK:交给应用自己的 listenerPhase 9 的重点不是记住 TUI 组件名或 RPC 的所有命令,而是掌握“输入适配:统一业务核心:事件投影”三层结构。只要新界面遵守这三个边界,就不需要修改 Agent Loop。
本章依据学习路线指定的一手资料整理:
源码路径在本章中只作为结论的回查入口,不作为阅读主线。
二、先分清五个层级
Section titled “二、先分清五个层级”| 层级 | 核心对象 | 拥有什么 | 不应该负责什么 |
|---|---|---|---|
| Agent Loop | Agent / agentLoop |
模型:工具循环、消息与 Tool Result | 终端、JSONL、应用界面 |
| 会话业务层 | AgentSession |
Prompt 预处理、队列、Session、Retry、Compaction、扩展和统一事件 | ANSI 渲染、RPC framing |
| 可替换运行时 | AgentSessionRuntime |
当前 Session 与 cwd-bound services;new/resume/fork/import | 单次模型流和组件展示 |
| 宿主适配层 | Interactive / Print / RPC / SDK app | 输入翻译、事件消费、宿主生命周期 | 复制 Agent 业务规则 |
| 显示基础设施 | TUI / Component |
组件树、终端输入、布局、光标和差量渲染 | Prompt、Tool、Compaction 决策 |
2.1 AgentSession 是业务协议
Section titled “2.1 AgentSession 是业务协议”对宿主而言,AgentSession 可以被理解为两组稳定接口:
命令面 prompt() / steer() / followUp() / abort() setModel() / setThinkingLevel() compact() / abortCompaction() navigateTree() / reload() ...
事件面 subscribe(listener) → agent / turn / message / tool 生命周期 → queue / compaction / retry / session 状态宿主发命令,不直接操纵 Agent Loop;宿主订阅事实事件,不轮询内部执行步骤。
2.2 AgentSessionRuntime 是“当前会话槽位”
Section titled “2.2 AgentSessionRuntime 是“当前会话槽位””AgentSession 表示一个当前会话实例,但切换、创建、fork 或导入可能让当前实例整体失效。
AgentSessionRuntime 因而额外持有:
current AgentSession+ 当前 cwd 绑定的 services+ 用于重新创建整套运行时的 factory+ 创建过程中的 diagnostics会话被替换后,宿主必须重新完成两件事:
- 对新
AgentSession重新绑定 Extensions; - 取消旧订阅,对新
AgentSession重新订阅事件。
这就是 Interactive、Print 和 RPC 都存在 rebindSession 流程的原因。只替换对象引用而不重绑,
会导致新 Session 的事件无人消费,或 UI 继续持有已经失效的扩展上下文。
证据入口:agent-session-runtime.ts、
sdk.md#createagentsessionruntime-and-agentsessionruntime。
2.3 AgentSessionEvent 是事实流,不是 UI 指令
Section titled “2.3 AgentSessionEvent 是事实流,不是 UI 指令”事件表达“业务上发生了什么”:消息开始、内容增加、工具执行、压缩开始、重试结束等。 它不表达“画一个红色框”“把光标移到第 10 行”。
因此不同宿主可以对同一事件做不同投影:
message_update(text_delta) ├─ TUI:更新 AssistantMessageComponent ├─ RPC:原样输出 JSONL ├─ SDK Web UI:更新前端 store └─ 日志系统:只记录 token 或时间指标事件层将业务状态变化与显示策略解耦,也是新产品表面能够复用核心的关键。
三、四种产品表面如何分工
Section titled “三、四种产品表面如何分工”| 表面 | 进程关系 | 输入 | 输出 | 状态访问 | 典型用途 |
|---|---|---|---|---|---|
| Interactive | Pi 进程内 | 键盘、编辑器、命令、图片 | TUI 组件树 | 直接读取 Session/Runtime | 完整终端产品 |
| Print text | Pi 进程内 | 初始 Prompt + 后续消息 | 最后一条 Assistant 文本 | 运行结束后读取 state | shell 单次调用 |
| Print json | Pi 进程内 | 同 Print text | Session header + 全部事件 JSONL | 通过事件流 | 脚本、日志采集 |
| RPC | 子进程边界 | stdin JSONL commands | stdout responses + events JSONL | 通过 RPC commands | IDE、其他语言、进程隔离 |
| SDK | 同一 Node.js 进程 | TypeScript 方法调用 | listener、返回值、直接 state | 最强、类型安全 | 嵌入式应用、自定义 UI |
3.1 选择 SDK 还是 RPC
Section titled “3.1 选择 SDK 还是 RPC”同一 Node.js/TypeScript 进程 → 优先 SDK → 类型安全、直接访问状态、无需序列化和子进程管理
其他语言、IDE 插件进程、需要故障隔离 → 选择 RPC → 用 JSONL 换取语言无关和进程边界RPC 不是“能力更强的 SDK”,而是 AgentSession 之上的跨进程适配协议。SDK 也不是一个新运行时,
它只是直接把创建和控制 AgentSession 的能力暴露给同进程应用。
四、三条必须掌握的主调用链
Section titled “四、三条必须掌握的主调用链”4.1 用户输入到 AgentSession.prompt()
Section titled “4.1 用户输入到 AgentSession.prompt()”Interactive 的主链可以抽象为:
Terminal key input → TUI 将输入交给当前聚焦 Component → Editor 维护文本、历史和提交动作 → InteractiveMode 识别宿主级命令或普通输入 → 普通输入调用 session.prompt(text, options) → Prompt 预处理:模板 / Skill / Extension command / 图片 → 若空闲:开始 Agent run → 若正在 streaming:按 steer / followUp 语义入队这里有三个分支必须记住:
- 普通 Prompt:进入统一 AgentSession 流程;
- Extension command:可以立即由扩展处理,即使 Agent 正在 streaming;
- streaming 中的普通输入:必须声明
steer或followUp,否则拒绝,而不是猜测用户意图。
steer 与 followUp 的时机不同:
steer 当前 Assistant turn 与本批 Tool Call 完成后 → 下一次 LLM 调用前插入
followUp 整个 Agent run 没有更多 Tool Call 和 steering 后 → 再开启后续任务证据入口:sdk.md#prompting-and-message-queueing、
interactive-mode.ts。
4.2 AgentEvent 到 TUI Component
Section titled “4.2 AgentEvent 到 TUI Component”InteractiveMode 订阅 AgentSessionEvent,把事件映射为组件状态:
| 事件 | TUI 投影 | 需要维护的宿主状态 |
|---|---|---|
agent_start |
显示 Working 状态,开启终端 progress | 当前工作指示器、Escape 行为 |
queue_update |
刷新待处理消息区 | steering/follow-up 队列展示 |
message_start(user) |
追加 UserMessageComponent | Chat 顺序 |
message_start(assistant) |
创建流式 AssistantMessageComponent | streamingComponent、streamingMessage |
message_update |
用累计消息刷新 Assistant;发现 Tool Call 时创建/更新 Tool 组件 | partial message、pendingTools map |
message_end |
固化 Assistant;完成参数或标记 error/aborted | 清除 streaming 引用,处理未完成工具 |
tool_execution_start |
Tool 组件进入执行态 | toolCallId → component |
tool_execution_update |
增量更新 Tool 输出 | partial result |
tool_execution_end |
固化结果和错误状态 | 从 pendingTools 移除 |
agent_end |
移除 Working 状态,清理临时流式对象 | 一次 Agent run 的显示收尾 |
agent_settled |
检查是否可以执行延迟 shutdown | 全部后处理完成的边界 |
compaction_start/end |
切换压缩状态;成功后按压缩后的 Session 重建 Chat | Escape 临时改为 abortCompaction |
auto_retry_start/end |
显示倒计时或失败;临时接管 Escape | retry attempt、timer、取消回调 |
session_info_changed |
更新标题和 Footer | Session 名称/文件等摘要 |
最重要的流式规律是:
message_start 创建一个可变的显示投影
message_update × N 每次收到的是“截至当前的累计 AssistantMessage” Component 用它更新文本、thinking 和 Tool Call 参数
message_end 将显示投影固化,并处理 aborted / error / 未完成工具业务消息属于 AgentSession;streamingComponent、pendingTools、展开状态、颜色、Footer 则属于
InteractiveMode。不能把这些 UI 暂态反向塞进 Agent State。
证据入口:interactive-mode.ts 中的
subscribeToAgent() 与 handleEvent()。
4.3 RPC 请求到 AgentSession
Section titled “4.3 RPC 请求到 AgentSession”client writes one JSON object + LF → RPC JSONL reader 按 LF 切帧 → JSON.parse → handleCommand(command) → 翻译成 session/runtime 方法 → 输出 command response
与此同时:
session.subscribe(event) → 事件随发生随时输出为 JSONL以 prompt 为例:
{"id":"1","type":"prompt","message":"..."} → session.prompt(..., preflightResult) → preflight 接受后立即输出 success response → Agent 继续异步运行 → message_update / tool_execution_* / agent_end / agent_settled 持续输出必须区分两种通道:
| 通道 | 回答的问题 | 是否代表任务完成 |
|---|---|---|
response |
这个 RPC command 是否被正确解析并接受/执行 | prompt success 不代表 Agent 已完成 |
AgentSessionEvent |
Agent 与 Session 随后发生了什么 | 由 agent_settled 表示后处理也已结束 |
命令携带的 id 只用于关联 response;事件是独立的异步事实流。客户端不能等到 prompt() 完整结束
才发送 success,否则会把“命令接收确认”和“长任务完成”错误地绑在一起。
RPC 使用严格 JSONL:每条记录只以 LF \n 分隔,允许输入 CRLF 时剥掉末尾 CR;不能把
Unicode 行分隔符误判成协议帧边界。
证据入口:rpc.md#protocol-overview、
rpc-mode.ts。
五、TUI 框架:组件树、失效通知与差量渲染
Section titled “五、TUI 框架:组件树、失效通知与差量渲染”5.1 TUI 不理解 AgentEvent
Section titled “5.1 TUI 不理解 AgentEvent”依赖方向是:
AgentSessionEvent → InteractiveMode 解释业务含义 → 修改 Component 状态/组件树 → ui.requestRender() → TUI 将组件树渲染成 string[] → 与 previousLines 比较并写终端packages/tui 只理解 Component:
render(width) → string[]handleInput(data)invalidate()?因此 TUI 可以被其他终端应用复用;Agent 也可以离开 TUI,被 RPC 或 SDK 宿主复用。
5.2 requestRender() 为什么不立即重画
Section titled “5.2 requestRender() 为什么不立即重画”模型流可能高频产生 delta。如果每个 delta 都同步重画整屏,会造成:
- 大量重复 layout 和 ANSI 输出;
- 终端闪烁;
- 光标和 IME 定位不稳定;
- 输出速度反过来阻塞模型事件处理。
TUI 使用“合并请求 + 最小渲染间隔”:
多个 requestRender() → renderRequested 只记录一次待渲染 → nextTick 进入 scheduleRender() → 根据上次渲染时间计算 delay → 到期执行一次 doRender() → 渲染期间若又失效,再调度下一轮force = true 时会丢弃旧快照并触发完整重画,适合主题、尺寸或无法安全增量处理的变化,
不用于普通 token delta。
5.3 差量渲染的核心算法
Section titled “5.3 差量渲染的核心算法”1. render(width) 得到 newLines2. 将 overlays 合成进 newLines3. 提取逻辑光标标记,并应用行尾 reset4. 比较 previousLines 与 newLines5. 找到 firstChanged / lastChanged6. 只移动到变化区间并重写这些行7. 清理旧内容多出的行8. 一次性写出同步输出块9. 保存 newLines、终端尺寸、viewport 和 hardware cursor只写变化区间能显著减少 spinner、流式文本和 Tool 进度造成的闪烁。把多段 ANSI 更新包在终端的 synchronized output 中,则避免用户看到“更新到一半”的中间帧。
以下情况会退化为完整重画,因为旧坐标已经不可靠:
- 第一次渲染;
- 终端宽度变化,导致所有折行变化;
- 需要处理的变化已经滚出可安全定位的 viewport;
- 内容收缩后必须清掉旧工作区;
- 显式
requestRender(true)。
5.4 invalidate() 与 requestRender() 不同
Section titled “5.4 invalidate() 与 requestRender() 不同”invalidate() 告诉 Component:缓存的布局/样式结果已经失效
requestRender() 告诉 TUI:安排一次新的屏幕提交主题变化通常两者都需要;普通流式组件若直接更新了自身内容,通常只需请求渲染。
证据入口:tui.ts 中的 Component、requestRender()、
scheduleRender() 与 doRender()。
六、Print、RPC、SDK 各自如何消费同一核心
Section titled “六、Print、RPC、SDK 各自如何消费同一核心”6.1 Print:最薄的宿主
Section titled “6.1 Print:最薄的宿主”创建/绑定 Session → 可选订阅 JSON 事件 → 顺序 await initialMessage 和 messages → text 模式读取最后一个 AssistantMessage → error / aborted 设置失败退出码 → 取消订阅、dispose runtime、flush stdout两种输出模式体现了“状态”和“事件”的两种读取方式:
text:任务结束后读取最终状态;json:运行中投影完整事件历史。
证据入口:print-mode.ts。
6.2 RPC:带协议边界的宿主
Section titled “6.2 RPC:带协议边界的宿主”RPC 除了命令翻译,还必须处理 SDK 同进程调用不需要面对的问题:
- JSON parse 和 command validation;
- request id 与 response 关联;
- stdout 只能承载协议数据,普通日志不能污染;
- stdout backpressure,防止生产事件快于消费者读取;
- stdin 结束、SIGTERM/SIGHUP、子进程清理;
- Extension UI request/response 的跨进程往返;
- Session 替换后的重新绑定。
RPC 为每个 AgentSessionEvent 提供序列化出口,但不重新定义事件语义。
6.3 SDK:由嵌入方自己成为宿主
Section titled “6.3 SDK:由嵌入方自己成为宿主”最小 SDK 集成只需要:
createAgentSession() → session.subscribe(listener) → await session.prompt(...) → session.dispose()需要 new/resume/fork/import 或 cwd 重建时,再使用 AgentSessionRuntime。嵌入方可以直接访问
Session state、Model、messages 和 Extension 能力,因此也承担更多生命周期责任。
典型自定义 Web UI:
HTTP/WebSocket input → 服务端校验用户与会话 → session.prompt / steer / abort → listener 将 AgentSessionEvent 写入 server-side store → WebSocket 推送精简后的前端事件 → React/Vue store 投影为组件这条链不需要让 Agent Loop 知道 HTTP、WebSocket 或前端框架。
证据入口:sdk.md。
七、生命周期、失败、取消和背压
Section titled “七、生命周期、失败、取消和背压”7.1 正常完成不是只有 agent_end
Section titled “7.1 正常完成不是只有 agent_end”agent_end 本次 Agent Loop 已结束
agent_settled retry、compaction、follow-up 等 Session 级后处理也已安定如果宿主要安全退出、关闭窗口或切换任务,应以 agent_settled 作为更可靠的最终边界。
7.2 错误分为命令错误和运行错误
Section titled “7.2 错误分为命令错误和运行错误”RPC 命令错误 JSON 无法解析、模型不存在、Prompt preflight 拒绝 → response.success = false
Prompt 接受后的运行错误 Provider、Tool、Retry 最终失败 → 正常 AgentSessionEvent / AssistantMessage(error)同样,SDK 的 preflightResult(false) 只表示 Prompt 在接受前失败;接受后的失败不会再伪装成
同一个命令的第二次拒绝。
7.3 取消是业务命令,界面只提供触发方式
Section titled “7.3 取消是业务命令,界面只提供触发方式”| 场景 | Session 能力 | Interactive 的触发投影 | RPC/SDK |
|---|---|---|---|
| 当前 Agent run | abort() |
Escape | abort command / method |
| Compaction | abortCompaction() |
压缩期间临时接管 Escape | 对应 command / method |
| Retry 等待 | abortRetry() |
Retry 状态期间临时接管 Escape | 对应 command / method |
Escape 只是 TUI 交互策略,真正的取消语义属于 Session。
7.4 RPC 必须尊重背压
Section titled “7.4 RPC 必须尊重背压”SDK listener 在同进程中可以自行决定缓冲方式;RPC 的 stdout 是有限缓冲区。若客户端读取过慢, RPC 必须等待输出背压,避免无限积压事件和内存增长。因此 RPC 还订阅底层 Agent 事件并在适当边界 等待 stdout drain。
这也是“UI 只消费事件”的一个限定:消费者不能拖垮生产者,跨进程适配层必须负责流量治理。
7.5 Session 替换失败由宿主呈现
Section titled “7.5 Session 替换失败由宿主呈现”Runtime 在 new/resume/fork/import 时负责 teardown、创建并应用新运行时;创建失败会抛给调用方。 Interactive 可以显示错误组件,RPC 返回失败 response,SDK 应用则可选择 toast、重试或回退。 Runtime 不硬编码某一种呈现。
八、如何设计一个新的宿主界面
Section titled “八、如何设计一个新的宿主界面”假设要新增 Web、Desktop 或 IDE 面板,不需要复制 InteractiveMode。按以下边界设计:
8.1 输入适配
Section titled “8.1 输入适配”- 普通提交 →
session.prompt(); - 运行中修改方向 →
steer; - 完成后追加任务 →
followUp; - 停止 →
abort; - 模型、thinking、compaction、Session 操作调用公开方法;
- 不直接修改 Agent Loop 的内部执行变量。
8.2 事件投影
Section titled “8.2 事件投影”- 按
message_start/update/end建立流式消息 reducer; - 按 toolCallId 维护 Tool 状态;
- 单独呈现 queue、compaction、retry;
- 区分
agent_end和agent_settled; - 不把颜色、DOM 节点、终端光标等 UI 暂态写回 Agent State。
8.3 生命周期
Section titled “8.3 生命周期”- 保存 unsubscribe;
- Session 替换后重新绑定 Extensions 和订阅;
- 退出时 abort/等待、dispose 并清理进程或连接资源;
- 跨进程或网络传输时处理 framing、背压、断线和重连策略。
8.4 最小宿主伪代码
Section titled “8.4 最小宿主伪代码”let session = runtime.session;let unsubscribe = bind(session);
function bind(nextSession) { return nextSession.subscribe(event => view.reduce(event));}
async function submit(text) { await session.prompt(text);}
async function switchSession(path) { unsubscribe(); await runtime.switchSession(path); session = runtime.session; unsubscribe = bind(session);}
async function dispose() { unsubscribe(); await runtime.dispose();}这里最值得验证的不是语法,而是不变量:宿主只通过命令改变业务,通过事件得到业务事实。
九、容易混淆的概念
Section titled “九、容易混淆的概念”| 概念 A | 概念 B | 区别 |
|---|---|---|
AgentSession |
AgentSessionRuntime |
前者管理当前会话业务;后者管理可被整体替换的当前会话与 cwd-bound services |
| Event | Component | Event 是业务事实;Component 是某个宿主的显示投影 |
message_update |
text delta | update 携带累计 AssistantMessage,并内含具体 AssistantMessageEvent;UI 不应只假设文本 |
agent_end |
agent_settled |
前者结束 Agent run;后者表示 Session 级后处理也已稳定 |
| RPC response | Agent event | response 确认命令;event 描述异步业务进展 |
| Print JSON | RPC | 前者主要输出事件、单次运行;后者是长期双向命令协议 |
| SDK | RPC | SDK 同进程、类型安全、直访状态;RPC 跨进程、语言无关、需序列化与背压 |
invalidate() |
requestRender() |
前者丢弃组件缓存;后者安排一次屏幕提交 |
| Full redraw | Differential render | 前者重建整个终端画面;后者只写变化行 |
| UI state | Agent state | 展开、颜色、光标、组件属于 UI;messages/model/tools 属于 Agent/Session |
十、用一张图记住 Phase 9
Section titled “十、用一张图记住 Phase 9” ┌──────────────────────────┐ Keyboard / Editor ────▶│ InteractiveMode │──▶ Component tree ─▶ TUI diff └────────────┬─────────────┘ CLI args / stdin ─────▶ PrintMode │ │ commands JSONL stdin ──────────▶ RpcMode │ ▼ TS application ───────▶ SDK ──▶ AgentSessionRuntime │ ▼ AgentSession prompt / queue / abort retry / compaction / session │ ▼ events ┌────────────┴─────────────┐ │ 同一 AgentSessionEvent 流 │ └──────────────────────────┘中心句:
Pi 把 AgentSession 当作可复用的业务内核,用命令作为输入、用事件作为输出;Interactive、Print、RPC 和 SDK 只是不同宿主适配器,TUI 则进一步把事件投影后的组件树做节流和差量提交。
十一、面试表达
Section titled “十一、面试表达”11.1 30 秒版本
Section titled “11.1 30 秒版本”Pi 的多种运行模式共享同一个 AgentSession。Interactive、Print、RPC 和 SDK 只负责把各自输入 翻译成 Session 方法,再订阅统一的 AgentSessionEvent。TUI 不进入 Agent 业务层,它把事件映射为 组件状态,并通过合并渲染请求、比较新旧行和同步 ANSI 输出减少闪烁。会话整体切换由 AgentSessionRuntime 管理,切换后宿主必须重新绑定扩展和事件订阅。
11.2 2 分钟版本
Section titled “11.2 2 分钟版本”可以按四层回答:
AgentSession是业务核心,统一管理 Prompt、队列、Agent Loop、Tool、Session、Retry 和 Compaction;AgentSessionRuntime管理当前 Session 以及 cwd-bound services,处理 new/resume/fork/import 造成的整体替换;- Interactive、Print、RPC、SDK 是宿主适配层,分别处理终端输入、单次命令行、跨进程 JSONL 和同进程 API;
- 所有宿主都消费同一事件流。Interactive 将事件 reducer 成组件状态,TUI 再将组件树渲染为行,与上一帧比较,只提交变化区间。
这种设计的收益是 Agent Loop 与产品表面解耦。新增 Web 或 IDE UI 时,不需要修改模型:工具循环, 只需实现输入翻译、事件投影、Session 重绑和资源清理。RPC 额外处理 framing、request correlation、 stdout 纯净性和背压;SDK 则以同进程和类型安全换取更直接的状态访问。
11.3 深入追问
Section titled “11.3 深入追问”为什么 UI 不直接控制 Agent Loop?
否则终端命令、组件状态和 ANSI 行为会进入通用运行时,RPC 与 SDK 需要重复逻辑。命令/事件边界让 业务核心独立测试,也让多个界面对同一事实采用不同呈现。
为什么 RPC 的 prompt success 不表示 Agent 完成?
它只表示 Prompt 通过 preflight 并被接受、排队或立即处理。长任务进度和最终结果通过事件异步输出, 否则 RPC command response 会被一次模型调用长期占用。
差量渲染为什么还需要完整重画?
终端宽度变化会改变全部折行,viewport 外的旧行无法安全定位,内容收缩也可能残留旧行。这些情况下 完整重画是恢复正确屏幕状态的安全路径。
为什么切换 Session 后要重新订阅?
订阅绑定的是具体 AgentSession 实例。Runtime 替换 Session 后,旧 listener 不会自动跟随新对象, 扩展上下文也可能绑定旧 cwd 和旧 Session。
十二、本阶段验收
Section titled “十二、本阶段验收”不看源码,应能回答:
- 画出 Interactive、Print、RPC、SDK 汇聚到 AgentSession 的架构图;
- 解释
AgentSession与AgentSessionRuntime的职责边界; - 复述用户输入到
session.prompt()的主链; - 解释
message_start → update* → end如何映射成流式组件; - 说明 Tool、Compaction、Retry 事件如何进入 UI;
- 解释 RPC response 与 Agent event 为什么是两条通道;
- 解释 TUI 如何合并渲染请求、寻找变化区间,以及何时完整重画;
- 说明
agent_end与agent_settled的区别; - 给出新 Web/IDE 宿主所需的输入、事件和生命周期设计;
- 在不修改 Agent Loop 的前提下描述一种新宿主界面。
十三、可选源码回查顺序
Section titled “十三、可选源码回查顺序”只在需要验证某个结论时按以下顺序跳转,无需通读:
sdk.md#core-concepts:先确认 Session/Runtime 公共协议;print-mode.ts:看最薄宿主,建立基线;rpc.md#protocol-overview:理解 response/event 双通道;rpc-mode.ts:只回查rebindSession、handleCommand和输入循环;interactive-mode.ts:只回查输入提交、subscribeToAgent、handleEvent;tui.ts:只回查requestRender、scheduleRender、doRender;agent-session-runtime.ts:最后验证 Session 替换与重绑边界。
十四、下一阶段连接
Section titled “十四、下一阶段连接”Phase 9 解释了当前产品如何围绕 AgentSession 建立多种宿主。Phase 10 将进一步研究
pi-durable Harness:当运行时间更长、状态需要 durable persistence、配置存在并发 mutation、崩溃后需要
恢复时,为什么只靠当前 AgentSession 编排还不够,以及新的 Harness 如何把运行阶段和保存边界变成
更明确的不变量。
当前宿主协议补充:接纳结果与嵌套事件
Section titled “当前宿主协议补充:接纳结果与嵌套事件”RPC 的成功响应表示命令已被接纳,不等于模型回答已经结束。prompt 的权威响应在 preflightResult 回调后发出,返回 handled/queued/started disposition;steer、follow_up 也返回实际 disposition。 clear_queue 另有命令入口。正文的命令/运行事件分离仍成立。
// rpc-mode.ts 缩写,省略命令 id 和异常分支。void session.prompt(command.message, { source: "rpc", preflightResult: disposition => output(success(id, "prompt", { disposition })),});// 后续 message/tool/settled 仍经订阅输出。const disposition = await session.steer(command.message, command.images, { source: "rpc" });return success(id, "steer", { disposition });codemode 产生的嵌套 tool_execution_* 事件带 parentToolCallId。宿主应以调用 id 和父子关系关联展示, 不要把它们认作模型新发出的独立 Tool Call,也不要从这些 UI 事件自行重复追加 Session 消息。 虚拟模型下应区分用户选择与实际回答模型;footer 的窗口与消耗需要对应实际路由结果。
当前 Interactive 增加 MCP 管理、codemode/MCP 工具参数与按视觉换行截断的预览,fullscreen 支持 可配置滚轮行数与自动加速。工具预览隐藏几行属于展示层,和模型 content 截断、脚本结构化结果预算 是三个不同的限制。TUI 流式渲染与主题更新也有减小重绘成本的调整,不改变事件消费的基本分层。
第十章的新 pi-durable watch/viewState 是另一套宿主接口:它观察提交后的 entries/documents。 不能拿本章 AgentSessionEvent 的字段和结束事件直接套到新 Harness;两者均可承载界面,但状态协议不同。