首页
/ pi agent-core 演进全图:从 CHANGELOG 解读 pi-agent-core 的 API 重构、钩子体系与 v4 会话仓库

pi agent-core 演进全图:从 CHANGELOG 解读 pi-agent-core 的 API 重构、钩子体系与 v4 会话仓库

2026-09-06 14:40:50作者:韦蓉瑛

本文以 packages/agent 下的 CHANGELOG 为主体,完整梳理 @earendil-works/pi-agent-core 从 0.31 到 0.84.x 的版本演进脉络:哪些是必须迁移的 Breaking Changes,哪些钩子(beforeToolCallafterToolCallshouldStopAfterTurnprepareNextTurn)分别在何时引入并如何影响 agent loop 的执行时序,以及 0.84.0 那次会话仓库 v4 重构到底改了什么。读完本文,你可以在升级该包时快速定位需要修改的调用点,并理解每条变更记录背后的源码行为。

一、包的身份与版本坐标

packages/agent 是 pi monorepo 中实现"有状态 Agent + 工具执行 + 事件流"的核心包,package.json 声明:

  • 包名:@earendil-works/pi-agent-core,当前仓库版本 0.84.4(与 CHANGELOG 的最新已发布版本一致);
  • 运行时要求:engines.node >= 22.19.0——这与 CHANGELOG 中 0.75.0(2026-05-17)"Raise minimum supported Node.js version to 22.19.0" 的 Breaking Change 互相印证;
  • 直接依赖:@earendil-works/pi-ai@earendil-works/pi-telemetrytypebox@1.3.7diffignoreyaml,可见 0.69.0 从 @sinclair/typebox 0.34 迁移到 typebox 1.x 已落地为真实依赖;
  • 公开入口:根导出(.)、./node./session/testing 三个 subpath。0.84.0 的 CHANGELOG 说明 harness v2 已从 experimental subpath "晋升为默认导出、移除 experimental 子路径",与这里的 exports 表完全吻合。

该包的源码主体在 packages/agent/srcagent.ts(Agent 类与状态)、agent-loop.ts(低层循环)、proxy.ts(浏览器代理)、types.ts(公共类型与契约注释),harness/ 子目录承载执行环境与工具。测试位于 packages/agent/test,其中 agent-loop.test.tsagent.test.ts 覆盖了本文反复引用的 shouldStopAfterTurnprepareNextTurn 行为,可作为升级后的回归验证入口。

二、如何阅读这份 CHANGELOG:结构约定与里程碑速览

