Files
my-pi/pi-compaction/README.md
T
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 2a6bf8c929 feat(pi-overflow): 新增统一的新工具结果展示截断扩展
此前截断分散在多层且互相冲突:Pi bash 保留尾部 50KB,RTK 兜底截断又只留
开头 12000 字符(砍掉报错与完整输出路径),pi-chrome 截开头 30K,web_fetch
各 provider 不一致。新扩展统一负责"新结果给模型看多少":
- capture.ts 最先加载暂存原文,index.ts 最后加载(system-prompt 之前)
- 超过 24K 字符时原文落盘 <agentDir>/my-pi/pi-overflow/(700/600、原子写),
  优先取 bash fullOutputPath 完整文件;模型看到 头 + 中段信号行 + 尾 的有界
  视图,行号基于落盘原文、结尾永远可见
- overflow_query:按 id 正则搜索、行区间、jsonPath 只读查询,id 校验防穿越
- 收编:RTK truncate 默认关闭、删除 pi-chrome truncateText、web_fetch 默认
  每页 20000(上限 100000)、web_search max_results 限定 1–20
- pi-compaction 遮蔽带 details.overflow.id 的结果时在占位符提示 overflow_query
- 登记 tools.jsonc / routing / 权限 allow / tool-search 常驻与分组

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-21 20:25:44 -07:00

