pi-compact
v0.8.0
Published
不依赖 LLM 的 Pi 自动压缩、精确历史召回与上下文增强扩展
Readme
pi-compact
English: README.md
面向 Pi 的长期记忆、确定性 session 压缩与精确历史召回扩展。
pi-compact 的核心是长期记忆。压缩负责窗口容量管理:历史可恢复,长期记忆在多次压缩、重启和 context window 切换后仍可重建,并通过工具按需查询。扩展不调用 LLM;确定性 checkpoint 仍只包含指针和审计元数据,另有当前 session 专属的规则化工作摘要,从原文提取并附 entry ID,可用于恢复脉络但不代表语义结论。原始 session entries 始终是事实来源,可通过召回工具重新读取。
功能
- 在当前 Pi 工作区
.pi/pi-compact/中维护 append-only 记忆事件日志;当前记忆状态由纯函数从事件重放,旧事件不能静默覆盖。 - 用户通过
/remember、/memories、/forget写入权威记忆;模型只能提出 provisional 提议,不会自动变成 active 或 pinned。 - 模型通过工具按需查询历史。压缩成功后,扩展从本次压缩涉及的 primary 原始记录中生成 session 隔离的结构化工作摘要,保留摘录、文件和 entry ID;摘要仅沿当前分支的压缩链累积,可在
before_agent_start注入当前 session,也可通过召回工具搜索。它是原文索引,不是语义推断或验证结论。扩展不注册contexthook。权威长期记忆仍只注入用户通过/remember写入的 pinned/active 记录。 - 接管 Pi 原生的
/compact命令,以及普通的manual、threshold和overflowcompaction。 - 使用 Pi 自己计算的压缩边界、token accounting、持久化和恢复流程;checkpoint 是确定性指针/审计内容,不是 primary memory。
- 记录 WindowManifest(windowId、父窗口、保留边界、sourceCount、sourceHash、previousHash)。
- 在压缩前检查工具调用与工具结果是否完整配对;发现不安全边界时优先回退到更早的安全边界,找不到任何安全边界才取消本次接管,避免破坏上下文。
- 生成确定性的事件 ledger,不把规则提取结果伪装成目标、决策或已完成任务。规则派生只提取文件、命令、退出码、测试计数等不需要语义推断的字段,并带 provenance。
- 历史记录区分 primary/derived:
compaction与branch_summary属于 derived,可用sourceClass过滤;thinking 保留在 raw 中,不进入默认搜索文本。 - 提供
pi_compact_recall工具,支持 list/search/read、entry ID、关键词、文件路径、消息类型、分页和原始 entry 回放;长 raw 可用offset/rawLimit分段读取。 - 提供
/pi-compact-recall命令,仅通过 UI 显示召回结果,不触发模型 turn。 - 提供
pi_memory_search、pi_memory_read、pi_memory_propose、pi_memory_update与pi_compact_new_context。 - 自动创建项目级
.pi/pi-compact.json配置,不覆盖已有项目或全局配置。
安装
当前源码要求 Pi >=0.87.1;开发与集成测试固定在 1.4.0;该版本要求 Node.js >=22.19.0。以下内容假设该仓库位于 github.com/DavidEasden/pi-compact。已发布的 v0.8.0 与当前源码一致。
Pi package 会执行扩展代码,请在安装前审查源代码。
通过 GitHub 安装
推荐固定到发布 tag(固定后的 ref 不会被 pi update --extensions 或 pi update --all 移动):
pi install git:github.com/DavidEasden/[email protected]或者不固定 ref,直接跟踪默认分支:
pi install git:github.com/DavidEasden/pi-compact也支持 SSH 简写与原始 HTTPS URL:
pi install git:[email protected]:DavidEasden/[email protected]
pi install https://github.com/DavidEasden/[email protected]说明:
git:前缀启用host/user/repo与git@host:user/repo简写;不带前缀时只接受协议 URL(https://、http://、ssh://、git://)。v0.8.0必须是已存在的 tag 或 commit(首次可用git tag v0.8.0 && git push origin v0.8.0创建并推送)。以后升级到新 tag,重新执行pi install git:github.com/DavidEasden/pi-compact@<新tag>。- 全局安装会克隆到
~/.pi/agent/git/github.com/DavidEasden/pi-compact;使用-l(项目 settings)时克隆位于.pi/git/github.com/DavidEasden/pi-compact,项目信任后启动时会自动安装缺失的 package。 - 不安装也可以临时试用:
pi -e git:github.com/DavidEasden/pi-compact通过 npm 安装
同一份代码已发布到 npm,包名为 pi-compact:
pi install npm:[email protected]或者跟踪最新发布版本:
pi install npm:pi-compact不安装也可以临时试用:
pi -e npm:pi-compact说明:
- 形如
npm:[email protected]的带版本 spec 会被固定,pi update --extensions与pi update --all会跳过它。 - 全局安装位于
~/.pi/agent/npm/;使用-l(项目 settings)时位于.pi/npm/。
临时加载本地版本
在项目根目录执行:
npm ci
pi -e /absolute/path/to/pi-compact也可以直接指定入口文件:
pi -e /absolute/path/to/pi-compact/index.ts安装本地 package
pi install /absolute/path/to/pi-compact默认写入全局 Pi settings。使用 -l 可以写入当前项目的 .pi/settings.json:
pi install -l /absolute/path/to/pi-compact当前项目使用 Pi 的 @earendil-works/pi-coding-agent API,并按 Pi 1.4.0 验证 context_edit、压缩后的消息投影和当前扩展生命周期。Pi 和 typebox 是 peer dependencies,由 Pi 环境提供;开发依赖固定 Pi 1.4.0,用于可重复的宿主集成测试。
配置
首次启动扩展时,如果项目中没有配置文件且用户目录中也没有全局配置,扩展会创建:
.pi/pi-compact.json配置优先级为:
- 当前项目的
.pi/pi-compact.json - 全局的
~/.pi/agent/pi-compact.json - 内置默认值
扩展使用第一个存在的配置文件,并以默认值补齐缺失字段;项目配置与全局配置不会逐字段合并。如果项目配置存在但 JSON 无法解析,扩展使用默认配置,不会继续读取全局配置。
默认配置如下:
{
"enabled": true,
"overrideDefaultCompaction": true,
"summaryMaxChars": 12000,
"autoRecall": true,
"autoRecallMode": "full",
"autoRecallMaxChars": 5000,
"recallMaxResults": 8,
"recallMaxChars": 16000,
"debug": false,
"memory": {
"enabled": true,
"pinnedInjection": true,
"proposalsProvisionalOnly": true,
"hintMaxChars": 4000,
"deriveOnCompact": true
},
"history": {
"autoRecallPrimaryOnly": true,
"excludeInContext": true
},
"window": {
"manifest": true
},
"sessionSummary": {
"enabled": true,
"inject": true,
"maxChars": 6000,
"maxEvidence": 80
}
}配置项说明:
| 配置项 | 默认值 | 说明 |
| --- | ---: | --- |
| enabled | true | 是否启用压缩接管;召回工具仍可使用,记忆功能由 memory.enabled 控制 |
| overrideDefaultCompaction | true | 是否接管 Pi 的普通 compaction;关闭后保留默认压缩 |
| summaryMaxChars | 12000 | 确定性 checkpoint 文本的最大字符数 |
| autoRecall | true | 仅兼容旧配置,当前不启用自动召回 |
| autoRecallMode | full | 仅解析旧的 full/hint/off 模式,当前均不注入历史 |
| autoRecallMaxChars | 5000 | 旧自动召回预算,当前无运行时作用 |
| recallMaxResults | 8 | 旧自动召回条数,当前无运行时作用;手动查询使用 limit |
| recallMaxChars | 16000 | 召回输出字符预算;模型工具始终遵守,只有 UI 命令可显式读取超预算的单条完整 raw |
| debug | false | 是否输出扩展调试日志(只输出计数和字符数,不输出原文) |
| memory.enabled | true | 是否启用长期记忆命令、工具读写与规则派生 |
| memory.pinnedInjection | true | 是否在 before_agent_start 把用户写入的 pinned/active 权威记忆追加到系统提示;provisional、规则派生与模型提议记录永不注入。需要 memory.enabled |
| memory.proposalsProvisionalOnly | true | 模型提议只能以 provisional 写入;即使设为 false 也不会自动升级为 active/pinned |
| memory.hintMaxChars | 4000 | before_agent_start 注入权威记忆提示的字符预算 |
| memory.deriveOnCompact | true | 压缩成功提交后是否在当前 session 写入规则派生的 provisional 记录(文件/命令/退出码/测试计数,以及用户原文中带持续性标记的偏好/约束/决策);不跨 session 共享 |
| history.autoRecallPrimaryOnly | true | 旧自动召回来源过滤,当前无运行时作用 |
| history.excludeInContext | true | 旧自动召回去重开关,当前无运行时作用 |
| window.manifest | true | 压缩成功提交后是否写入 WindowManifest 与窗口事件日志;取消不写入 |
| sessionSummary.enabled | true | 是否在成功压缩后生成当前 session 的确定性工作摘要,并允许摘要工具查询 |
| sessionSummary.inject | true | 是否把当前分支已提交摘要注入当前 session 的系统提示;不会跨 session 注入 |
| sessionSummary.maxChars | 6000 | 自动注入摘要的最大字符数;按完整 JSON 证据项裁剪 |
| sessionSummary.maxEvidence | 80 | 每个 session 摘要最多保留的原文证据项数量,最少为 2,以保留初始用户请求和近期证据 |
也接受扁平别名,例如 memoryEnabled、memoryHintMaxChars、historyAutoRecallPrimaryOnly、windowManifest。嵌套对象优先。数值配置必须是正安全整数;sessionSummary.maxEvidence 最少为 2。summaryMaxChars、autoRecallMaxChars、recallMaxResults、recallMaxChars、memory.hintMaxChars、sessionSummary.maxChars、sessionSummary.maxEvidence 的上限依次为 100000、50000、30、100000、50000、30000、500;超过上限的合法值会被截断,非法值会回退到默认值;未知配置项会被忽略。字符预算不是 token 预算。默认策略:记忆开启、模型提议仅 provisional;历史和摘要可通过工具按需查询,自动注入仅限有预算的 session 摘录索引及用户确认的 pinned/active 记忆。旧注入配置不能重新启用已移除的 context hook。
自动压缩阈值和保留尾部大小仍由 Pi 自身的 compaction settings 控制,本扩展不另设触发阈值。
使用
立即压缩
使用 Pi 原生命令:
/compactPi 原生的 /compact 命令会调用正常的压缩流程。当 enabled 和 overrideDefaultCompaction 都为 true 时,本扩展接收 session_before_compact 事件,并用确定性 checkpoint 替换 Pi 默认的 LLM 摘要。扩展不再注册单独的压缩命令。当 enabled 或 overrideDefaultCompaction 为 false 时,/compact 保留 Pi 默认的摘要行为。
权威长期记忆
长期记忆存放在 Pi ctx.cwd 对应工作区的 .pi/pi-compact/memory.jsonl(append-only 事件日志),不按用户全局共享。相同 ctx.cwd 启动的 session 共享 workspace 记忆;不同 ctx.cwd 使用各自独立的日志,即使 sessionId 相同也不会互通。扩展不自动推断 Git 根目录。session 记忆仅在创建它的同一 session 内可见。新接口只提供 workspace 与 session 两种范围;旧日志中的 project 和 user 会在读取时映射为 workspace,不会重写日志。当前状态由纯函数 projector 重放得到。用户写入是权威来源;模型提议默认且始终为 provisional,必须由用户 /remember 确认后才成为 active/pinned。扩展不能保证模型一定遵守这些记忆。
memory.jsonl(及 windows.jsonl)的追加受按日志文件的同步文件锁(.pi/pi-compact/memory.jsonl.lock、windows.jsonl.lock)保护,避免多个 Pi 进程交叉执行「读末条 → 算 seq → 追加」而丢失事件。PI_COMPACT_LOCK_TIMEOUT_MS 只是有限等待超时(主要供测试),不是活锁租约:持有者 PID 仍存活时,不会因为锁文件年龄而回收。崩溃恢复只在以下情况删除锁:所有者 PID 明确死亡;锁内 startTime 与实时启动时间都能读到且明确不同(PID 复用);或能用当前进程的 startTime 证明这是本进程遗留且当前未持有。Linux 读取 /proc/<pid>/stat;macOS 和其他可用 Unix 平台尝试设置 TZ=UTC 后同步执行 ps;Windows 尝试标准系统能力(如 PowerShell Get-Process StartTime)。启动时间身份是尽力而为——并非所有平台都能读到,失败一律视为无法验证。无法解析、空的或缺少有效 owner token/pid 的锁在宽限期内不会被自动删除(可能正处在创建写入窗口);超过宽限期(PI_COMPACT_STALE_LOCK_GRACE_MS,默认 1000 毫秒,主要供测试覆盖)后按崩溃遗留自动回收,避免空锁文件把写入永久卡死。宽限期内无法验证身份时会等到超时,并抛出明确错误:所有者 metadata 无法验证或锁仍被持有,且本次写入未完成。人工删除 .lock 文件仍是明确的恢复途径。锁在进程内可重入,事务结束时必然释放。释放与回收必须核对锁文件中的 owner token,并在存在 pid/startTime 时一并核对;expected metadata 缺失或不匹配时不会删除他人的新锁。写入失败会显式报错并告知用户或模型,失败后不会声称写入成功。
读取任一日志时,事件必须构成完整链:seq 从 1 起严格连续递增,prevHash 必须等于上一条被接受事件的 hash,且每条事件自身 hash 正确。遇到首个非法、重复、跳号或被篡改的事件即停止,只重放可信前缀。一旦存在这样的事件,该日志的后续追加会被拒绝并报出明确错误:用户必须先人工修复日志才能继续写入,扩展不会通过截断或重写日志来掩盖问题。EOF 处无法解析的半行仍被容忍——读取时跳过,下次追加前补齐换行,因此写入中途崩溃仍可恢复。
/remember 使用 pnpm 而不是 npm
/remember pin kind:constraint 不要修改生产数据库
/remember supersede:mem_abc 新的权威表述
/memories
/memories status:pinned token
/forget mem_abc/remember 支持 pin、kind:、scope:workspace|session、priority:、supersede:<id>;默认 scope 为 workspace。旧命令中的 scope:project 与 scope:user 作为兼容别名映射为 workspace,不代表跨工作区共享。/memories 可按 status:、kind: 和关键词过滤。/forget 将记忆标记为 resolved,不再出现在默认记忆查询中。这些命令只做本地确定性写入并通过 UI 通知,不触发模型 turn。
记忆工具:
| 工具 | 说明 |
| --- | --- |
| pi_memory_search | 搜索当前工作区记忆;无关键词且无显式 status 时列出 pinned/active/provisional;支持 offset/limit 分页 |
| pi_memory_read | 按 ID 读取;默认读取 4000 字符,长内容通过 offset/limit 分段 |
| pi_memory_propose | 模型提出当前工作区或当前 session 的 provisional 记忆,不会自动变成 active/pinned |
| pi_memory_update | 只能 edit/resolve 模型自己的 provisional 提议 |
| pi_compact_new_context | 登记换窗请求;同批工具全部完成后压缩,成功后继续当前任务;不支持时提示使用 /compact |
memory.enabled=false 会同时禁用记忆命令、搜索、读取、提议和修改。scope:session 的记录仅在创建它的同一 sessionId 下可见、可修改;没有有效 sessionId 时不能创建或访问会话范围记录。workspace 记忆在同一 Pi ctx.cwd 下的 session 间共享,不会转存到用户全局目录。不同 cwd 是不同工作区;扩展不自动将仓库子目录归并到 Git 根目录。
记忆搜索和读取的总文本输出上限固定为 16000 字符,包含 JSON 元数据与转义。搜索对每条正文生成最多 600 字符的片段,省略的完整内容可按 ID 读取;读取默认段长为 4000,显式大 limit 也受总预算限制。搜索的 details.nextOffset 是下一页记录偏移;读取 JSON 的 nextOffset 是下一段正文字符偏移。显式查询 status:resolved 或 status:superseded 无需同时提供关键词。过大的元数据会标记 metadataTruncated,原始记忆仍保留在日志中。
换窗工具只在压缩成功回调之后发起一次续跑。工具批次被用户中止、压缩失败或取消、会话切换,或已有新输入时,不会擅自续跑;失败仍可使用 Pi 原生 /compact 重试。
规则派生记忆带 author=rule 与 provenance,状态为 provisional,不是用户确认的事实;它们使用 scope=session,只在生成它们的 session 中可见。除文件、命令、测试计数外,提取器只从用户原文中挑选带有“偏好/默认/始终/本项目”等持续性标记的短句,归类为 preference、constraint 或 decision;不分析 assistant 自述,不从普通请求推断长期偏好,也会跳过疑似凭据内容。没有有效 sessionId 时不会自动生成。用户确认前始终保持 provisional,不会自动升级或注入;旧版写入的规则型 workspace provisional 记录会在下次成功压缩时标记为 resolved,用户写入的记录不受影响。
记忆写入有明确上限:正文超过 8000 字符会被拒绝而不是静默截断;sourceEntryIds 会去重并限制为 64 条 / 4096 字符,截断状态会写入工具结果。同一 scope(session 范围还需同一 sessionId)下正文相同的模型提议只写入一条。模型或规则产生的 provisional 记录在 30 天后从默认列表与搜索中隐藏;显式查询 status:provisional 仍可返回,用户写入的记录不会因此过期。记忆搜索与读取的总输出上限为 16000 字符(含 JSON 元数据与转义),搜索对每条正文生成最多 600 字符的片段,被省略的内容可按 ID 读取。
debug=true 时会记录摘要/派生候选计数、字符及 token 估算、处理耗时、失败类别,以及 Pi before_provider_request 边界上的 payload/稳定前缀哈希与长度;assistant usage 中会记录 provider 报告的 input/output/cacheRead/cacheWrite。日志不含 prompt 或记忆正文。哈希/前缀稳定只验证扩展实际观察到的 provider payload;cacheRead/cacheWrite 是 provider 报告值,是否产生真实成本收益仍取决于 provider 与服务端实现,不由扩展保证。
查看状态(只读)
/pi-compact-status 是只读诊断命令,显示当前生效开关(enabled、memory、summary、debug)、memory/window 日志的事件数、行数与文件大小、当前 session 的摘要事件与证据数量,以及进程内的注入与压缩统计(缓存命中/未命中、最近一次注入的字符与 token 数、最近一次压缩的原因、耗时、记录数、checkpoint 字符数与派生记录数)。结果通过 UI 通知显示,不发送给模型,不触发模型 turn。
手动召回
在 Pi 中执行:
/pi-compact-recall token 刷新支持的参数形式:
/pi-compact-recall file:src/auth/session.ts
/pi-compact-recall kind:tool_result timeout
/pi-compact-recall ids:entry-123,entry-456 raw
/pi-compact-recall scope:all 旧配置 page:2 limit:5
/pi-compact-recall raw:true file:src/auth.ts参数说明:
| 参数 | 说明 |
| --- | --- |
| 普通文本 | 按关键词搜索原始 session records |
| ids:id1,id2 | 按 entry ID 精确选择记录 |
| file:path | 匹配记录中的文件路径 |
| kind:type | 按 user、assistant、tool_call、tool_result、bash 或 custom 过滤 |
| scope:active-lineage | 只搜索当前 branch,默认值 |
| scope:all | 搜索整个 session,包括其他 branch |
| page:N | 结果分页,从 1 开始 |
| limit:N | 每页结果数,默认 8,范围为 1 到 30;不受 recallMaxResults 影响 |
| raw 或 raw:true | 输出原始 session entry JSON |
| summary 或 action:summary | 读取当前 session、当前分支最近一次已提交的结构化摘要 |
| summary_search 或 action:summary_search | 在当前分支摘要证据中按关键词搜索,返回摘录和来源 entry ID |
| list 或 action:list | 按最近记录有界列出,不需要关键词 |
| offset:N | raw 分段读取的字符偏移 |
| rawLimit:N | raw 分段读取的字符长度 |
| sourceClass:primary\|derived\|all | 按来源类别过滤 |
示例中的 entry ID 是占位符,请使用召回结果中显示的真实 ID。
摘要由规则从当前 session 的 primary 原始记录中提取,只记录摘录、类别、文件和来源 entry ID,不推断工作完成状态、测试结果含义或用户偏好。摘要沿当前分支的父压缩事件累计;不同 session 使用独立的哈希命名日志。需要完整内容时,按摘要给出的 entry ID 读取原文。
指定 ids 后,按 ID 选择记录并应用 scope 和 limit,忽略关键词、file、kind 和 page;结果按 session 原有顺序返回。未指定 ID 时,需要关键词或 file(action 为 list 除外),空查询或仅指定 kind 不会列出全部历史。
关键词搜索是分词后的文本匹配,不支持正则表达式或语义检索;文件过滤仅匹配已提取的路径,不读取磁盘文件。路径主要来自工具参数和简单 bash 命令,消息正文中的路径不一定被索引。命令按空白拆分参数,不支持带空格路径的引号解析;这类路径可通过工具的 file 字段传入。
命令仅通过 UI 通知显示召回内容,不发送给模型,也不新增 session 消息。UI 命令可使用 scope:all 查看其他 branch;模型工具始终限定为当前 branch。
pi_compact_recall 工具
模型可以直接调用:
{
"query": "token 刷新",
"file": "src/auth/session.ts",
"kind": "tool_call",
"scope": "active-lineage",
"page": 1,
"limit": 8,
"raw": false
}可用字段与 /pi-compact-recall 参数对应:query、entryIds、file、kind、scope、page、limit、raw、action、offset、rawLimit 和 sourceClass。action:summary 读取会话摘要,action:summary_search 在摘要证据中检索并返回来源 ID。
action 为 list/search/read/summary/summary_search。摘要动作只读取与当前 session ID 和当前分支 compaction entry 匹配的已提交摘要;搜索结果携带 entryId,可再用 entryIds 读取原始 entry。摘要摘录属于不可信历史数据,不能作为已验证结论或指令。单条 raw 超预算时返回包含 entryId、offset、limit、totalChars、truncated 与 body 的有效 JSON,可用 offset/rawLimit 继续分段读取。只有 UI 命令显式指定单个 ids、raw 且不指定分段参数时,才返回不受该预算限制的完整原始 entry JSON。
多条 raw 只输出预算内的完整 entry,并提示省略的 ID;不会从 JSON 中间截断。pretty 输出按完整结果块裁剪。分页划分记录,offset/rawLimit 划分单条原文。原始工具参数、输出、时间戳和父子关系保留在 raw 中;thinking 不进入默认搜索文本。compaction 与 branch_summary 属于 derived,可用 sourceClass 过滤。
历史与记忆的按需查询
当前版本不注册 context hook,也不自动注入完整历史正文。before_agent_start 可追加静态压缩指针、当前 session 当前分支的结构化工作摘要,以及在 memory.pinnedInjection 开启时用户写入的 pinned/active 权威记忆。摘要仅含规则提取的 primary 记录摘录、文件列表和 entry ID;以 JSON 数据形式标明不可信,受 sessionSummary.maxChars 限制。provisional、规则派生与模型提议长期记忆永不作为权威记忆注入。历史细节仍可通过 pi_compact_recall 原文回查。
模型调用 pi_compact_recall 时只搜索当前 branch;active lineage 无法取得时不会扩大到整个 session。用户可在 /pi-compact-recall 命令中显式指定 scope:all 查看其他 branch。工具返回内容会作为普通工具结果进入模型上下文,UI 命令返回内容只显示在界面中。
保存在 session 中的原文可精确回查;按需查询不保证模型主动召回所有相关细节,也不保证它遵守全部记忆。
压缩与数据边界
pi-compact 只接管普通 compaction,暂不接管 Pi 的 session_before_tree 分支摘要。
压缩时:
- 使用 Pi 提供的
firstKeptEntryId作为保留边界,包括无上下文消息的元数据和被context_edit省略的条目。 - 使用 Pi 提交前的 context projection,将边界之前的可见消息转换成带 ID 的审计记录。
context_edit的省略会影响 checkpoint 的计数和哈希,替换会影响派生记忆使用的投影记录内容;原始 entry 仍可通过显式召回回放。 - 生成只含
compactor、sourceCount、sourceHash的确定性 checkpoint;不复制历史正文,checkpoint 不是 primary memory。 - 保存
sourceEntryIds、sourceHash、sourceRecordCount、keptEntryId、omittedRecordCount、checkpointChars、summaryMaxChars、isSplitTurn、estimatedTokensAfter(字符数 / 4 向上取整,不是 provider usage)。扩展不会伪造计费usage;Pi 会自行计算包含完整上下文的压缩后估算值。 - 使用 Pi 1.4.0 的
buildSessionProjection()预演候选 compaction,检查应用context_edit省略与替换后的保留尾部。结果必须跟在对应调用之后,普通调用必须有结果;已被 checkpoint 替换的前缀不阻塞压缩。边界不安全时在当前 compaction window 内逐个预演更早边界;没有安全边界、无法构造投影或请求中止时取消,不落回 LLM 摘要。Pi 的error、abortedassistant 终态允许无结果调用。安全校验通过后,checkpoint 生成出错会降级为指针型 JSON;回退后重新计算isSplitTurn。 - Pi 提交 compaction 并发出成功事件后,才在同一文件锁中生成窗口清单并写入
.pi/pi-compact/windows.jsonl;窗口 ID 绑定 sessionId 与实际 compaction entry,重复事件不会重复写入。规则派生记忆也在成功后写入。失败或取消不会留下窗口及派生记录。旧 checkpoint 中的details.window仍可读取,新清单以窗口日志为准。
原始 session entries 是历史事实来源;结构化会话摘要是从 primary 记录生成的确定性摘录索引,带来源 entry IDs 和源哈希,不做语义推断,也不验证原文陈述。用户写入的 memory log 才是长期权威记忆事实来源。checkpoint 只保存计数和哈希,不是原文备份。图片等非文本内容在文本提取中仅显示占位信息。
扩展不删除原始 session entries,也不建立独立备份;原文仍依赖 Pi 的 session 存储。有限上下文不能同时展示全部历史。
开发
安装依赖:
npm ci运行测试:
npm testnpm test 同时运行单元测试与 Pi SDK 集成测试;仅运行宿主生命周期回归可使用 npm run test:integration。集成测试使用本地 Faux provider,不需要 API key,不访问线上模型,所有会话和日志都写入临时目录。
运行 TypeScript 类型检查:
npm run typecheck运行手动性能基准:
npm run bench检查发布包内容:
npm pack --dry-run运行源码的发布白名单为 index.ts 和 src/;npm 还会自动包含 package.json 和本 README。测试文件与 tsconfig.json 不会进入 package。
项目结构
index.ts Pi 扩展入口
src/config.ts 配置读取、归一化和初始化
src/hooks.ts 压缩投影校验与 session hooks
src/recall.ts 召回工具与 /pi-compact-recall 命令
src/memory.ts 记忆命令与记忆/新窗口工具
src/status.ts 只读 /pi-compact-status 诊断
src/core/content.ts 消息文本、文件、thinking 分离和片段边界
src/core/auto-recall.ts 旧自动召回门控纯函数,未接入运行时
src/core/lock.ts 同步日志锁、owner-token 回收、跨平台启动时间身份
src/core/ledger.ts 确定性 checkpoint 与 details
src/core/session.ts session entry 转换、sourceClass、搜索和原文回放
src/core/jsonl.ts 损坏安全的 JSONL 读写
src/core/projector.ts 记忆事件纯函数投影
src/core/store.ts `.pi/pi-compact/` 事件日志
src/core/window.ts WindowManifest 与哈希链
src/core/summary.ts session 摘要的确定性提取、隔离存储、累积和搜索
src/core/working.ts 权威记忆提示渲染
src/core/memory-policy.ts 记忆写入上限、提议去重与 provisional TTL
src/core/runtime-stats.ts 进程内注入与压缩计数
src/core/derive.ts 无语义推断的规则派生
src/types.ts 共享类型
scripts/bench.mjs 手动性能基准
tests/ 单元测试验证范围
当前已验证:
确定性记录提取、关键词和文件搜索、原始记录保留与 hash。
thinking 不进入默认搜索;primary/derived 来源过滤和模型工具的当前分支限制。
记忆事件重放确定性、supersede/pin、EOF 半行容忍与断链后拒绝追加。
窗口哈希链、记忆命令/工具入口、长 raw 分段读取。
工具调用与工具结果的压缩边界校验。
配置初始化与非法配置归一化,包括 memory/history/window。
通过模拟 hook 事件检查
manual、threshold、overflow三种 compaction reason 及已中止请求。空 active lineage 不扩大搜索范围;没有
contexthook,只有带来源 ID 的有界 session 摘录会按配置注入。before_agent_start的压缩指针与权威记忆注入,包括 provisional/规则/模型记录的排除与pinnedInjection关闭开关。旧的自动召回门控仍有单元测试,但运行时未接入;
working.ts仅用于渲染权威记忆提示,不再产生旧的pi-compact-memory-hint消息。日志锁在 token/metadata 不匹配时不删除,活锁不按年龄回收;无效 metadata 在宽限期后可回收。跨平台启动时间身份(Linux
/proc、Unixps、WindowsGet-Process)与无法验证时的保守等待均有不依赖切换process.platform或真实 PID 复用的单测。干净依赖安装、TypeScript 类型检查和真实 Pi CLI 扩展加载。
Pi 1.4.0 真实 SDK 的元数据切点、完整 overflow retry、
context_edit省略/替换与切点预演、孤立工具结果取消、并行及顺序工具换窗后续跑、用户中止、取消后的无副作用、成功事件去重和会话保存恢复。记忆工具禁用、session 范围隔离、显式历史状态查询、长正文与 JSON 元数据预算、搜索和读取分页。
尚未覆盖线上模型 API 的真实网络响应和计费,以及交互 TUI 的人工操作与长时间运行。
许可证
MIT
