# pi-cache-stack

`pi-cache-stack` 是运行在官方 Pi 上的工具发现与 active-tool policy 扩展。它只负责：

1. **最小初始工具集**：保留 `lazy` gateway、默认基础工具和配置中的 `alwaysActive` 工具。
2. **工具发现**：通过 `lazy search` 或 `/lazy search` 查找未激活的注册工具。
3. **会话内激活策略**：`lazy activate` 使用 `setActiveTools(current + additions)` 纯增量地加入工具，并在后续轮次保持。
4. **外部 ownership 保留**：`externallyManaged` 工具由 owning extension 决定是否 active；cache-stack 只保留当前状态，不主动添加，也不允许 lazy 绕过 owner policy。
5. **策略诊断**：显示 active tools、请求体大小、disabled/externally-managed 工具、模型策略和官方 fallback 边界。
6. **工具输出边界**：在最终工具结果进入模型与会话上下文前，对指定工具执行可配置的 UTF-8 安全截断，并保留完整输出文件。

Prompt cache、session affinity、`addedToolNames`、`additional_tools` / tool-search / deferred tool serialization，以及 active tools 对应的 Available tools/Guidelines 都由官方 Pi/provider 负责。本扩展不会修改 system prompt、构建 provider payload，也不会保存或实现 prompt cache。

## 官方 Pi 与 provider 的边界

- ChatGPT OAuth/OpenAI 的 `prompt_cache_key`、session affinity 和 native dynamic tool loading 由官方 Pi/provider 处理。
- 支持 native deferred loading 的模型可以由官方 Pi 在工具结果位置加载新增工具定义，并使用 `addedToolNames` 选择 provider 原生序列化。
- 没有 native deferred loading 的 provider 仍会由官方 Pi 发送当前 active tools。cache-stack 只减少初始和未使用工具的请求体，不承诺激活轮的完整 prompt-cache 命中。
- `reset`、模型策略变化或 `disabled` 变化需要移除 active tools 时，会走官方 fallback。此类变更可能重建 system prompt 并使 provider prefix-cache entry 失效。
- `getAllTools()` 的公共 `ToolInfo` snapshot 只暴露 `promptGuidelines`，不暴露 `promptSnippet`。搜索、激活结果和 `/lazy` 状态会标记可见的 prompt guidelines，并始终给出保守的通用提示；扩展不会读取私有字段，也不会剥离、冻结或替换这些 metadata。带 prompt metadata 的工具激活可能触发官方 Pi 的 system-prompt rebuild。
- 官方 Pi 根据当前 active tool set 自己生成 Available tools/Guidelines。cache-stack 的 `before_agent_start` 只做 policy reconcile，不返回自定义 system prompt，也不注入完整 inactive-tool catalog；inactive tools 通过 `lazy({})` 状态和 `lazy({ search: "..." })` 发现。

`lazy` 不是权限隔离。它只控制当前 Pi session 的 active tool 集合，模型可以搜索并激活未被 `disabled` 且未声明为 `externallyManaged` 的注册工具。

## 安装

推荐通过官方 Pi package manager 安装：

```bash
pi install npm:pi-cache-stack
```

固定版本：

```bash
pi install npm:pi-cache-stack@0.5.0
```

管理命令：

```bash
pi update npm:pi-cache-stack
pi remove npm:pi-cache-stack
```

如果之前通过 Git 或本地目录加载过同一扩展，应先移除旧来源，避免重复加载。安装或切换后执行 `/reload`，必要时重启 Pi。

## 配置

配置文件为 `~/.pi/agent/cache-stack.json`。节级对象深合并，数组整体替换，`null` 视为未设置。配置只描述工具 policy，不配置 provider cache 或请求 payload。

```jsonc
{
  "lazyTools": {
    "enabled": true,
    // 默认 bash/read/write/edit/ls/find/grep 会保留；这里追加工具
    "alwaysActive": [],
    // 注册但不允许进入 active set 或被 lazy activate
    "disabled": [],
    // 由其他扩展决定 active；这里只保留 owner 当前已激活的状态
    "externallyManaged": ["openai_generate_image"],
    // 按模型覆盖 policy；支持 * 和 ? 通配符
    "modelOverrides": {
      "anthropic/claude-haiku-*": {
        "enabled": false
      },
      "openai/gpt-5.6-*": {
        "enabled": true,
        "alwaysActive": ["mcp"],
        "disabled": [],
        "externallyManaged": ["openai_generate_image"]
      }
    }
  },
  "toolOutput": {
    // 只限制最终写入模型/会话上下文的结果，不影响 UI 实时流式显示
    "enabled": true,
    "maxBytes": 20480,
    "maxLines": 2000,
    "tools": ["bash", "read", "grep", "find", "ls"]
  }
}
```

行为说明：