89 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# pi-compaction
Pressure-aware context masking and threshold compaction for Pi.
## Responsibilities
Pi context management is layered; this extension owns the middle two layers:
- **L1 — entry truncation** (future `pi-overflow`): truncate new tool results as they are written. Not implemented here.
- **L2 — observation masking** (this extension): under real context pressure, replace old tool results outside a protected recent window with deterministic one-line placeholders via Pi `context_edit` entries. Originals stay in the session untouched and are recoverable with `context_tree_query`.
- **L3 — whole-context summary** (this extension): when the session idles (`agent_settled`) above the summary threshold, trigger `ctx.compact` once per leaf.
- **Pi native threshold compaction** is the final safety net and is never blocked; this extension no longer cancels `session_before_compact` based on its own thresholds.
Additionally it:
- publishes one effective-context snapshot for normal provider requests and Codex/SubAPI native compaction;
- maintains a masked-results ledger so `/context`, `/compaction-status`, the footer, and AutoReview see the same placeholders the model sees.
Context Mode remains responsible for preventing large external data from entering the session. RTK remains responsible for ordinary tool-output cleanup.
## Configuration
Configuration lives in the installed bundle's `config/pi-compaction.json` (the bundle repository's `config/` directory is the single source of truth; `pi update` refreshes the installed copy). The extension never reads `settings.json` and never writes config at runtime:
```json
{
"enabled": true,
"masking": {
"enabled": true,
"triggerTokens": 200000,
"triggerRatio": 0.6,
"targetTokens": 100000,
"targetRatio": 0.35,
"minClearTokens": 50000,
"keepRecentTurns": 10,
"minEntryTokens": 256,
"excludeTools": ["ask_user_question"]
},
"summary": {
"triggerTokens": 400000,
"triggerRatio": 0.9,
"thinking": "low"
},
"native": {
"enabled": true
}
}
```
All thresholds are absolute token counts constrained by a context-window ratio: `effectiveThreshold = min(tokens, floor(ratio × contextWindow))`. Masking fires when `currentTokens ≥ triggerEff` and clears the oldest eligible results until `currentTokens - cleared ≤ targetEff`; if the clearable volume stays under `min(minClearTokens, triggerEff - targetEff)` nothing is masked, so masking is rare, batched, and hysteretic. `masking.target` is normalized below `masking.trigger` and `summary.trigger` above `masking.trigger` when a config would violate that ordering. Invalid or missing fields fall back to defaults in memory.
Masking never touches the most recent `keepRecentTurns` assistant turns, user/assistant messages, `excludeTools`, `SKILL.md` reads, entries already masked, or results smaller than `minEntryTokens`.
## Native providers
Native compaction is enabled by the `native.enabled` config key and uses the model selected in the session when compaction starts. It does not select a separate summarization model.
| Provider | API | Native transport |
| --- | --- | --- |
| `openai-codex` | `openai-codex-responses` | Existing Codex Responses endpoint and JWT/account-ID authentication |
| `sub-api` | `openai-responses` | Public Responses endpoint and resolved gateway headers or API key |
For SubAPI, keep the existing `models.json` API as `openai-responses`. The adapter requires an explicit HTTP(S) `baseUrl`: for example, `https://gateway.example/v1` becomes `https://gateway.example/v1/responses`; an existing `/responses` suffix is not duplicated. It preserves configured headers and merges the native compaction beta feature, without decoding the API key or adding a ChatGPT account ID. No gateway hostname or credentials are bundled.
SubAPI preserves a non-empty explicit `Authorization` header (case-insensitively) ahead of the API key. Without it, a non-blank API key supplies Bearer authentication; if no key is available, a non-empty `cf-aig-authorization` header also supports proxy-managed authentication. Header-only credentials do not create a dummy Bearer token. Missing or blank credentials fail before any network request and return control to Pi. Official Codex still requires its API key/JWT even when explicit authentication headers are present.
Both transports send the current projection as ResponseItems, append `compaction_trigger`, and parse the SSE compaction item. SubAPI must support Sub2API native remote compaction v2 on its public Responses endpoint. This is not a client for the separate `/v1/responses/compact` endpoint; supporting only that endpoint or ordinary Responses chat is insufficient.
Request interception also restores SubAPI native checkpoints into subsequent Responses input. Checkpoints retain a bounded portable summary for switching models/providers, and their version 1 format is unchanged. Native authentication, transport or response failures return control to Pi's normal compaction fallback. Other provider/API pairs and `native.enabled: false` retain the existing portable-summary path. Setting `native.enabled` to false does not discard an already-created native checkpoint.
The gateway must have a compatible deployed version, model and available upstream account. Offline tests cover the protocol and lifecycle; they do not establish live gateway availability. No settings migration, provider rename or Pi core modification is required.
## Session format
The extension writes no `segment-summary` or other custom entries for masking: L2 uses Pi's native `context_edit` entries (`{ type: "context_edit", targetId, replacement }`), which alter only future model context. The raw session entries — and therefore UI, exports, and the evidence ledger — are unchanged; every masked result remains recoverable through `context_tree_query`. Masking placeholders are deterministic single lines starting with `[pi-compaction] Earlier `.
Compaction entries still carry `pi-compaction` version 1 details; native checkpoints use the same kind/version. This implementation intentionally does not read legacy `context-prune-*`, `openai-codex-native-compaction`, or older `segment-summary` entries. Start a new session after switching to this extension.
## Interfaces
- `context_tree_query`: recover the original output of a tool result that pi-compaction cleared from context, by tool call ID (1–20 ids per call, bounded output). Use it when a `[pi-compaction] Earlier` placeholder lacks evidence needed for the current task.
- pi-overflow linkage: when the masked tool result carries `details.overflow.id` (pi-overflow stored the oversized original), the placeholder ends with `Full original stored: overflow_query {"id":"…"}`. pi-compaction only reads that public field and never imports pi-overflow internals.
- `/compaction-status`: show raw/effective tokens, reclaimed tokens, confidence, segments, masked results, and generation.
- Service `my-pi.pi-compaction.service.v1`: `getSnapshot`, `getMetrics`, and `query(sessionId, ids)` — `query` returns a placeholder record only for tool call IDs that are currently masked, so consumers such as the permission system's AutoReview see the same evidence the model does.
## Performance model
The first session hydration scans the branch once. Appended turns use delta projection and immutable cached snapshots; the `context` hook performs no model call, no session write, and returns the message list unchanged except on the native-checkpoint path, where post-checkpoint `context_edit` entries are applied during projection. Masking drafts are produced in `turn_end` in one batch and only when both the trigger and the `minClear` hysteresis pass, so the hot path stays allocation-free for unprompted turns.