@zhushanwen/pi-goal 0.9.0 → 0.9.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 -2
- package/package.json +10 -6
- package/src/__tests__/ports.test.ts +1 -1
- package/src/__tests__/session.test.ts +5 -35
- package/src/session.ts +7 -14
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ Codex 风格的 `/goal` 命令 — 持久目标驱动自主循环,支持任务
|
|
|
13
13
|
## 安装
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
# symlink
|
|
17
|
-
ln -s /path/to/xyz-pi-extensions-workspace/<branch>/extensions/goal \
|
|
16
|
+
# symlink 方式(开发推荐;globalExtDir 平铺布局,目标不带分组层)
|
|
17
|
+
ln -s /path/to/xyz-pi-extensions-workspace/<branch>/extensions/universal/goal \
|
|
18
18
|
~/.pi/agent/extensions/goal
|
|
19
19
|
|
|
20
20
|
# npm 方式(正式)
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-goal",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Codex-style /goal command for Pi — persistent goal-driven autonomous loop with evidence-based completion, token/time budgets, blocked detection, and steering templates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
|
+
"xyz-agent": {
|
|
8
|
+
"role": "universal"
|
|
9
|
+
},
|
|
7
10
|
"pi": {
|
|
8
11
|
"extensions": [
|
|
9
12
|
"./index.ts"
|
|
@@ -22,14 +25,14 @@
|
|
|
22
25
|
"index.ts"
|
|
23
26
|
],
|
|
24
27
|
"peerDependencies": {
|
|
25
|
-
"@earendil-works/pi-ai": "
|
|
26
|
-
"@earendil-works/pi-coding-agent": "
|
|
27
|
-
"@earendil-works/pi-tui": "
|
|
28
|
+
"@earendil-works/pi-ai": "^0.84.1",
|
|
29
|
+
"@earendil-works/pi-coding-agent": "^0.84.1",
|
|
30
|
+
"@earendil-works/pi-tui": "^0.84.1",
|
|
28
31
|
"typebox": "*",
|
|
29
|
-
"@zhushanwen/pi-pending-notifications": "0.3.
|
|
32
|
+
"@zhushanwen/pi-pending-notifications": "0.3.4"
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
32
|
-
"@xyz-agent/extension-protocol": "
|
|
35
|
+
"@xyz-agent/extension-protocol": "0.5.1"
|
|
33
36
|
},
|
|
34
37
|
"peerDependenciesMeta": {
|
|
35
38
|
"@earendil-works/pi-tui": {
|
|
@@ -41,6 +44,7 @@
|
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
43
46
|
"@fast-check/vitest": "^0.4.1",
|
|
47
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
44
48
|
"fast-check": "^4.9.0",
|
|
45
49
|
"vitest": "^4.1.8"
|
|
46
50
|
},
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - setGuiWidget 经 guiSetWidget 编码 marker 进 ctx.ui.setWidget(RPC 模式)
|
|
6
6
|
* - isGui 反映 ctx.mode(rpc → GUI 渲染通道;tui → 原生文本)
|
|
7
7
|
*
|
|
8
|
-
* 运行:cd extensions/goal && npx vitest run src/__tests__/ports.test.ts
|
|
8
|
+
* 运行:cd extensions/universal/goal && npx vitest run src/__tests__/ports.test.ts
|
|
9
9
|
*/
|
|
10
10
|
import { describe, expect, it } from "vitest";
|
|
11
11
|
import { GUI_WIDGET_MARKER, guiComponent } from "@xyz-agent/extension-protocol";
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* session.ts 测试 — reconstructGoalState
|
|
2
|
+
* session.ts 测试 — reconstructGoalState
|
|
3
3
|
*
|
|
4
4
|
* 覆盖:
|
|
5
5
|
* - MF-7: reconstructGoalState 3 条 FR(G-006 entry GC / G-015 强制激活 / G-024 throw→null)
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* [REMOVED W4] isStaleContextError + STALE_CONTEXT_PATTERNS 测试随实现删除——
|
|
8
|
+
* 双重废(无生产调用方 + patterns 与 pi 0.84.1 真实 stale 文案零匹配),见
|
|
9
|
+
* session.ts 的 [REMOVED W4] 注释。
|
|
7
10
|
*
|
|
8
11
|
* 用 fake SessionPort(内存 entries 数组)。
|
|
9
12
|
*/
|
|
@@ -19,9 +22,7 @@ import {
|
|
|
19
22
|
import type { SessionEntryLike, SessionPort } from "../ports";
|
|
20
23
|
import {
|
|
21
24
|
createGoalSession,
|
|
22
|
-
isStaleContextError,
|
|
23
25
|
reconstructGoalState,
|
|
24
|
-
STALE_CONTEXT_PATTERNS,
|
|
25
26
|
} from "../session";
|
|
26
27
|
|
|
27
28
|
// ── Fake SessionPort ─────────────────────────────────
|
|
@@ -39,37 +40,6 @@ function makeGoalStateEntry(state: GoalRuntimeState): SessionEntryLike {
|
|
|
39
40
|
return { type: "custom", customType: ENTRY_TYPE, data: serializeState(state) };
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
// ── isStaleContextError(MF-8, FR-8.2 G-010)──────────
|
|
43
|
-
|
|
44
|
-
describe("isStaleContextError (FR-8.2 G-010)", () => {
|
|
45
|
-
it("5 个 STALE_CONTEXT_PATTERNS 各匹配", () => {
|
|
46
|
-
for (const pattern of STALE_CONTEXT_PATTERNS) {
|
|
47
|
-
const err = new Error(`something ${pattern} happened`);
|
|
48
|
-
expect(isStaleContextError(err)).toBe(true);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("大小写混合匹配", () => {
|
|
53
|
-
expect(isStaleContextError(new Error("Context CANCELED"))).toBe(true);
|
|
54
|
-
expect(isStaleContextError(new Error("ABORTED by user"))).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("非 stale 错误 → false", () => {
|
|
58
|
-
expect(isStaleContextError(new Error("network timeout"))).toBe(false);
|
|
59
|
-
expect(isStaleContextError(new Error("permission denied"))).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("字符串输入(非 Error)", () => {
|
|
63
|
-
expect(isStaleContextError("the context was aborted")).toBe(true);
|
|
64
|
-
expect(isStaleContextError("random error string")).toBe(false);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("null/undefined 输入 → false(不抛错)", () => {
|
|
68
|
-
expect(isStaleContextError(null)).toBe(false);
|
|
69
|
-
expect(isStaleContextError(undefined)).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
43
|
// ── reconstructGoalState(MF-7)──────────────────────
|
|
74
44
|
|
|
75
45
|
describe("reconstructGoalState", () => {
|
package/src/session.ts
CHANGED
|
@@ -30,20 +30,13 @@ export function createGoalSession(): GoalSession {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// ── Stale Context 检测(FR-8.2 G-010)─────────────────
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
] as const;
|
|
41
|
-
|
|
42
|
-
export function isStaleContextError(error: Error | unknown): boolean {
|
|
43
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
44
|
-
const lower = msg.toLowerCase();
|
|
45
|
-
return STALE_CONTEXT_PATTERNS.some((p) => lower.includes(p));
|
|
46
|
-
}
|
|
33
|
+
// [REMOVED W4] 原实现导出 STALE_CONTEXT_PATTERNS + isStaleContextError,双重废:
|
|
34
|
+
// ① 无生产调用方(仅测试引用)——goal 的 appendEntry/sendMessage 调用点均在 pi
|
|
35
|
+
// event handler 内,pi runner 对 handler 错误有隔离,无「捕获后分诊 stale」的场景;
|
|
36
|
+
// ② patterns 与 pi 0.84.1 真实 stale 文案零匹配("This extension ctx is stale
|
|
37
|
+
// after session replacement...",runner.js:352——"stale context" ≠ "ctx is stale")。
|
|
38
|
+
// 复用价值场景请参照 scheduler 的已验证方案:runtime.ts STALE_CTX_MARKER
|
|
39
|
+
// 'stale after session replacement'(子串匹配真实文案)+ 代际计数治本(G1)。
|
|
47
40
|
|
|
48
41
|
// ── reconstructGoalState(session_start 时调)──────────
|
|
49
42
|
|