Files
my-pi/pi-compaction
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 56cbc83296 refactor: 移除 context-mode,Chrome 快照改为直接返回
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>
2026-10-21 20:45:33 -07:00
..
2026-09-01 14:52:43 +08:00
2026-09-01 14:52:43 +08:00
2026-09-01 14:52:43 +08:00

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.

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] 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.