mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 07:23:06 +00:00
feat: add pi-condense with default enablement
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
- `pi-lsp@0.1.7`:提供声明式 LSP 接入;组合包内置 `typescript-language-server@5.3.0` + `typescript@6.0.3`,并配置机器级 `kotlin-lsp --stdio` 与 `jdtls`。
|
||||
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索、后台学习和 secret scanning,默认使用 policy-only 模式。
|
||||
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程 compaction,默认阈值为 90%。
|
||||
- `pi-condense@2.9.1`:总结已完成的工具调用批次,以短 stub 替换历史原始输出,并通过 `context_tree_query` 按需恢复;组合包在用户尚未配置 `contextPrune.enabled` 时默认开启。
|
||||
- 根包还固定安装 `@tavily/pi-extension@0.1.2`、`@keenable/pi-search@0.1.2`、`pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0`、`typescript@6.0.3`、`pi-context-view` 和 `@firstpick/pi-extension-codex-fast-mode`;`@gotgenes/pi-permission-system` 改为指向仓库内 `pi-permission-system/` 的本地 `file:` 依赖。
|
||||
- `install.sh`:先安装根组合包,再交互检查 Kitty/Solarized Dark、Oh My Zsh/Powerlevel10k/Zsh 插件、CodeGraph、Kotlin LSP、Java 21+ 和 JDT LS;缺失项目只在用户明确选择 `Y` 后安装或配置。Powerlevel10k 默认配置来自仓库内置的 `config/p10k.zsh`(当前 Rainbow/ASCII 单行紧凑主题)。
|
||||
- `update.sh`:先通过 `pi update` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
||||
@@ -51,6 +52,7 @@
|
||||
- Exa MCP 使用托管 Streamable HTTP 端点和 `eager` lifecycle;`EXA_API_KEY` 通过 `x-api-key` 请求头发送,不得放入 URL、仓库文件或日志。所有在线搜索工具统一采用“来源名 + 原始语义工具名”的命名形式,例如 `tavily_web_search`、`exa_web_search`、`keenable_search`。Exa 高级搜索必须显式约束结果数量和文本长度;Tavily 输出默认控制 `max_results` 且非必要不请求 raw content;Keenable 优先利用中文、站点和日期筛选能力。
|
||||
- 搜索 key 默认保存在 `${XDG_CONFIG_HOME:-$HOME/.config}/my-pi/search.env`,文件必须为 `600` 且不得提交;`search_config.sh` 只输出配置状态,禁止回显 key。
|
||||
- `pi-context-view` 只观察上下文占用,不参与压缩策略。
|
||||
- `pi-condense` 负责压缩已经进入会话的历史工具结果,与 Context Mode 的输入隔离职责互补;`extensions/condense.ts` 只在 `settings.json` 尚无 `contextPrune.enabled` 时写入 `true`,必须保留用户显式设置的 `false`,配置无效时不得覆盖原文件。其他参数沿用上游默认,包括 `agent-message` 触发模式和 skill 路径保护。
|
||||
- Codex fast mode 只为符合条件的 `openai-codex-responses` 请求设置 priority service tier,由 `/fast-mode` 在会话内控制。
|
||||
- 权限策略默认允许常规工具,允许 FFF 工具;拒绝 Bash 直搜和敏感凭据路径;Git 非只读操作、包管理、外部目录、文件/系统/网络高风险操作与普通 MCP 调用先由 `pi-permission-system` 判为 `ask`。
|
||||
- `pi-permission-auto-review` 不是独立 `tool_call` gate,而是 `pi-permission-system` authorizer chain 中名为 `auto-review` 的链路;只复核权限基线产生的 `ask`,不会重复处理已 `allow` 或已 `deny` 的请求。
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
- `pi-lsp@0.1.7`:为 TypeScript/JavaScript、Kotlin 和 Java 提供 LSP 诊断、跳转与符号工具;TypeScript 后端由组合包内置。
|
||||
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索和 secret scanning。
|
||||
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程压缩。
|
||||
- `pi-condense@2.9.1`:把已完成的工具调用批次总结为可恢复的短摘要,并通过 `context_tree_query` 按需取回原始输出;组合包首次加载时默认开启。
|
||||
- 本仓库维护的 `pi-permission-auto-review`:作为 `pi-permission-system` authorizer,使用 Codex Guardian 风格策略自动复核 `ask` 请求。
|
||||
- 本仓库维护的 `pi-permission-system`:从 `@gotgenes/pi-permission-system@26.2.1` 源码导入,负责工具、路径、MCP、硬拒绝和兜底权限基线。
|
||||
- `extensions/tool-routing.ts`:保留 Pi 默认系统提示词,并按当前激活工具追加简短的工具与搜索路由规则;提供 `/dump-system-prompt` 导出当前有效提示词。
|
||||
@@ -256,6 +257,14 @@ Hermes Memory 使用上游默认的 `policy-only` 模式:不把完整记忆直
|
||||
达到 90% 上下文占用时触发,并写入 Pi 原生 compaction boundary。失败时保持原历史且不静默
|
||||
回退到文本摘要;checkpoint 与创建它的 Codex model 绑定。
|
||||
|
||||
### Pi Condense
|
||||
|
||||
组合包首次加载时会在 `contextPrune.enabled` 尚未配置的情况下写入 `true`,因此 `pi-condense` 默认开启;用户通过 `/pruner off` 写入的显式 `false` 会被保留,后续启动或升级不会重新覆盖。它使用上游默认的 `agent-message` 触发模式:同一用户任务中的多轮工具调用会在最终文本回复后统一触发整理,默认仍按每个 assistant turn 分别生成摘要;后续上下文只保留摘要和 `t1`、`t2` 等引用。需要原文时,模型可调用 `context_tree_query({ toolCallIds: ["t1"] })` 恢复。
|
||||
|
||||
常用命令:`/pruner status` 查看状态,`/pruner settings` 调整配置,`/pruner now` 立即处理待总结批次,`/pruner tree` 浏览已归档调用,`/pruner stats` 查看累计成本和节省,`/pruner off` 关闭。配置保存在 Pi agent 目录 `settings.json` 的 `contextPrune` 字段。建议保留默认的 skill 路径保护,并把必须逐字复用输出的工具加入 `protectedTools`。
|
||||
|
||||
它与 Context Mode 的职责不同:Context Mode 避免大型原始输出进入当前模型上下文,Pi Condense 则压缩已经进入会话、且已完成使用的历史工具结果。它也不替代 Codex 原生远程 compaction;不希望自动整理历史时可运行 `/pruner off`。
|
||||
|
||||
### Codex fast mode
|
||||
|
||||
该扩展只在 `openai-codex` provider 的 Responses 请求上加入
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
export type CondenseDefaultResult = "updated" | "unchanged" | "skipped-invalid";
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
/** Enable pi-condense only when the user has not already chosen an enabled state. */
|
||||
export function ensureCondenseEnabledDefault(agentDir: string): CondenseDefaultResult {
|
||||
const targetPath = join(agentDir, "settings.json");
|
||||
let settings: Record<string, unknown> = {};
|
||||
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(readFileSync(targetPath, "utf8"));
|
||||
if (!isObject(parsed)) return "skipped-invalid";
|
||||
settings = parsed;
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== "ENOENT") return "skipped-invalid";
|
||||
}
|
||||
|
||||
const existing = settings.contextPrune;
|
||||
if (existing !== undefined && !isObject(existing)) return "skipped-invalid";
|
||||
if (isObject(existing) && Object.hasOwn(existing, "enabled")) return "unchanged";
|
||||
|
||||
settings.contextPrune = { ...(existing ?? {}), enabled: true };
|
||||
mkdirSync(dirname(targetPath), { recursive: true });
|
||||
const temporaryPath = `${targetPath}.my-pi.tmp`;
|
||||
writeFileSync(temporaryPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
|
||||
renameSync(temporaryPath, targetPath);
|
||||
return "updated";
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||
import { ensureCondenseEnabledDefault } from "./condense-config.ts";
|
||||
|
||||
/** Apply the bundle default before pi-condense reads settings on session_start. */
|
||||
export default async function bundledCondenseExtension(pi: ExtensionAPI): Promise<void> {
|
||||
const result = ensureCondenseEnabledDefault(getAgentDir());
|
||||
if (result === "skipped-invalid") {
|
||||
console.warn("my-pi: skipped the pi-condense default because settings.json or contextPrune is invalid");
|
||||
}
|
||||
const { default: condenseExtension } = await import("../node_modules/pi-condense/index.ts");
|
||||
condenseExtension(pi);
|
||||
}
|
||||
Generated
+20
@@ -15,6 +15,7 @@
|
||||
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
||||
"@tavily/pi-extension": "0.1.2",
|
||||
"context-mode": "1.0.169",
|
||||
"pi-condense": "2.9.1",
|
||||
"pi-context-view": "0.4.2",
|
||||
"pi-hashline-edit": "0.8.3",
|
||||
"pi-hermes-memory": "0.9.6",
|
||||
@@ -5624,6 +5625,25 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pi-condense": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/pi-condense/-/pi-condense-2.9.1.tgz",
|
||||
"integrity": "sha512-FDwd0RuncXTeUHmfUiKBxCnSjmgf4JOiZ83ABsxiw6IYPqIfmWWmouFVopDnfcL/36UOfyv3lP5ZV30pj2ffZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "buymeacoffee",
|
||||
"url": "https://buymeacoffee.com/jjurasszek"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@earendil-works/pi-ai": "*",
|
||||
"@earendil-works/pi-coding-agent": "*",
|
||||
"@earendil-works/pi-tui": "*",
|
||||
"@sinclair/typebox": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/pi-context-view": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/pi-context-view/-/pi-context-view-0.4.2.tgz",
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"./node_modules/@firstpick/pi-extension-codex-fast-mode/index.ts",
|
||||
"./node_modules/pi-hermes-memory/src/index.ts",
|
||||
"./node_modules/@ogulcancelik/pi-codex-compaction/index.ts",
|
||||
"./extensions/condense.ts",
|
||||
"./pi-permission-auto-review/index.ts",
|
||||
"./extensions/permission-system.ts",
|
||||
"./extensions/tool-routing.ts"
|
||||
@@ -60,6 +61,7 @@
|
||||
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
||||
"@tavily/pi-extension": "0.1.2",
|
||||
"context-mode": "1.0.169",
|
||||
"pi-condense": "2.9.1",
|
||||
"pi-context-view": "0.4.2",
|
||||
"pi-hashline-edit": "0.8.3",
|
||||
"pi-hermes-memory": "0.9.6",
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import { ensureCondenseEnabledDefault } from "../extensions/condense-config.ts";
|
||||
|
||||
async function withAgentDir(run: (agentDir: string) => Promise<void>): Promise<void> {
|
||||
const agentDir = await mkdtemp(join(tmpdir(), "my-pi-condense-"));
|
||||
try {
|
||||
await run(agentDir);
|
||||
} finally {
|
||||
await rm(agentDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
test("pi-condense defaults to enabled when contextPrune is absent", async () => {
|
||||
await withAgentDir(async (agentDir) => {
|
||||
assert.equal(ensureCondenseEnabledDefault(agentDir), "updated");
|
||||
assert.deepEqual(JSON.parse(await readFile(join(agentDir, "settings.json"), "utf8")), {
|
||||
contextPrune: { enabled: true },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("pi-condense default preserves existing settings and contextPrune fields", async () => {
|
||||
await withAgentDir(async (agentDir) => {
|
||||
const path = join(agentDir, "settings.json");
|
||||
await writeFile(path, JSON.stringify({ theme: "dark", contextPrune: { minBatchChars: 4000 } }), "utf8");
|
||||
assert.equal(ensureCondenseEnabledDefault(agentDir), "updated");
|
||||
assert.deepEqual(JSON.parse(await readFile(path, "utf8")), {
|
||||
theme: "dark",
|
||||
contextPrune: { minBatchChars: 4000, enabled: true },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("pi-condense default respects an explicit enabled choice", async () => {
|
||||
await withAgentDir(async (agentDir) => {
|
||||
const path = join(agentDir, "settings.json");
|
||||
const original = `${JSON.stringify({ contextPrune: { enabled: false } }, null, 2)}\n`;
|
||||
await writeFile(path, original, "utf8");
|
||||
assert.equal(ensureCondenseEnabledDefault(agentDir), "unchanged");
|
||||
assert.equal(await readFile(path, "utf8"), original);
|
||||
});
|
||||
});
|
||||
|
||||
test("pi-condense default does not overwrite malformed settings", async () => {
|
||||
await withAgentDir(async (agentDir) => {
|
||||
const path = join(agentDir, "settings.json");
|
||||
await writeFile(path, "{not-json", "utf8");
|
||||
assert.equal(ensureCondenseEnabledDefault(agentDir), "skipped-invalid");
|
||||
assert.equal(await readFile(path, "utf8"), "{not-json");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user