openclaw/docs/zh-CN/pi.md

608 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
title: Pi 集成架构
x-i18n:
generated_at: "2026-02-01T21:21:38Z"
model: claude-opus-4-5
provider: pi
source_hash: 18639af549c00f8be1acea69dd4751ee158760607b50c0f7d475d5350a97c27a
source_path: pi.md
workflow: 15
---
# Pi 集成架构
本文档描述了 OpenClaw 如何与 [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) 及其兄弟包(`pi-ai`、`pi-agent-core`、`pi-tui`)集成,以驱动其 AI 智能体能力。
## 概述
OpenClaw 使用 pi SDK 将 AI 编程智能体嵌入其消息 Gateway网关架构中。OpenClaw 不是将 pi 作为子进程启动或使用 RPC 模式,而是直接导入并通过 `createAgentSession()` 实例化 pi 的 `AgentSession`。这种嵌入式方法提供了:
- 对会话生命周期和事件处理的完全控制
- 自定义工具注入(消息、沙箱、渠道特定操作)
- 按渠道/上下文自定义系统提示词
- 支持分支/压缩的会话持久化
- 多账户认证配置文件轮换与故障转移
- 提供商无关的模型切换
## 包依赖
```json
{
"@mariozechner/pi-agent-core": "0.49.3",
"@mariozechner/pi-ai": "0.49.3",
"@mariozechner/pi-coding-agent": "0.49.3",
"@mariozechner/pi-tui": "0.49.3"
}
```
| 包 | 用途 |
| ----------------- | ------------------------------------------------------------------------------------------ |
| `pi-ai` | 核心 LLM 抽象:`Model`、`streamSimple`、消息类型、提供商 API |
| `pi-agent-core` | 智能体循环、工具执行、`AgentMessage` 类型 |
| `pi-coding-agent` | 高级 SDK`createAgentSession`、`SessionManager`、`AuthStorage`、`ModelRegistry`、内置工具 |
| `pi-tui` | 终端 UI 组件(用于 OpenClaw 的本地 TUI 模式) |
## 文件结构
```
src/agents/
├── pi-embedded-runner.ts # 从 pi-embedded-runner/ 重新导出
├── pi-embedded-runner/
│ ├── run.ts # 主入口runEmbeddedPiAgent()
│ ├── run/
│ │ ├── attempt.ts # 带会话设置的单次尝试逻辑
│ │ ├── params.ts # RunEmbeddedPiAgentParams 类型
│ │ ├── payloads.ts # 从运行结果构建响应载荷
│ │ ├── images.ts # 视觉模型图片注入
│ │ └── types.ts # EmbeddedRunAttemptResult
│ ├── abort.ts # 中止错误检测
│ ├── cache-ttl.ts # 上下文裁剪的缓存 TTL 跟踪
│ ├── compact.ts # 手动/自动压缩逻辑
│ ├── extensions.ts # 为嵌入式运行加载 pi 扩展
│ ├── extra-params.ts # 提供商特定的流参数
│ ├── google.ts # Google/Gemini 轮次排序修复
│ ├── history.ts # 历史记录限制(私聊 vs 群聊)
│ ├── lanes.ts # 会话/全局命令通道
│ ├── logger.ts # 子系统日志记录器
│ ├── model.ts # 通过 ModelRegistry 解析模型
│ ├── runs.ts # 活跃运行跟踪、中止、队列
│ ├── sandbox-info.ts # 系统提示词的沙箱信息
│ ├── session-manager-cache.ts # SessionManager 实例缓存
│ ├── session-manager-init.ts # 会话文件初始化
│ ├── system-prompt.ts # 系统提示词构建器
│ ├── tool-split.ts # 将工具拆分为内置 vs 自定义
│ ├── types.ts # EmbeddedPiAgentMeta、EmbeddedPiRunResult
│ └── utils.ts # ThinkLevel 映射、错误描述
├── pi-embedded-subscribe.ts # 会话事件订阅/分发
├── pi-embedded-subscribe.types.ts # SubscribeEmbeddedPiSessionParams
├── pi-embedded-subscribe.handlers.ts # 事件处理器工厂
├── pi-embedded-subscribe.handlers.lifecycle.ts
├── pi-embedded-subscribe.handlers.types.ts
├── pi-embedded-block-chunker.ts # 流式块回复分块
├── pi-embedded-messaging.ts # 消息工具发送跟踪
├── pi-embedded-helpers.ts # 错误分类、轮次验证
├── pi-embedded-helpers/ # 辅助模块
├── pi-embedded-utils.ts # 格式化工具
├── pi-tools.ts # createOpenClawCodingTools()
├── pi-tools.abort.ts # 工具的 AbortSignal 封装
├── pi-tools.policy.ts # 工具允许/拒绝列表策略
├── pi-tools.read.ts # Read 工具自定义
├── pi-tools.schema.ts # 工具 schema 规范化
├── pi-tools.types.ts # AnyAgentTool 类型别名
├── pi-tool-definition-adapter.ts # AgentTool -> ToolDefinition 适配器
├── pi-settings.ts # 设置覆盖
├── pi-extensions/ # 自定义 pi 扩展
│ ├── compaction-safeguard.ts # 安全防护扩展
│ ├── compaction-safeguard-runtime.ts
│ ├── context-pruning.ts # 基于缓存 TTL 的上下文裁剪扩展
│ └── context-pruning/
├── model-auth.ts # 认证配置文件解析
├── auth-profiles.ts # 配置文件存储、冷却、故障转移
├── model-selection.ts # 默认模型解析
├── models-config.ts # models.json 生成
├── model-catalog.ts # 模型目录缓存
├── context-window-guard.ts # 上下文窗口验证
├── failover-error.ts # FailoverError 类
├── defaults.ts # DEFAULT_PROVIDER、DEFAULT_MODEL
├── system-prompt.ts # buildAgentSystemPrompt()
├── system-prompt-params.ts # 系统提示词参数解析
├── system-prompt-report.ts # 调试报告生成
├── tool-summaries.ts # 工具描述摘要
├── tool-policy.ts # 工具策略解析
├── transcript-policy.ts # 转录验证策略
├── skills.ts # Skills 快照/提示词构建
├── skills/ # Skills 子系统
├── sandbox.ts # 沙箱上下文解析
├── sandbox/ # 沙箱子系统
├── channel-tools.ts # 渠道特定工具注入
├── openclaw-tools.ts # OpenClaw 特定工具
├── bash-tools.ts # exec/process 工具
├── apply-patch.ts # apply_patch 工具OpenAI
├── tools/ # 各工具实现
│ ├── browser-tool.ts
│ ├── canvas-tool.ts
│ ├── cron-tool.ts
│ ├── discord-actions*.ts
│ ├── gateway-tool.ts
│ ├── image-tool.ts
│ ├── message-tool.ts
│ ├── nodes-tool.ts
│ ├── session*.ts
│ ├── slack-actions.ts
│ ├── telegram-actions.ts
│ ├── web-*.ts
│ └── whatsapp-actions.ts
└── ...
```
## 核心集成流程
### 1. 运行嵌入式智能体
主入口是 `pi-embedded-runner/run.ts` 中的 `runEmbeddedPiAgent()`
```typescript
import { runEmbeddedPiAgent } from "./agents/pi-embedded-runner.js";
const result = await runEmbeddedPiAgent({
sessionId: "user-123",
sessionKey: "main:whatsapp:+1234567890",
sessionFile: "/path/to/session.jsonl",
workspaceDir: "/path/to/workspace",
config: openclawConfig,
prompt: "Hello, how are you?",
provider: "anthropic",
model: "claude-sonnet-4-20250514",
timeoutMs: 120_000,
runId: "run-abc",
onBlockReply: async (payload) => {
await sendToChannel(payload.text, payload.mediaUrls);
},
});
```
### 2. 会话创建
`runEmbeddedAttempt()`(由 `runEmbeddedPiAgent()` 调用)内部,使用 pi SDK
```typescript
import { createAgentSession, SessionManager, SettingsManager } from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({
cwd: resolvedWorkspace,
agentDir,
authStorage: params.authStorage,
modelRegistry: params.modelRegistry,
model: params.model,
thinkingLevel: mapThinkingLevel(params.thinkLevel),
systemPrompt: createSystemPromptOverride(appendPrompt),
tools: builtInTools,
customTools: allCustomTools,
sessionManager,
settingsManager,
skills: [],
contextFiles: [],
additionalExtensionPaths,
});
```
### 3. 事件订阅
`subscribeEmbeddedPiSession()` 订阅 pi 的 `AgentSession` 事件:
```typescript
const subscription = subscribeEmbeddedPiSession({
session: activeSession,
runId: params.runId,
verboseLevel: params.verboseLevel,
reasoningMode: params.reasoningLevel,
toolResultFormat: params.toolResultFormat,
onToolResult: params.onToolResult,
onReasoningStream: params.onReasoningStream,
onBlockReply: params.onBlockReply,
onPartialReply: params.onPartialReply,
onAgentEvent: params.onAgentEvent,
});
```
处理的事件包括:
- `message_start` / `message_end` / `message_update`(流式文本/思考)
- `tool_execution_start` / `tool_execution_update` / `tool_execution_end`
- `turn_start` / `turn_end`
- `agent_start` / `agent_end`
- `auto_compaction_start` / `auto_compaction_end`
### 4. 提示
设置完成后,对会话进行提示:
```typescript
await session.prompt(effectivePrompt, { images: imageResult.images });
```
SDK 处理完整的智能体循环:发送到 LLM、执行工具调用、流式输出响应。
## 工具架构
### 工具管道
1. **基础工具**pi 的 `codingTools`read、bash、edit、write
2. **自定义替换**OpenClaw 用 `exec`/`process` 替换 bash为沙箱自定义 read/edit/write
3. **OpenClaw 工具**消息、浏览器、画布、会话、定时任务、Gateway网关等
4. **渠道工具**Discord/Telegram/Slack/WhatsApp 特定操作工具
5. **策略过滤**:通过配置文件、提供商、智能体、群组、沙箱策略过滤工具
6. **Schema 规范化**:为 Gemini/OpenAI 的特殊要求清理 schema
7. **AbortSignal 封装**:封装工具以响应中止信号
### 工具定义适配器
pi-agent-core 的 `AgentTool` 与 pi-coding-agent 的 `ToolDefinition` 具有不同的 `execute` 签名。`pi-tool-definition-adapter.ts` 中的适配器对此进行了桥接:
```typescript
export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
return tools.map((tool) => ({
name: tool.name,
label: tool.label ?? name,
description: tool.description ?? "",
parameters: tool.parameters,
execute: async (toolCallId, params, onUpdate, _ctx, signal) => {
// pi-coding-agent 签名与 pi-agent-core 不同
return await tool.execute(toolCallId, params, signal, onUpdate);
},
}));
}
```
### 工具拆分策略
`splitSdkTools()` 将所有工具通过 `customTools` 传递:
```typescript
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) {
return {
builtInTools: [], // 空。我们覆盖所有内容
customTools: toToolDefinitions(options.tools),
};
}
```
这确保了 OpenClaw 的策略过滤、沙箱集成和扩展工具集在各提供商之间保持一致。
## 系统提示词构建
系统提示词在 `buildAgentSystemPrompt()``system-prompt.ts`中构建。它组装一个包含以下部分的完整提示词工具、工具调用风格、安全防护、OpenClaw CLI 参考、Skills、文档、工作区、沙箱、消息、回复标签、语音、静默回复、心跳、运行时元数据以及启用时的记忆和反应还有可选的上下文文件和额外系统提示词内容。子智能体使用的最小提示词模式会裁剪部分章节。
提示词通过 `systemPrompt` 覆盖传递给 pi
```typescript
const systemPrompt = createSystemPromptOverride(appendPrompt);
// 返回:(defaultPrompt: string) => 裁剪后的自定义提示词
```
## 会话管理
### 会话文件
会话是具有树结构id/parentId 链接)的 JSONL 文件。Pi 的 `SessionManager` 处理持久化:
```typescript
const sessionManager = SessionManager.open(params.sessionFile);
```
OpenClaw 通过 `guardSessionManager()` 封装它以确保工具结果安全。
### 会话缓存
`session-manager-cache.ts` 缓存 SessionManager 实例以避免重复文件解析:
```typescript
await prewarmSessionFile(params.sessionFile);
sessionManager = SessionManager.open(params.sessionFile);
trackSessionManagerAccess(params.sessionFile);
```
### 历史记录限制
`limitHistoryTurns()` 根据渠道类型(私聊 vs 群聊)裁剪对话历史。
### 压缩
自动压缩在上下文溢出时触发。`compactEmbeddedPiSessionDirect()` 处理手动压缩:
```typescript
const compactResult = await compactEmbeddedPiSessionDirect({
sessionId, sessionFile, provider, model, ...
});
```
## 认证与模型解析
### 认证配置文件
OpenClaw 维护一个认证配置文件存储,每个提供商可有多个 API 密钥:
```typescript
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });
```
配置文件在失败时轮换,并带有冷却跟踪:
```typescript
await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });
const rotated = await advanceAuthProfile();
```
### 模型解析
```typescript
import { resolveModel } from "./pi-embedded-runner/model.js";
const { model, error, authStorage, modelRegistry } = resolveModel(
provider,
modelId,
agentDir,
config,
);
// 使用 pi 的 ModelRegistry 和 AuthStorage
authStorage.setRuntimeApiKey(model.provider, apiKeyInfo.apiKey);
```
### 故障转移
`FailoverError` 在配置了故障转移时触发模型回退:
```typescript
if (fallbackConfigured && isFailoverErrorMessage(errorText)) {
throw new FailoverError(errorText, {
reason: promptFailoverReason ?? "unknown",
provider,
model: modelId,
profileId,
status: resolveFailoverStatus(promptFailoverReason),
});
}
```
## Pi 扩展
OpenClaw 加载自定义 pi 扩展以实现特定行为:
### 压缩安全防护
`pi-extensions/compaction-safeguard.ts` 为压缩添加防护机制,包括自适应令牌预算以及工具失败和文件操作摘要:
```typescript
if (resolveCompactionMode(params.cfg) === "safeguard") {
setCompactionSafeguardRuntime(params.sessionManager, { maxHistoryShare });
paths.push(resolvePiExtensionPath("compaction-safeguard"));
}
```
### 上下文裁剪
`pi-extensions/context-pruning.ts` 实现基于缓存 TTL 的上下文裁剪:
```typescript
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
setContextPruningRuntime(params.sessionManager, {
settings,
contextWindowTokens,
isToolPrunable,
lastCacheTouchAt,
});
paths.push(resolvePiExtensionPath("context-pruning"));
}
```
## 流式传输与块回复
### 块分块
`EmbeddedBlockChunker` 管理将流式文本拆分为离散的回复块:
```typescript
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;
```
### 思考/最终标签剥离
流式输出会被处理以剥离 `<think>`/`<thinking>` 块并提取 `<final>` 内容:
```typescript
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => {
// 剥离 <think>...</think> 内容
// 如果 enforceFinalTag仅返回 <final>...</final> 内容
};
```
### 回复指令
`[[media:url]]`、`[[voice]]`、`[[reply:id]]` 等回复指令会被解析和提取:
```typescript
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);
```
## 错误处理
### 错误分类
`pi-embedded-helpers.ts` 对错误进行分类以进行适当处理:
```typescript
isContextOverflowError(errorText) // 上下文过大
isCompactionFailureError(errorText) // 压缩失败
isAuthAssistantError(lastAssistant) // 认证失败
isRateLimitAssistantError(...) // 速率限制
isFailoverAssistantError(...) // 应进行故障转移
classifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "timeout" | ...
```
### 思考级别回退
如果不支持某个思考级别,则进行回退:
```typescript
const fallbackThinking = pickFallbackThinkingLevel({
message: errorText,
attempted: attemptedThinking,
});
if (fallbackThinking) {
thinkLevel = fallbackThinking;
continue;
}
```
## 沙箱集成
启用沙箱模式后,工具和路径会受到约束:
```typescript
const sandbox = await resolveSandboxContext({
config: params.config,
sessionKey: sandboxSessionKey,
workspaceDir: resolvedWorkspace,
});
if (sandboxRoot) {
// 使用沙箱隔离的 read/edit/write 工具
// exec 在容器中运行
// 浏览器使用桥接 URL
}
```
## 提供商特定处理
### Anthropic
- 拒绝魔术字符串清理
- 连续角色的轮次验证
- Claude Code 参数兼容性
### Google/Gemini
- 轮次排序修复(`applyGoogleTurnOrderingFix`
- 工具 schema 清理(`sanitizeToolsForGoogle`
- 会话历史清理(`sanitizeSessionHistory`
### OpenAI
- Codex 模型的 `apply_patch` 工具
- 思考级别降级处理
## TUI 集成
OpenClaw 还有一个直接使用 pi-tui 组件的本地 TUI 模式:
```typescript
// src/tui/tui.ts
import { ... } from "@mariozechner/pi-tui";
```
这提供了类似于 pi 原生模式的交互式终端体验。
## 与 Pi CLI 的主要区别
| 方面 | Pi CLI | OpenClaw 嵌入式 |
| ---------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
| 调用方式 | `pi` 命令 / RPC | 通过 `createAgentSession()` 使用 SDK |
| 工具 | 默认编程工具 | 自定义 OpenClaw 工具套件 |
| 系统提示词 | AGENTS.md + 提示词 | 按渠道/上下文动态生成 |
| 会话存储 | `~/.pi/agent/sessions/` | `~/.openclaw/agents/<agentId>/sessions/`(或 `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` |
| 认证 | 单一凭据 | 多配置文件轮换 |
| 扩展 | 从磁盘加载 | 编程式 + 磁盘路径 |
| 事件处理 | TUI 渲染 | 基于回调onBlockReply 等) |
## 未来考虑
可能需要重构的领域:
1. **工具签名对齐**:目前在 pi-agent-core 和 pi-coding-agent 签名之间进行适配
2. **SessionManager 封装**`guardSessionManager` 增加了安全性但也增加了复杂度
3. **扩展加载**:可以更直接地使用 pi 的 `ResourceLoader`
4. **流式处理器复杂度**`subscribeEmbeddedPiSession` 已经变得很大
5. **提供商特殊处理**许多提供商特定的代码路径pi 可能可以统一处理
## 测试
所有涵盖 pi 集成及其扩展的现有测试:
- `src/agents/pi-embedded-block-chunker.test.ts`
- `src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts`
- `src/agents/pi-embedded-helpers.classifyfailoverreason.test.ts`
- `src/agents/pi-embedded-helpers.downgradeopenai-reasoning.test.ts`
- `src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts`
- `src/agents/pi-embedded-helpers.formatrawassistanterrorforui.test.ts`
- `src/agents/pi-embedded-helpers.image-dimension-error.test.ts`
- `src/agents/pi-embedded-helpers.image-size-error.test.ts`
- `src/agents/pi-embedded-helpers.isautherrormessage.test.ts`
- `src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts`
- `src/agents/pi-embedded-helpers.iscloudcodeassistformaterror.test.ts`
- `src/agents/pi-embedded-helpers.iscompactionfailureerror.test.ts`
- `src/agents/pi-embedded-helpers.iscontextoverflowerror.test.ts`
- `src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts`
- `src/agents/pi-embedded-helpers.islikelycontextoverflowerror.test.ts`
- `src/agents/pi-embedded-helpers.ismessagingtoolduplicate.test.ts`
- `src/agents/pi-embedded-helpers.messaging-duplicate.test.ts`
- `src/agents/pi-embedded-helpers.normalizetextforcomparison.test.ts`
- `src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.test.ts`
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.keeps-tool-call-tool-result-ids-unchanged.test.ts`
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.test.ts`
- `src/agents/pi-embedded-helpers.sanitizegoogleturnordering.test.ts`
- `src/agents/pi-embedded-helpers.sanitizesessionmessagesimages-thought-signature-stripping.test.ts`
- `src/agents/pi-embedded-helpers.sanitizetoolcallid.test.ts`
- `src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts`
- `src/agents/pi-embedded-helpers.stripthoughtsignatures.test.ts`
- `src/agents/pi-embedded-helpers.validate-turns.test.ts`
- `src/agents/pi-embedded-runner-extraparams.live.test.ts`(实时测试)
- `src/agents/pi-embedded-runner-extraparams.test.ts`
- `src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts`
- `src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts`
- `src/agents/pi-embedded-runner.createsystempromptoverride.test.ts`
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts`
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts`
- `src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts`
- `src/agents/pi-embedded-runner.guard.test.ts`
- `src/agents/pi-embedded-runner.limithistoryturns.test.ts`
- `src/agents/pi-embedded-runner.resolvesessionagentids.test.ts`
- `src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts`
- `src/agents/pi-embedded-runner.sanitize-session-history.test.ts`
- `src/agents/pi-embedded-runner.splitsdktools.test.ts`
- `src/agents/pi-embedded-runner.test.ts`
- `src/agents/pi-embedded-subscribe.code-span-awareness.test.ts`
- `src/agents/pi-embedded-subscribe.reply-tags.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.calls-onblockreplyflush-before-tool-execution-start-preserve.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-append-text-end-content-is.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-call-onblockreplyflush-callback-is-not.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-duplicate-text-end-repeats-full.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.includes-canvas-action-metadata-tool-summaries.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-indented-fenced-blocks-intact.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.reopens-fenced-blocks-splitting-inside-them.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.streams-soft-chunks-paragraph-preference.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.waits-multiple-compaction-retries-before-resolving.test.ts`
- `src/agents/pi-embedded-subscribe.tools.test.ts`
- `src/agents/pi-embedded-utils.test.ts`
- `src/agents/pi-extensions/compaction-safeguard.test.ts`
- `src/agents/pi-extensions/context-pruning.test.ts`
- `src/agents/pi-settings.test.ts`
- `src/agents/pi-tool-definition-adapter.test.ts`
- `src/agents/pi-tools-agent-config.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-b.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-d.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-f.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.test.ts`
- `src/agents/pi-tools.policy.test.ts`
- `src/agents/pi-tools.safe-bins.test.ts`
- `src/agents/pi-tools.workspace-paths.test.ts`