oh-pi 0.1.67 → 0.1.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/types.js +1 -0
- package/package.json +1 -1
- package/pi-package/extensions/smart-compact.ts +89 -0
package/dist/index.js
CHANGED
|
@@ -43,7 +43,7 @@ async function quickFlow(env) {
|
|
|
43
43
|
providers,
|
|
44
44
|
theme: "dark",
|
|
45
45
|
keybindings: "default",
|
|
46
|
-
extensions: ["safe-guard", "git-guard", "auto-session-name", "custom-footer", "compact-header", "auto-update"],
|
|
46
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name", "custom-footer", "compact-header", "auto-update", "smart-compact"],
|
|
47
47
|
prompts: ["review", "fix", "explain", "commit", "test"],
|
|
48
48
|
agents: "general-developer",
|
|
49
49
|
thinking: "medium",
|
package/dist/types.js
CHANGED
|
@@ -49,6 +49,7 @@ export const EXTENSIONS = [
|
|
|
49
49
|
{ name: "compact-header", label: "⚡ Compact Header — Dense startup info replacing verbose output", default: true },
|
|
50
50
|
{ name: "ant-colony", label: "🐜 Ant Colony — Autonomous multi-agent swarm with adaptive concurrency", default: false },
|
|
51
51
|
{ name: "auto-update", label: "🔄 Auto Update — Check for oh-pi updates on startup and notify", default: true },
|
|
52
|
+
{ name: "smart-compact", label: "🗜️ Smart Compact — Trim large tool outputs and old messages in-flight", default: true },
|
|
52
53
|
{ name: "bg-process", label: "⏳ Bg Process — Auto-background long-running commands (dev servers, etc.)", default: false },
|
|
53
54
|
];
|
|
54
55
|
/** 快捷键绑定方案(default / vim / emacs) */
|
package/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 智能压缩扩展 — 在发送给 LLM 前裁剪大块内容
|
|
3
|
+
*
|
|
4
|
+
* 策略:
|
|
5
|
+
* 1. 工具输出超过阈值 → 保留首尾,中间替换为 "[...truncated N lines]"
|
|
6
|
+
* 2. 用户粘贴的大块文本 → 同上
|
|
7
|
+
* 3. 越旧的消息裁剪越激进
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const MAX_TOOL_OUTPUT_CHARS = 8000;
|
|
11
|
+
const MAX_USER_BLOCK_CHARS = 12000;
|
|
12
|
+
const KEEP_HEAD = 1500;
|
|
13
|
+
const KEEP_TAIL = 2500;
|
|
14
|
+
|
|
15
|
+
function truncateText(text: string, max: number, head = KEEP_HEAD, tail = KEEP_TAIL): string {
|
|
16
|
+
if (text.length <= max) return text;
|
|
17
|
+
const lines = text.split("\n");
|
|
18
|
+
if (lines.length <= 10) return text; // 短文本不裁
|
|
19
|
+
const headText = text.slice(0, head);
|
|
20
|
+
const tailText = text.slice(-tail);
|
|
21
|
+
const removedLines = text.slice(head, -tail).split("\n").length;
|
|
22
|
+
return `${headText}\n\n[...truncated ${removedLines} lines...]\n\n${tailText}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function compactContent(content: any): any {
|
|
26
|
+
if (typeof content === "string") {
|
|
27
|
+
return truncateText(content, MAX_TOOL_OUTPUT_CHARS);
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(content)) return content;
|
|
30
|
+
return content.map((block: any) => {
|
|
31
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
32
|
+
return { ...block, text: truncateText(block.text, MAX_TOOL_OUTPUT_CHARS) };
|
|
33
|
+
}
|
|
34
|
+
return block;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default function smartCompact(pi: any) {
|
|
39
|
+
pi.on("context", async (event: any) => {
|
|
40
|
+
const messages = event.messages;
|
|
41
|
+
if (!messages || messages.length < 4) return; // 太短不处理
|
|
42
|
+
|
|
43
|
+
// 只处理非最近 3 条消息(保留最近上下文完整)
|
|
44
|
+
const cutoff = messages.length - 3;
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < cutoff; i++) {
|
|
47
|
+
const msg = messages[i];
|
|
48
|
+
if (!msg) continue;
|
|
49
|
+
|
|
50
|
+
if (msg.role === "toolResult") {
|
|
51
|
+
msg.content = compactContent(msg.content);
|
|
52
|
+
} else if (msg.role === "user") {
|
|
53
|
+
// 用户消息用更宽松的阈值
|
|
54
|
+
if (typeof msg.content === "string" && msg.content.length > MAX_USER_BLOCK_CHARS) {
|
|
55
|
+
msg.content = truncateText(msg.content, MAX_USER_BLOCK_CHARS);
|
|
56
|
+
} else if (Array.isArray(msg.content)) {
|
|
57
|
+
msg.content = msg.content.map((block: any) => {
|
|
58
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.length > MAX_USER_BLOCK_CHARS) {
|
|
59
|
+
return { ...block, text: truncateText(block.text, MAX_USER_BLOCK_CHARS) };
|
|
60
|
+
}
|
|
61
|
+
return block;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} else if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
65
|
+
// 裁剪 assistant 的大块工具调用参数
|
|
66
|
+
msg.content = msg.content.map((block: any) => {
|
|
67
|
+
if (block.type === "toolCall" && block.arguments) {
|
|
68
|
+
const args = JSON.stringify(block.arguments);
|
|
69
|
+
if (args.length > MAX_TOOL_OUTPUT_CHARS) {
|
|
70
|
+
try {
|
|
71
|
+
const parsed = typeof block.arguments === "string" ? JSON.parse(block.arguments) : block.arguments;
|
|
72
|
+
// 裁剪大字符串参数
|
|
73
|
+
for (const key of Object.keys(parsed)) {
|
|
74
|
+
if (typeof parsed[key] === "string" && parsed[key].length > MAX_TOOL_OUTPUT_CHARS) {
|
|
75
|
+
parsed[key] = truncateText(parsed[key], MAX_TOOL_OUTPUT_CHARS);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { ...block, arguments: parsed };
|
|
79
|
+
} catch { return block; }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return block;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { messages };
|
|
88
|
+
});
|
|
89
|
+
}
|