@zhushanwen/pi-smart-context 0.4.1 → 0.4.2
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/README.md +2 -8
- package/package.json +1 -1
- package/src/__tests__/tool.test.ts +28 -1
- package/src/render-result.ts +3 -28
- package/src/tool.ts +0 -3
package/README.md
CHANGED
|
@@ -32,15 +32,9 @@
|
|
|
32
32
|
pi install npm:@zhushanwen/pi-smart-context
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## 工具激活
|
|
36
36
|
|
|
37
|
-
`compact_context` 工具为 `exposure
|
|
38
|
-
|
|
39
|
-
```json
|
|
40
|
-
{ "defaultTools": ["+tool_search"] }
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
`+` 前缀是增量修饰(保留 read/bash/edit/write 默认集)。未开启 `tool_search` 时该工具对模型不可见。
|
|
37
|
+
`compact_context` 工具为 direct 常驻——注册不声明 `exposure` 字段(pi SDK 缺省 `direct`),工具声明随会话常驻、对模型默认可见,无需经 `tool_search` 检索激活,独立 pi 开箱即用。
|
|
44
38
|
|
|
45
39
|
## 配置
|
|
46
40
|
|
package/package.json
CHANGED
|
@@ -12,11 +12,14 @@ vi.mock("../pure.js", async (importOriginal) => {
|
|
|
12
12
|
import { DEFAULT_SMART_CONTEXT_CONFIG, loadSmartContextConfig } from "../pure.js";
|
|
13
13
|
import { registerCompactContextTool } from "../tool.js";
|
|
14
14
|
import { countCompactions } from "../pure.js";
|
|
15
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
16
16
|
|
|
17
17
|
interface RegisteredTool {
|
|
18
18
|
name: string;
|
|
19
|
+
/** pi SDK 的 exposure 缺省值 "direct"(常驻)——不写字段时 SDK 侧默认(pi-semantics PS-91),本视图可缺省。 */
|
|
20
|
+
exposure?: string;
|
|
19
21
|
parameters: { type?: string };
|
|
22
|
+
renderResult?: (result: unknown, options: { expanded: boolean }, theme: Theme) => { render: (width: number) => string[] };
|
|
20
23
|
execute: (id: string, params: Record<string, unknown>, signal: undefined, onUpdate: undefined, ctx: ExtensionContext) => Promise<{ content: Array<{ type: string; text: string }>; details: Record<string, unknown> }>;
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -67,6 +70,17 @@ describe("compact_context 工具(R2 降级态:fire-and-forget + 结果注入
|
|
|
67
70
|
expect(tools[0].name).toBe("compact_context");
|
|
68
71
|
});
|
|
69
72
|
|
|
73
|
+
// exposure 重划终态锚(C-ext-05,2026-10-23 ADR-0136):direct 常驻 = 注册时不写
|
|
74
|
+
// exposure 字段(SDK 缺省 direct,pi-semantics PS-91)——误写 "deferred" 会把压缩入口
|
|
75
|
+
// 从模型默认可见集收走(tool_search 才可激活),此处红。
|
|
76
|
+
it("exposure 注册终态:direct 常驻——不写 exposure 字段(SDK 缺省 direct)", () => {
|
|
77
|
+
const { pi, tools } = makePi();
|
|
78
|
+
registerCompactContextTool(pi);
|
|
79
|
+
expect(tools).toHaveLength(1);
|
|
80
|
+
expect(tools[0]!.name).toBe("compact_context");
|
|
81
|
+
expect(tools[0]!.exposure).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
70
84
|
it("门控拒绝(排除命中)→ throw 带原因与恢复指引(D5)", async () => {
|
|
71
85
|
mockedLoad.mockReturnValue({ ...DEFAULT_SMART_CONTEXT_CONFIG, excludedModels: ["zai/glm"] });
|
|
72
86
|
const { pi, tools } = makePi();
|
|
@@ -95,6 +109,19 @@ describe("compact_context 工具(R2 降级态:fire-and-forget + 结果注入
|
|
|
95
109
|
expect(result.details).toMatchObject({ mode: "same-model", launched: true, fellBack: false, compactionCount: 1 });
|
|
96
110
|
});
|
|
97
111
|
|
|
112
|
+
it("renderResult 接线(C-ext-05):注册定义携带函数成员 + 折叠态输出 details.summary 一行", async () => {
|
|
113
|
+
const { pi, tools } = makePi();
|
|
114
|
+
registerCompactContextTool(pi, { getEntries: () => [{ type: "message" }] });
|
|
115
|
+
const tool = tools[0];
|
|
116
|
+
expect(typeof tool.renderResult).toBe("function");
|
|
117
|
+
const result = await tool.execute("t1", {}, undefined, undefined, makeCtx());
|
|
118
|
+
const renderTheme = { fg: (_token: string, text: string) => text } as unknown as Theme;
|
|
119
|
+
const lines = tool.renderResult!(result, { expanded: false }, renderTheme).render(400);
|
|
120
|
+
// Text.render 按宽度 pad,trim 后断言(折叠态 = details.summary 一行)
|
|
121
|
+
expect(lines.length).toBe(1);
|
|
122
|
+
expect(lines[0].trim()).toBe("Context compacted");
|
|
123
|
+
});
|
|
124
|
+
|
|
98
125
|
it("onComplete 兑现后 nextTurn 注入结果(含模式/前后 tokens/成本;降智提示按次数)", async () => {
|
|
99
126
|
const { pi, tools } = makePi();
|
|
100
127
|
registerCompactContextTool(pi, { getEntries: () => [{ type: "compaction" }, { type: "compaction" }] });
|
package/src/render-result.ts
CHANGED
|
@@ -4,32 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { Text } from "@earendil-works/pi-tui";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
/** content[0] 的 text 纯文本(content 缺失或非 text 类型时兜底空串)。 */
|
|
10
|
-
function contentText(result: unknown): string {
|
|
11
|
-
const content = fieldValue(result, "content");
|
|
12
|
-
if (!Array.isArray(content)) return "";
|
|
13
|
-
return firstContentText({ content });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** unknown 值按字段名安全取值(index-signature 收窄,替代全可选结构断言)。 */
|
|
17
|
-
function fieldValue(v: unknown, key: string): unknown {
|
|
18
|
-
if (typeof v !== "object" || v === null) return undefined;
|
|
19
|
-
return (v as Record<string, unknown>)[key];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** 从 details 安全读取 summary(容错非对象形态与旧版本 entry)。 */
|
|
23
|
-
function readDetailsSummary(result: unknown): string | undefined {
|
|
24
|
-
const summary = fieldValue(fieldValue(result, "details"), "summary");
|
|
25
|
-
return typeof summary === "string" && summary !== "" ? summary : undefined;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 折叠态回退:content 文本首行(保持一行摘要形态)。 */
|
|
29
|
-
function firstLine(text: string): string {
|
|
30
|
-
const idx = text.indexOf("\n");
|
|
31
|
-
return idx === -1 ? text : text.slice(0, idx);
|
|
32
|
-
}
|
|
7
|
+
import { firstLine, readDetailsSummary, resultContentText } from "@zhushanwen/extension-protocol";
|
|
33
8
|
|
|
34
9
|
/** 折叠态(expanded=false)= details.summary 一行(dim);展开态 = content 纯文本。 */
|
|
35
10
|
export function renderCompactContextResult(
|
|
@@ -37,7 +12,7 @@ export function renderCompactContextResult(
|
|
|
37
12
|
options: { expanded: boolean },
|
|
38
13
|
theme: Theme,
|
|
39
14
|
): Text {
|
|
40
|
-
if (options.expanded) return new Text(
|
|
41
|
-
const summary = readDetailsSummary(result) ?? firstLine(
|
|
15
|
+
if (options.expanded) return new Text(resultContentText(result), 0, 0);
|
|
16
|
+
const summary = readDetailsSummary(result) ?? firstLine(resultContentText(result));
|
|
42
17
|
return new Text(theme.fg("dim", summary), 0, 0);
|
|
43
18
|
}
|
package/src/tool.ts
CHANGED
|
@@ -153,9 +153,6 @@ export function registerCompactContextTool(
|
|
|
153
153
|
pi.registerTool({
|
|
154
154
|
name: "compact_context",
|
|
155
155
|
label: "compact_context",
|
|
156
|
-
// deferred(tool-declaration-slimming D4):不常驻声明,tool_search 命中即激活;
|
|
157
|
-
// 迁移写入的 +tool_search 条目(runtime pi-codemode-settings)承载检索入口
|
|
158
|
-
exposure: "deferred",
|
|
159
156
|
description: TOOL_DESCRIPTION,
|
|
160
157
|
parameters: CompactContextParams,
|
|
161
158
|
execute: async (_toolCallId, params, _signal, _onUpdate, ctx) => {
|