CHANGELOG 采用 Keep a Changelog 风格:顶部是 [Unreleased],随后按版本号倒序排列,每个版本下分 Breaking Changes / Added / Changed / Fixed 四类条目,并附 issue/PR 编号(如 #8979)。空版本号(如 0.83.00.82.1)表示该补丁/小版本无对外需要记录的变更。

按主题归纳,整份 CHANGELOG 可划分为五条主线:

主题 关键版本 一句话概括
公共 API 形态重构 0.31.0 / 0.32.0 / 0.65.0 / 0.81.0 移除 Transport 抽象、队列拆分为 steer/followUp、AgentState 改直接属性访问、streamFn 变为必需
钩子与回合控制 0.58.0 / 0.64.0 / 0.69.0 / 0.72.0 / 0.80.3 / 0.84.1 / 0.84.4 before/afterToolCall、terminate 提示、shouldStopAfterTurn、prepareNextTurn 及其时序调整
会话仓库 v4 重构 0.84.0 Session/SessionStorage/SessionRepo 取代旧会话模型,JsonlSessionRepo 原子发布
Harness 从实验到默认 0.80.0 / 0.82.0 / 0.84.0 Models 成为唯一认证路径、toolContext 工具模型、v2 API 晋升默认导出
跨版本修复集群 0.68.x / 0.75.4 / 0.79.9 / 0.80.4 / 0.84.x 并行工具语义、Windows/WSL 兼容、compaction 串行化、streamProxy 元数据保真

下表从 CHANGELOG 中抽取了信息量最大的版本节点,方便按需跳读:

版本 日期 代表性条目
0.84.4 2026-08-28 Breaking:prepareNextTurn/prepareNextTurnWithContext 只在循环确定还要再跑一个回合时才执行;修复 Windows NodeExecutionEnv 在无 taskkill.exe 时 abort 崩溃
0.84.1 2026-08-07 BeforeToolCallResult.terminate 允许被拦截的工具调用参与批量提前终止;Agent.reset() 在运行中拒绝
0.84.0 2026-08-06 会话仓库 v4 大重构;AgentOptions.shouldStopAfterTurn;代理转发任意 samplingParams;telemetry schema 与生成文档
0.82.0 2026-07-24 Harness 改用应用定义的 toolContextAgentHarnessTool;提供上下文感知的 read/write/edit/bash 工具
0.81.0 2026-07-21 SessionStorage 增加 getPathToRootOrCompaction() 等契约;uuidv7 移入 pi-ai;streamFunction 变为必需
0.75.0 2026-05-17 最低 Node.js 版本提升到 22.19.0
0.69.0 2026-04-22 typebox 1.x 迁移;工具结果 terminate: true 提示可跳过后续 LLM 调用
0.65.0 2026-04-03 AgentState 重塑:setter 方法全部移除,改为直接属性访问;subscribe() 监听器可被 await
0.58.0 2026-03-14 引入 beforeToolCall/afterToolCall 钩子;toolExecution: "parallel" | "sequential"
0.32.0 2026-01-03 队列 API 拆分为 steer()/followUp(),语义完全不同
0.31.0 2026-01-02 移除 Transport 抽象,改用 streamFn;新增 streamProxy()、低层 agentLoop()/agentLoopContinue()

三、主线一:公共 API 的四次形态重构

这条主线决定了"你的代码要怎么写"。按时间顺序:

0.31.0(2026-01-02)——Transport 抽象被整体移除。 ProviderTransportAppTransportAgentTransport 接口删除,自定义流实现改为通过 streamFn 选项注入;messageTransformer 更名 convertToLlmpreprocessor 更名 transformContextAppMessage 更名 AgentMessage。同时新增 streamProxy() 供浏览器应用经后端代理 LLM 调用,以及低层 agentLoop()/agentLoopContinue()。低层函数的现状可以在 agent-loop.ts 中找到:runLoop() 是两者共享的主循环,shouldStopAfterTurnprepareNextTurn 正是通过 AgentLoopConfig 传入的。

0.32.0(2026-01-03)——队列拆分为 steer/followUp。 queueMessage() 一分为二:steer(msg) 在当前工具批次完成后立即注入(中途转向),followUp(msg) 等到 agent 本应停止时才投递(排队追加)。配套引入 steeringMode/followUpMode"one-at-a-time""all"),以及 clearSteeringQueue()/clearFollowUpQueue()/clearAllQueues()。这一结构在 agent.ts 中体现为两个独立的 PendingMessageQueue 实例,构造时默认 "one-at-a-time"(对应 AgentOptions.steeringMode/followUpMode,见 agent.ts#L216-L238)。

0.65.0(2026-04-03)——AgentState 重塑。 这是最容易被忽略但影响面很大的 Breaking Change:

  • streamMessagestreamingMessageerrorerrorMessageisStreamingstreamingMessagependingToolCallserrorMessage 变为只读(pendingToolCalls 类型为 ReadonlySet<string>);
  • 所有 agent.setXxx() 变更方法删除,改为直接对 agent.state 赋值:agent.state.systemPrompt = ...agent.state.model = ...agent.state.thinkingLevel = ...agent.state.tools = ...agent.state.messages = ...(赋值会复制顶层数组);
  • AgentOptions.initialState 不再接受运行时私有字段;
  • agent.subscribe() 监听器变为可 await 的异步回调并接收活跃 AbortSignalagent_end 之后的订阅者结算完成前,waitForIdle()prompt()continue() 不会 resolve。

0.81.0(2026-07-21)——streamFunction 变为必需。 低层循环的流函数不再允许隐式回退,防止内置 provider 被意外打进选择性打包的 bundle;uuidv7 导出随之移入 @earendil-works/pi-ai。需要注意 CHANGELOG 随后在 0.81.1 中恢复了 AgentstreamFn 选项与宿主可配置的默认回退,agent.ts#L216-L238runtimeOptions.streamFn ?? getDefaultStreamFn() 以及 stream-fn.tssetDefaultStreamFn 就是这条"恢复兼容"落地的位置。

四、主线二:钩子与回合控制系统(本文重点)

这是 CHANGELOG 中条目最密集、也最能体现 agent loop 设计思想的一条线。

4.1 工具钩子:0.58.0 到 0.84.1 的补齐

  • 0.58.0 引入 beforeToolCall(预检拦截,参数校验后执行)与 afterToolCall(执行后改写结果),并引入 toolExecution: "parallel" | "sequential",默认 parallel:并行模式按顺序做预检、并发执行被允许的工具,最终按 assistant 源顺序产出 toolResult;
  • 0.64.0 增加 AgentTool.prepareArguments:在 schema 校验前改写原始参数,用于兼容"旧工具 schema 的恢复会话";
  • 0.67.67 修复并行工具批次的收尾语义:afterToolCall 抛错会转换为 error 工具结果而不是中止整个批次(#3084);
  • 0.69.0 引入 terminate: true 工具结果提示:当当前批次所有已最终化的工具结果都声明终止时才跳过自动的后续 LLM 调用,混合批次照常继续(#3525);
  • 0.84.1 补齐 BeforeToolCallResult.terminate:被拦截(block)的工具调用也能参与上述"全批次终止"判定(#7715)。

这套语义与 README 的"Tools"一节一致:execute()、被拦截的 beforeToolCallafterToolCall 三处返回 terminate: true 都只是运行时提示,落盘的 toolResult 消息仍是标准 LLM 工具结果。

4.2 回合控制:shouldStopAfterTurn 与 prepareNextTurn

0.72.0 首次把 shouldStopAfterTurn 加入低层循环配置,0.84.0 将其提升到 AgentOptions。其契约在 types.ts#L213-L223 中有完整注释:在回合完全完成、turn_end 已发出之后调用;返回 true 时循环发出 agent_end 并退出,且不会去轮询 steering/follow-up 队列、不会发起新的 LLM 调用,也不中断 provider 流或正在运行的工具。入参类型 ShouldStopAfterTurnContexttypes.ts#L125-L135)包含完成的 assistant 消息、该回合的 toolResults、回合结束后的 context,以及本次运行最终将返回的 newMessages

主循环中的实际执行点在 agent-loop.ts#L243-L257turn_end 发出、lastCompletedTurn 快照构建完成后立即检查 shouldStopAfterTurn,为真则 agent_end 并 return;否则才轮询 steering 消息进入下一轮。典型用途是"上下文快满时先优雅收手,交给 compaction"。

0.80.3 增加 prepareNextTurnWithContext(低层 prepareNextTurn 原本只接收 AbortSignal),让需要完整回合上下文的宿主可以拿到"上一回合消息 + 工具结果 + 当前上下文"。返回值 AgentLoopTurnUpdatetypes.ts#L137-L147)可替换下一回合的 contextmodelthinkingLevel——即逐回合动态换模型、换思考级别的能力。

0.84.4 的 Breaking Change 值得单独强调prepareNextTurn/prepareNextTurnWithContext 现在只在 shouldStopAfterTurn 与排队消息检查都确认循环还要再开一个 assistant 回合之后才运行,不再在最终回合或终止回合后运行。从源码结构看,agent-loop.ts#L176-L198prepareNextTurn 的调用位于内层 while第二次迭代入口lastCompletedTurn 非空时);而 0.84.4 之前该回调还会在收尾阶段被触发。因此如果你曾把"收尾工作"(落盘、统计、发通知)挂在 prepareNextTurn 上,升级到 0.84.4 后应将其迁移到 agent_end 事件处理——Agent 类的订阅者会在 agent_end 上被 await,是官方推荐的终止屏障位置。

4.3 相关配套

  • 0.63.2 增加 Agent.signal:暴露当前回合的活跃 AbortSignal,便于把取消传导进嵌套异步工作;
  • 0.58.4 修复 steering 消息时机:必须等当前 assistant 消息的工具批次完全结束才注入,而不是跳过尚待执行的工具调用;
  • 0.50.8 增加 maxRetryDelayMs 上限,约束服务端要求的重试延迟;
  • 0.52.12 / 0.72.1 引入并随后把默认 transport 设为 "auto""sse" / "websocket" / "auto" 可传导至 provider 调用)。

五、主线三:0.84.0 的会话仓库 v4 重构

0.84.0 是整份 CHANGELOG 中 Breaking 条目最重的一个版本,核心是用 v4 车道(lane)化模型替换旧 harness 会话模型:

  • 新 API 面SessionSessionStorageSessionRepo,带来持久化操作记录(durable operation records)、全局事实(global facts)、共享序列号(shared sequence numbers)、树作用域的车道视图(tree-scoped lane views);
  • 旧 API 去留:旧 JSONL 与内存仓库 API 移除,替代物是都实现 SessionRepo 契约的 v4 JsonlSessionRepoInMemorySessionRepo
  • 文件系统契约收紧:harness 执行环境新增必需FileSystem.renameFile() 操作,用于 JSONL 会话的原子发布;自定义文件系统实现必须提供"同一文件系统内的替换语义";
  • 查询增强:有界的 Session.findEntriesOnBranch()/findEntryOnBranch()(显式遍历、过滤、排序与 limit 选项),以及索引化的 findOpenOperations() 恢复查询与 RecordQuery.operationKind 过滤(#7646);
  • 原子性修复同版本落地:JSONL 会话分叉与"撕裂尾部"(torn-tail)修复现在原子发布,避免中断写入留下半写/损坏会话(#7707);session ID 改为按工作目录唯一,而非全局唯一;
  • harness v2 晋升:v2 会话与 AgentHarness API 从 experimental 入口晋升为默认包导出,同时提供"编译完整"的 AgentHarness v2 scaffold——未完成的操作路径会以 HarnessNotImplemented 拒绝,直到持久化执行实现补齐;
  • 可观测性:新增类型化的 AI 请求与 harness telemetry schema、组合 schema 元组、回调辅助函数,以及由脚本生成的 schema 参考文档(对应 generate-telemetry-docs.tsdocs/telemetry-schema.md)。

从源码结构看,这些导出确实存在:src/index.ts 直接 export * from "./harness/session/index.ts"export * from "./harness/agent-harness.ts",并无独立 experimental 子路径;package.json 的 exports 表只保留 ../node./session/testing,印证了"experimental 子路径已被移除"的记载。

同版本还包含两项对上层很有用的新增:AgentOptions.shouldStopAfterTurn(见上一节)与代理对任意 OpenAI 兼容 samplingParams 的转发——后者在 proxy.ts#L62 的类型白名单与 proxy.ts#L105 的透传逻辑中可以得到印证。

六、主线四:Harness 工具模型与 Models 唯一认证路径

  • 0.82.0AgentHarnessExecutionEnv 依赖与无上下文的 AgentTool 输入,被应用定义的 toolContext 值与上下文感知的 AgentHarnessTool 定义取代;read/write/edit/bash 四个 harness 工具因此获得上下文感知实现(含 bash 异步执行准备),并且路径处理、edit 序列化、shell 输出捕获、显式非继承环境、跨平台进程清理全部对齐 coding-agent 行为;
  • 0.80.0AgentHarnessOptions.models 变为必需且是唯一认证路径——harness 的每回合流式、compaction、branch summarization 都经由提供的 Models 实例(models.streamSimple()/completeSimple())解析 provider 认证;getApiKeyAndHeaders 移除,compact()/generateSummary()/generateBranchSummary() 改为接收 Models 参数;StreamFn 被结构化为 (model, context, options?) => AssistantMessageEventStream | Promise<...>
  • 0.81.0:compaction 与分支摘要请求改用全新路由会话 ID 并(在支持时)关闭提示缓存(#6618),工具结果、compaction 条目与分支摘要携带 usage 元数据(#6671)。

七、修复条目中的跨版本模式(升级时的回归参考)

CHANGELOG 的 Fixed 条目看似零散,实际反复出现在几类问题上,升级跨这些版本时可以重点回归:

  1. 并行工具执行语义:0.68.1 修复 tool_execution_end 应随每个工具最终化立即发出、而持久化 toolResult 消息仍按 assistant 源顺序发出(#3503);0.75.4 修复 abort 后不再继续准备兄弟工具调用的预检(#4276);
  2. 截断消息保护:0.80.4 让来自长度截断(stop reason length)assistant 消息的工具调用直接失败而不是空等不存在的工具结果(#6285);0.75.4 修复以尾随换行结尾的超长单行输出的尾部截断(#4715);0.80.0 修复 compaction 估算忽略"全零 usage"的截断响应(#5526);
  3. Windows / WSL 兼容:0.79.9 让经 WSL bash.exe 的命令改走 stdin 传脚本以保证 shell 变量展开(#5893);0.75.4 隐藏后台进程的控制台窗口(#4699);0.84.4 修复 taskkill.exe 缺失时 NodeExecutionEnv abort 崩溃(#6596);0.84.0 修复文件 basename、递归 skill 加载与 prompt 模板名的 Windows 路径处理;
  4. JSONL 会话完整性:0.80.4 起短 entry id 改用 uuidv7 随机尾段(避免时间戳前缀近乎恒定,#6242)、JSONL 头支持自定义元数据(#6417);0.84.0 完成分叉与撕裂尾的原子发布;
  5. 代理与流选项保真:0.68.1 修复 streamProxy() 保留可序列化子集(session、transport、retry-delay、metadata、header、cache-retention、thinking-budget,#3512);0.84.2 修复 finalized 工具调用元数据(如 OpenAI Responses namespace)在代理路径上丢失(#7709);
  6. compaction 串行化:0.80.4 修复 harness 跨回合 compaction 串行化摘要请求,避免单并发 provider 被要求并行生成(#5536);
  7. Unreleased:write 工具此前把 UTF-16 码元计数误报为字节计数,现已移除误导性计数(#8979)。

八、实操:升级验证与快速上手

运行环境:Node.js ≥ 22.19.0。在 monorepo 根目录可用 pi-test.sh 或包内脚本 npm run test(即 vitest --run,配置见 vitest.config.ts)运行核心测试;harness 测试由独立配置 vitest.harness.config.ts 驱动(npm run test:harness)。agent-loop.test.tsagent.test.ts 中包含 shouldStopAfterTurnprepareNextTurn 的时序用例,是验证第四节行为的最直接依据。

最小可运行示例(继承自 README,体现 0.80.0 之后的 Models 认证路径):

import { Agent } from "@earendil-works/pi-agent-core";
import { createModels } from "@earendil-works/pi-ai";
import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic";

const models = createModels();
models.setProvider(anthropicProvider());
const model = models.getModel("anthropic", "claude-sonnet-4-6");
if (!model) throw new Error("Model not found");

const agent = new Agent({
  initialState: {
    systemPrompt: "You are a helpful assistant.",
    model,
  },
  streamFn: models.streamSimple.bind(models),
});

agent.subscribe((event) => {
  if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await agent.prompt("Hello!");

升级检查清单(由 CHANGELOG 直接推导):

  1. 跨 0.84.0:确认你没有引用旧 JSONL/内存仓库 API 或 experimental subpath;自定义 FileSystem 是否实现了 renameFile();harness 是否改用 Models 认证路径;
  2. 跨 0.84.4:把 prepareNextTurn 上承载的收尾逻辑迁到 agent_end 订阅者;
  3. 跨 0.81.0:显式提供 streamFunction/streamFn,不再依赖隐式回退;uuidv7 改从 @earendil-works/pi-ai 导入;
  4. 跨 0.65.0:删除所有 agent.setXxx() 调用,改用 agent.state 属性;订阅回调改为 async 并善用第二个参数 AbortSignal
  5. 跨 0.32.0:确认消息注入场景用的是 steer()(中途转向)还是 followUp()(排队追加),二者投递语义完全不同;
  6. 跨 0.69.0:工具参数 schema 的 import 从 @sinclair/typebox 改为 typebox

九、结语

packages/agent 的 CHANGELOG 并不只是一份修补记录,它完整刻画了 pi agent-core 的设计取向:以事件流为 UI 契约、以回合级钩子(shouldStopAfterTurn/prepareNextTurn)为控制点、以 terminate 提示把"是否继续"的决策权交还调用方、以 v4 会话仓库保证转录与操作记录的持久化正确性。对二次开发者而言,把 Breaking Changes 按本文四、五节的版本区间逐一核对,再辅以 packages/agent/test 下的对应测试用例做回归,就足以安全地跟随这个快速演进的包前进。

登录后查看全文
热门项目推荐
相关项目推荐