context-mode 的 ctx_execute 系工具以真实 shell 执行且权限为 allow,绕过了 bash 策略与 protectedWrites;其每轮注入、独立 MCP 子进程与 skills 也与组合包 提示词单一所有权冲突。大输出职责已由 pi-overflow(入口截断)与 pi-compaction(压力遮蔽)承担: - 删除 context-mode 依赖、wrapper/过滤器、skills,以及 ctx_* 的权限、 tools.jsonc、路由规则、tool-search 常驻与 4 个分组 - better-sqlite3@12 仅为 context-mode 传递依赖,随之移除根 allowScripts - pi-chrome:按用户决定移除快照隐私规则与 .pi/chrome-context 临时文件机制, 快照 JSON 直接作为工具结果返回、details 只留小型元数据,超长由 pi-overflow 截断并用 overflow_query 提取;授权生命周期不变 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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_editentries. Originals stay in the session untouched and are recoverable withcontext_tree_query. - L3 — whole-context summary (this extension): when the session idles (
agent_settled) above the summary threshold, triggerctx.compactonce per leaf. - Pi native threshold compaction is the final safety net and is never blocked; this extension no longer cancels
session_before_compactbased 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.
pi-overflow bounds new tool results as they arrive. 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:
{
"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] Earlierplaceholder 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 withFull 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, andquery(sessionId, ids)—queryreturns 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.