mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
此前截断分散在多层且互相冲突: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>
89 lines
7.7 KiB
Markdown
89 lines
7.7 KiB
Markdown
# 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.
|