- `enabled: true` 时，session 初始 active set 为 `lazy` gateway、默认基础工具和 `alwaysActive` 工具；其他已注册工具通过搜索后激活。
- `enabled: false` 时恢复当前注册表中的完整工具集，并在 resume、tree navigation 和 compaction 后继续保持完整集。disabled 在该模式下不是权限边界。
- `alwaysActive` 会与默认基础工具合并；`disabled` 最终优先。
- `externallyManaged` 是 preserve-only：已由 owner 激活的注册工具会保留，inactive 工具不会被 cache-stack 添加、搜索或 lazy activate，也不会写入 branch activation snapshot。关闭这类工具应使用 owning extension 的配置。
- 同一名称同时出现在 `externallyManaged` 与 `disabled`/`alwaysActive` 时属于 ownership 冲突，`/lazy` 会明确诊断；cache-stack reconcile 中仍保持现有 `disabled` 优先和 `alwaysActive` 主动添加语义。
- `modelOverrides` 在 `model_select` 时重新解析。数组整体替换；仍符合策略的 session activation 和当前 active 的 externally-managed 工具会保留，被 disabled、移出注册表或转为其他 ownership 的 cache-owned activation 会被移除，并标记为官方 fallback。
- `toolOutput` 默认限制 `bash/read/grep/find/ls` 的最终文本结果为 20KB 或 2000 行。`bash` 保留尾部，其他默认工具保留头部；发生截断时完整文本会保存到 `~/.pi/pi-cache-stack/tool-output/` 并在结果中给出路径。
- `toolOutput.tools` 是整体替换的 allowlist；设 `enabled: false` 可恢复官方 Pi 的原始结果大小。图片块、错误状态、usage 和工具 details 保持不变。
- 工具输出限制发生在 `tool_result` 边界，因此不会缩短执行中的 UI streaming update，只限制最终进入模型和 session history 的内容。
- 配置错误会 fail-open 到规范化默认值，不阻塞 Pi 启动。

## 发现、激活与诊断

模型可以调用：

```text
lazy({})
lazy({ search: "web search URL" })
lazy({ activate: ["web_search"] })
lazy({ reset: true })
```

用户命令：

```text
/lazy
/lazy search <query>
/lazy activate <name>[,<name>...]
/lazy reset
```

激活是纯增量操作：扩展读取当前 `getActiveTools()`，调用 `setActiveTools(current + additions)`。因此官方 Pi 可以识别新增工具名并决定走 native deferred loading 或 fallback。只有模型调用 `lazy({ activate })` 的工具执行路径标为 `native-candidate`；`/lazy activate`、model/lifecycle reconcile、reset、disabled/removal 和 branch restore 都是官方 `fallback`。active set 未变化时标为 `no-op`，不会重复调用 `setActiveTools()`。

激活状态通过官方 `pi.appendEntry()` 写入当前 branch 的 `pi-cache-stack.activation-state.v1` custom entry：

```json
{
  "version": 1,
  "activatedTools": ["tool_name"]
}
```

新 session 从空 activation 开始；resume、fork、session tree navigation 和 successful compaction 从当前 branch 最后一条有效 snapshot 恢复，并过滤 missing、disabled、always-active、externally-managed 工具。相同 snapshot 不会重复追加 entry。旧版本 snapshot 中多余的 `catalogHash` 字段会被安全忽略，历史 activation state 仍可恢复。

`/lazy` 状态还会显示：

- 当前 active tools、inactive discoverable tool names 与估算的请求体开销；
- externally-managed 工具的 active/inactive/unregistered 状态及 ownership 冲突；
- 本 session 已激活的 lazy tools；
- 最近一次 active-tool update 是 initial、native-candidate、official fallback 还是 no-op；
- 带 prompt metadata 的工具及其可能的 system-prompt/cache 影响。

## 开发

```bash
npm install --ignore-scripts
npm run typecheck
npm test
```

测试覆盖配置规范化、最小初始工具集、`before_agent_start` 不替换 system prompt、purely-additive activation、branch-local session persistence/restore、legacy snapshot 兼容、compaction no-op、prompt metadata warning、reset/model/disabled removal fallback，以及不产生 custom-message 历史增长或访问 fork API 的入口行为。工具输出测试还覆盖 head/tail 策略、UTF-8 边界、聚合字节/行预算、图片块保留、完整输出落盘、错误结果元数据和持久化失败 fail-open。

官方 Pi 0.84.3 的 dynamic-loading smoke 应在不调用真实 provider 的前提下运行；只验证 `getActiveTools()` / `setActiveTools()` 的 load point 和 `addedToolNames` 相关行为。不要在本项目测试中提交 API key、调用付费 provider 或改写官方 Pi 核心。

## 维护边界

官方 Pi 负责核心 runtime、prompt cache、session affinity 和 provider-specific tool serialization。`pi-cache-stack` 只维护工具搜索、active-tool policy、配置和诊断；通用 runtime 缺陷应提交官方 Pi/provider，而不是复制或维护一套 Pi 核心。
