pi-harness-runtime 1.1.49 → 1.1.71-3
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/CHANGELOG.md +30 -9
- package/README.md +119 -0
- package/harness/langchain/agents.d.ts +143 -0
- package/harness/langchain/agents.test.d.ts +2 -0
- package/harness/langchain/agents.test.ts +0 -1
- package/harness/langchain/agents.ts +196 -13
- package/harness/langchain/checkpointer.d.ts +48 -0
- package/harness/langchain/checkpointer.test.d.ts +8 -0
- package/harness/langchain/cron-cli.d.ts +12 -0
- package/harness/langchain/cron-cli.ts +169 -0
- package/harness/langchain/daemon.d.ts +161 -0
- package/harness/langchain/daemon.test.d.ts +11 -0
- package/harness/langchain/daemon.test.ts +62 -5
- package/harness/langchain/daemon.ts +231 -12
- package/harness/langchain/file-tools.d.ts +97 -0
- package/harness/langchain/file-tools.ts +29 -11
- package/harness/langchain/graph.d.ts +706 -0
- package/harness/langchain/graph.ts +184 -26
- package/harness/langchain/model-router.d.ts +63 -0
- package/harness/langchain/model-router.test.d.ts +2 -0
- package/harness/langchain/model-router.test.ts +191 -0
- package/harness/langchain/model-router.ts +188 -0
- package/harness/langchain/models.d.ts +72 -0
- package/harness/langchain/models.ts +111 -49
- package/harness/langchain/ping-pong-middleware.d.ts +28 -0
- package/harness/langchain/ping-pong-middleware.test.d.ts +2 -0
- package/harness/langchain/ping-pong-middleware.test.ts +69 -0
- package/harness/langchain/ping-pong-middleware.ts +204 -0
- package/harness/langchain/run.d.ts +15 -0
- package/harness/langchain/run.ts +15 -3
- package/harness/langchain/scoreboard.test.d.ts +13 -0
- package/harness/langchain/scoreboard.test.ts +28 -5
- package/harness/langchain/status-line.d.ts +91 -0
- package/harness/langchain/status-line.test.d.ts +8 -0
- package/harness/langchain/surge.d.ts +103 -0
- package/harness/langchain/surge.test.d.ts +10 -0
- package/harness/langchain/surge.test.ts +179 -0
- package/harness/langchain/surge.ts +188 -19
- package/harness/langchain/widget.d.ts +105 -0
- package/harness/langchain/widget.test.d.ts +8 -0
- package/harness/ssh-detach-interceptor.test.ts +263 -0
- package/harness/ssh-detach-interceptor.ts +438 -0
- package/index.ts +212 -47
- package/package.json +4 -2
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/dist/src/index.d.ts +2 -0
- package/packages/intent-analyzer/dist/src/index.d.ts.map +1 -1
- package/packages/intent-analyzer/dist/src/index.js +1 -0
- package/packages/intent-analyzer/dist/src/index.js.map +1 -1
- package/packages/intent-analyzer/dist/src/ping-pong.d.ts +56 -0
- package/packages/intent-analyzer/dist/src/ping-pong.d.ts.map +1 -0
- package/packages/intent-analyzer/dist/src/ping-pong.js +286 -0
- package/packages/intent-analyzer/dist/src/ping-pong.js.map +1 -0
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/intent-analyzer/src/index.ts +13 -0
- package/packages/intent-analyzer/src/ping-pong.ts +387 -0
- package/packages/intent-analyzer/test/ping-pong.test.ts +96 -0
- package/packages/jev-judge/README.md +234 -0
- package/packages/jev-judge/dist/decision-engine.d.ts +100 -0
- package/packages/jev-judge/dist/decision-engine.d.ts.map +1 -0
- package/packages/jev-judge/dist/decision-engine.js +151 -0
- package/packages/jev-judge/dist/decision-engine.js.map +1 -0
- package/packages/jev-judge/dist/e2e-judge.d.ts +61 -0
- package/packages/jev-judge/dist/e2e-judge.d.ts.map +1 -0
- package/packages/jev-judge/dist/e2e-judge.js +268 -0
- package/packages/jev-judge/dist/e2e-judge.js.map +1 -0
- package/packages/jev-judge/dist/flaky-detector.d.ts +34 -0
- package/packages/jev-judge/dist/flaky-detector.d.ts.map +1 -0
- package/packages/jev-judge/dist/flaky-detector.js +154 -0
- package/packages/jev-judge/dist/flaky-detector.js.map +1 -0
- package/packages/jev-judge/dist/index.d.ts +77 -0
- package/packages/jev-judge/dist/index.d.ts.map +1 -0
- package/packages/jev-judge/dist/index.js +271 -0
- package/packages/jev-judge/dist/index.js.map +1 -0
- package/packages/jev-judge/dist/types.d.ts +97 -0
- package/packages/jev-judge/dist/types.d.ts.map +1 -0
- package/packages/jev-judge/dist/types.js +8 -0
- package/packages/jev-judge/dist/types.js.map +1 -0
- package/packages/jev-judge/package.json +46 -0
- package/packages/jev-judge/src/decision-engine.ts +231 -0
- package/packages/jev-judge/src/e2e-judge.ts +364 -0
- package/packages/jev-judge/src/flaky-detector.ts +190 -0
- package/packages/jev-judge/src/index.ts +345 -0
- package/packages/jev-judge/src/types.ts +131 -0
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/README.md +71 -0
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/todo-bd-sync/src/extension.ts +16 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/dist/src/approved-patterns.d.ts +51 -0
- package/packages/trajectory/dist/src/approved-patterns.d.ts.map +1 -0
- package/packages/trajectory/dist/src/approved-patterns.js +134 -0
- package/packages/trajectory/dist/src/approved-patterns.js.map +1 -0
- package/packages/trajectory/dist/src/index.d.ts +13 -0
- package/packages/trajectory/dist/src/index.d.ts.map +1 -0
- package/packages/trajectory/dist/src/index.js +12 -0
- package/packages/trajectory/dist/src/index.js.map +1 -0
- package/packages/trajectory/dist/src/store.d.ts +69 -0
- package/packages/trajectory/dist/src/store.d.ts.map +1 -0
- package/packages/trajectory/dist/src/store.js +253 -0
- package/packages/trajectory/dist/src/store.js.map +1 -0
- package/packages/trajectory/dist/src/types.d.ts +73 -0
- package/packages/trajectory/dist/src/types.d.ts.map +1 -0
- package/packages/trajectory/dist/src/types.js +9 -0
- package/packages/trajectory/dist/src/types.js.map +1 -0
- package/packages/trajectory/dist/test/store.test.d.ts +2 -0
- package/packages/trajectory/dist/test/store.test.d.ts.map +1 -0
- package/packages/trajectory/dist/test/store.test.js +228 -0
- package/packages/trajectory/dist/test/store.test.js.map +1 -0
- package/packages/trajectory/package.json +29 -0
- package/packages/trajectory/src/approved-patterns.ts +180 -0
- package/packages/trajectory/src/index.ts +26 -0
- package/packages/trajectory/src/store.ts +285 -0
- package/packages/trajectory/src/types.ts +84 -0
- package/packages/trajectory/test/store.test.d.ts +2 -0
- package/packages/trajectory/test/store.test.ts +250 -0
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/packages/write-review/src/blackboard.ts +38 -5
- package/packages/write-review/src/types.ts +13 -0
- package/skills/harness-runtime/SKILL.md +67 -4
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { describe, it, beforeEach } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { rmSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { TrajectoryStore, resetTrajectoryStore } from "../src/store.js";
|
|
7
|
+
const TEST_DIR = join(homedir(), ".pi-harness-test-trajectories");
|
|
8
|
+
function freshStore() {
|
|
9
|
+
rmSync(TEST_DIR, { force: true, recursive: true });
|
|
10
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
11
|
+
return new TrajectoryStore(TEST_DIR);
|
|
12
|
+
}
|
|
13
|
+
describe("TrajectoryStore", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
rmSync(TEST_DIR, { force: true, recursive: true });
|
|
16
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
17
|
+
resetTrajectoryStore();
|
|
18
|
+
});
|
|
19
|
+
it("start() returns a UUID", () => {
|
|
20
|
+
const store = freshStore();
|
|
21
|
+
const id = store.start("test task");
|
|
22
|
+
assert.match(id, /^[0-9a-f-]{36}$/);
|
|
23
|
+
});
|
|
24
|
+
it("append() writes NDJSON to the correct daily file", () => {
|
|
25
|
+
const store = freshStore();
|
|
26
|
+
const id = "test-id-123";
|
|
27
|
+
const now = new Date();
|
|
28
|
+
store.append({
|
|
29
|
+
id,
|
|
30
|
+
taskRequest: "fix the login bug",
|
|
31
|
+
createdAt: now.toISOString(),
|
|
32
|
+
durationMs: 5000,
|
|
33
|
+
iterations: 2,
|
|
34
|
+
verdict: "approved",
|
|
35
|
+
reason: "reviewer approved",
|
|
36
|
+
plan: "# Plan\n1. Fix it",
|
|
37
|
+
code: "// fixed",
|
|
38
|
+
files: ["auth.ts"],
|
|
39
|
+
comments: [{ file: "auth.ts", comment: "looks good", severity: "minor" }],
|
|
40
|
+
summary: "looks good",
|
|
41
|
+
classified: false,
|
|
42
|
+
});
|
|
43
|
+
// Should have created a daily file
|
|
44
|
+
const files = readdirSync(TEST_DIR);
|
|
45
|
+
assert.ok(files.length > 0, "should create at least one directory");
|
|
46
|
+
// Navigate to the daily file
|
|
47
|
+
const monthDir = files[0];
|
|
48
|
+
const monthFiles = readdirSync(join(TEST_DIR, monthDir));
|
|
49
|
+
assert.ok(monthFiles.length > 0, "should create daily ndjson file");
|
|
50
|
+
const content = readFileSync(join(TEST_DIR, monthDir, monthFiles[0]), "utf8");
|
|
51
|
+
const record = JSON.parse(content.split("\n").find((l) => l.trim()));
|
|
52
|
+
assert.strictEqual(record.id, id);
|
|
53
|
+
assert.strictEqual(record.verdict, "approved");
|
|
54
|
+
assert.strictEqual(record.iterations, 2);
|
|
55
|
+
});
|
|
56
|
+
it("list() returns records newest-first", () => {
|
|
57
|
+
const store = freshStore();
|
|
58
|
+
const id1 = store.start("task 1");
|
|
59
|
+
store.append({
|
|
60
|
+
id: id1,
|
|
61
|
+
taskRequest: "task 1",
|
|
62
|
+
createdAt: "2025-07-01T10:00:00.000Z",
|
|
63
|
+
durationMs: 1000,
|
|
64
|
+
iterations: 1,
|
|
65
|
+
verdict: "approved",
|
|
66
|
+
reason: "ok",
|
|
67
|
+
plan: "p1",
|
|
68
|
+
code: "c1",
|
|
69
|
+
files: [],
|
|
70
|
+
comments: [],
|
|
71
|
+
summary: "s1",
|
|
72
|
+
classified: false,
|
|
73
|
+
});
|
|
74
|
+
const id2 = store.start("task 2");
|
|
75
|
+
store.append({
|
|
76
|
+
id: id2,
|
|
77
|
+
taskRequest: "task 2",
|
|
78
|
+
createdAt: "2025-07-02T10:00:00.000Z",
|
|
79
|
+
durationMs: 2000,
|
|
80
|
+
iterations: 2,
|
|
81
|
+
verdict: "changes_requested",
|
|
82
|
+
reason: "converged: only minor comments",
|
|
83
|
+
plan: "p2",
|
|
84
|
+
code: "c2",
|
|
85
|
+
files: [],
|
|
86
|
+
comments: [],
|
|
87
|
+
summary: "s2",
|
|
88
|
+
classified: false,
|
|
89
|
+
});
|
|
90
|
+
const all = store.list();
|
|
91
|
+
assert.strictEqual(all.length, 2);
|
|
92
|
+
assert.strictEqual(all[0].id, id2, "newest first");
|
|
93
|
+
assert.strictEqual(all[1].id, id1);
|
|
94
|
+
});
|
|
95
|
+
it("stats() aggregates correctly", () => {
|
|
96
|
+
const store = freshStore();
|
|
97
|
+
store.append({
|
|
98
|
+
id: "s1",
|
|
99
|
+
taskRequest: "t1",
|
|
100
|
+
createdAt: "2025-07-01T10:00:00.000Z",
|
|
101
|
+
durationMs: 1000,
|
|
102
|
+
iterations: 1,
|
|
103
|
+
verdict: "approved",
|
|
104
|
+
reason: "reviewer approved",
|
|
105
|
+
plan: "p",
|
|
106
|
+
code: "c",
|
|
107
|
+
files: ["a.ts"],
|
|
108
|
+
comments: [],
|
|
109
|
+
summary: "s",
|
|
110
|
+
classified: false,
|
|
111
|
+
});
|
|
112
|
+
store.append({
|
|
113
|
+
id: "s2",
|
|
114
|
+
taskRequest: "t2",
|
|
115
|
+
createdAt: "2025-07-01T11:00:00.000Z",
|
|
116
|
+
durationMs: 3000,
|
|
117
|
+
iterations: 3,
|
|
118
|
+
verdict: "changes_requested",
|
|
119
|
+
reason: "max iterations (3) reached with changes still requested",
|
|
120
|
+
plan: "p",
|
|
121
|
+
code: "c",
|
|
122
|
+
files: ["a.ts", "b.ts"],
|
|
123
|
+
comments: [],
|
|
124
|
+
summary: "s",
|
|
125
|
+
classified: false,
|
|
126
|
+
});
|
|
127
|
+
const stats = store.stats();
|
|
128
|
+
assert.strictEqual(stats.total, 2);
|
|
129
|
+
assert.strictEqual(stats.byVerdict.approved, 1);
|
|
130
|
+
assert.strictEqual(stats.byVerdict.changes_requested, 1);
|
|
131
|
+
assert.strictEqual(stats.avgIterations, 2);
|
|
132
|
+
assert.strictEqual(stats.avgDurationMs, 2000);
|
|
133
|
+
assert.strictEqual(stats.byFile["a.ts"], 2);
|
|
134
|
+
assert.strictEqual(stats.byFile["b.ts"], 1);
|
|
135
|
+
assert.strictEqual(stats.byLabel["max-iterations"], 1);
|
|
136
|
+
assert.strictEqual(stats.byLabel["converged"], 1);
|
|
137
|
+
});
|
|
138
|
+
it("classify() returns correct label for converged", () => {
|
|
139
|
+
const store = freshStore();
|
|
140
|
+
const cls = store.classify({
|
|
141
|
+
id: "x",
|
|
142
|
+
taskRequest: "x",
|
|
143
|
+
createdAt: new Date().toISOString(),
|
|
144
|
+
durationMs: 1000,
|
|
145
|
+
iterations: 1,
|
|
146
|
+
verdict: "approved",
|
|
147
|
+
reason: "reviewer approved",
|
|
148
|
+
plan: "",
|
|
149
|
+
code: "",
|
|
150
|
+
files: [],
|
|
151
|
+
comments: [],
|
|
152
|
+
summary: "",
|
|
153
|
+
classified: false,
|
|
154
|
+
});
|
|
155
|
+
assert.strictEqual(cls.label, "converged");
|
|
156
|
+
assert.strictEqual(cls.trajectoryId, "x");
|
|
157
|
+
});
|
|
158
|
+
it("classify() returns stuck for same-file reason", () => {
|
|
159
|
+
const store = freshStore();
|
|
160
|
+
const cls = store.classify({
|
|
161
|
+
id: "y",
|
|
162
|
+
taskRequest: "y",
|
|
163
|
+
createdAt: new Date().toISOString(),
|
|
164
|
+
durationMs: 1000,
|
|
165
|
+
iterations: 2,
|
|
166
|
+
verdict: "changes_requested",
|
|
167
|
+
reason: "stuck: same file (auth.ts) flagged for 3 comment(s)",
|
|
168
|
+
plan: "",
|
|
169
|
+
code: "",
|
|
170
|
+
files: ["auth.ts"],
|
|
171
|
+
comments: [],
|
|
172
|
+
summary: "",
|
|
173
|
+
classified: false,
|
|
174
|
+
});
|
|
175
|
+
assert.strictEqual(cls.label, "stuck");
|
|
176
|
+
assert.strictEqual(cls.pattern, "same-file-repeated");
|
|
177
|
+
assert.ok(cls.confidence < 1.0);
|
|
178
|
+
});
|
|
179
|
+
it("byVerdict() filters correctly", () => {
|
|
180
|
+
const store = freshStore();
|
|
181
|
+
store.append({
|
|
182
|
+
id: "v1",
|
|
183
|
+
taskRequest: "v1",
|
|
184
|
+
createdAt: "2025-07-01T10:00:00.000Z",
|
|
185
|
+
durationMs: 1000,
|
|
186
|
+
iterations: 1,
|
|
187
|
+
verdict: "approved",
|
|
188
|
+
reason: "ok",
|
|
189
|
+
plan: "",
|
|
190
|
+
code: "",
|
|
191
|
+
files: [],
|
|
192
|
+
comments: [],
|
|
193
|
+
summary: "",
|
|
194
|
+
classified: false,
|
|
195
|
+
});
|
|
196
|
+
store.append({
|
|
197
|
+
id: "v2",
|
|
198
|
+
taskRequest: "v2",
|
|
199
|
+
createdAt: "2025-07-02T10:00:00.000Z",
|
|
200
|
+
durationMs: 1000,
|
|
201
|
+
iterations: 1,
|
|
202
|
+
verdict: "blocked",
|
|
203
|
+
reason: "blocked",
|
|
204
|
+
plan: "",
|
|
205
|
+
code: "",
|
|
206
|
+
files: [],
|
|
207
|
+
comments: [],
|
|
208
|
+
summary: "",
|
|
209
|
+
classified: false,
|
|
210
|
+
});
|
|
211
|
+
const approved = store.byVerdict("approved");
|
|
212
|
+
assert.strictEqual(approved.length, 1);
|
|
213
|
+
assert.strictEqual(approved[0].id, "v1");
|
|
214
|
+
const blocked = store.byVerdict("blocked");
|
|
215
|
+
assert.strictEqual(blocked.length, 1);
|
|
216
|
+
assert.strictEqual(blocked[0].id, "v2");
|
|
217
|
+
});
|
|
218
|
+
it("stats() handles empty store gracefully", () => {
|
|
219
|
+
rmSync(TEST_DIR, { force: true, recursive: true });
|
|
220
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
221
|
+
// Remove the month dir so the store sees an empty directory
|
|
222
|
+
const store = freshStore();
|
|
223
|
+
const stats = store.stats();
|
|
224
|
+
assert.strictEqual(stats.total, 0);
|
|
225
|
+
assert.strictEqual(stats.avgIterations, 0);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
//# sourceMappingURL=store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../test/store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pi-harness/trajectory",
|
|
3
|
+
"version": "1.1.71-3",
|
|
4
|
+
"description": "Trajectory capture for the write-review loop — persists cycle records for learning",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/src/index.js",
|
|
7
|
+
"types": "./dist/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/src/index.js",
|
|
11
|
+
"types": "./dist/src/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js",
|
|
17
|
+
"lint": "biome check src"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"typescript": "^5.7.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@moocoding/write-review": "workspace:*"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ApprovedPatternStore — tracks review patterns that have been approved.
|
|
3
|
+
*
|
|
4
|
+
* Once a reviewer approves code with a given comment, that comment pattern is
|
|
5
|
+
* "learned" and the reviewer should not re-flag it in future sessions.
|
|
6
|
+
*
|
|
7
|
+
* Storage: `~/.pi-harness/approved-patterns.json`
|
|
8
|
+
* Format: keyed by pattern-hash so lookup is O(1)
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* - Load patterns before each review → inject into reviewer prompt
|
|
12
|
+
* - After "approved" verdict → call .approve() to persist new patterns
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
readFileSync,
|
|
17
|
+
writeFileSync,
|
|
18
|
+
existsSync,
|
|
19
|
+
mkdirSync,
|
|
20
|
+
renameSync,
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { join, dirname } from "node:path";
|
|
23
|
+
import { homedir } from "node:os";
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
|
|
26
|
+
const APPROVED_PATTERNS_FILE = ".pi-harness/approved-patterns.json";
|
|
27
|
+
|
|
28
|
+
export interface ApprovedPattern {
|
|
29
|
+
hash: string;
|
|
30
|
+
file?: string;
|
|
31
|
+
comment: string;
|
|
32
|
+
severity: string;
|
|
33
|
+
approvedAt: string; // ISO
|
|
34
|
+
approvalCount: number; // how many times this pattern was approved
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ApprovedPatternStoreData {
|
|
38
|
+
version: number;
|
|
39
|
+
patterns: Record<string, ApprovedPattern>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getPatternsPath(): string {
|
|
43
|
+
return join(homedir(), APPROVED_PATTERNS_FILE);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function defaultData(): ApprovedPatternStoreData {
|
|
47
|
+
return { version: 1, patterns: {} };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function hashPattern(
|
|
51
|
+
file: string | undefined,
|
|
52
|
+
comment: string,
|
|
53
|
+
severity: string,
|
|
54
|
+
): string {
|
|
55
|
+
return createHash("sha256")
|
|
56
|
+
.update(`${file ?? ""}|${comment}|${severity}`)
|
|
57
|
+
.digest("hex")
|
|
58
|
+
.slice(0, 16);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Singleton store for approved review patterns. */
|
|
62
|
+
export class ApprovedPatternStore {
|
|
63
|
+
private readonly _path: string;
|
|
64
|
+
private _data: ApprovedPatternStoreData;
|
|
65
|
+
|
|
66
|
+
constructor(path?: string) {
|
|
67
|
+
this._path = path ?? getPatternsPath();
|
|
68
|
+
this._data = this._load();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private _load(): ApprovedPatternStoreData {
|
|
72
|
+
if (!existsSync(this._path)) return defaultData();
|
|
73
|
+
try {
|
|
74
|
+
const raw = readFileSync(this._path, "utf8");
|
|
75
|
+
return JSON.parse(raw) as ApprovedPatternStoreData;
|
|
76
|
+
} catch {
|
|
77
|
+
return defaultData();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private _save(): void {
|
|
82
|
+
mkdirSync(dirname(this._path), { recursive: true });
|
|
83
|
+
// Atomic write: temp file + rename
|
|
84
|
+
const tmp = `${this._path}.${Date.now()}.tmp`;
|
|
85
|
+
writeFileSync(tmp, JSON.stringify(this._data, null, "\t"), "utf8");
|
|
86
|
+
renameSync(tmp, this._path);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Check if a comment matches an approved pattern. Returns the pattern or null. */
|
|
90
|
+
get(
|
|
91
|
+
file: string | undefined,
|
|
92
|
+
comment: string,
|
|
93
|
+
severity: string,
|
|
94
|
+
): ApprovedPattern | null {
|
|
95
|
+
const hash = hashPattern(file, comment, severity);
|
|
96
|
+
return this._data.patterns[hash] ?? null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Record that a pattern was approved. Called after "approved" verdict. */
|
|
100
|
+
approve(
|
|
101
|
+
comments: Array<{ file?: string; comment: string; severity?: string }>,
|
|
102
|
+
): void {
|
|
103
|
+
const now = new Date().toISOString();
|
|
104
|
+
let changed = false;
|
|
105
|
+
for (const c of comments) {
|
|
106
|
+
const sev = c.severity ?? "minor";
|
|
107
|
+
const hash = hashPattern(c.file, c.comment, sev);
|
|
108
|
+
const existing = this._data.patterns[hash];
|
|
109
|
+
if (existing) {
|
|
110
|
+
existing.approvalCount++;
|
|
111
|
+
existing.approvedAt = now;
|
|
112
|
+
} else {
|
|
113
|
+
this._data.patterns[hash] = {
|
|
114
|
+
hash,
|
|
115
|
+
file: c.file,
|
|
116
|
+
comment: c.comment,
|
|
117
|
+
severity: sev,
|
|
118
|
+
approvedAt: now,
|
|
119
|
+
approvalCount: 1,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
changed = true;
|
|
123
|
+
}
|
|
124
|
+
if (changed) this._save();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Build a markdown section for injecting into the reviewer prompt.
|
|
129
|
+
* Lists previously-approved patterns so the reviewer can skip them.
|
|
130
|
+
*/
|
|
131
|
+
toMarkdown(): string {
|
|
132
|
+
const patterns = Object.values(this._data.patterns);
|
|
133
|
+
if (patterns.length === 0) return "";
|
|
134
|
+
|
|
135
|
+
// Show the most recently approved first (top 20)
|
|
136
|
+
const recent = patterns
|
|
137
|
+
.sort((a, b) => b.approvalCount - a.approvalCount)
|
|
138
|
+
.slice(0, 20);
|
|
139
|
+
|
|
140
|
+
const lines = ["## Previously Approved Patterns", ""];
|
|
141
|
+
for (const p of recent) {
|
|
142
|
+
const file = p.file ? `\`${p.file}\`: ` : "";
|
|
143
|
+
lines.push(
|
|
144
|
+
`- ${file}${p.comment} [${p.severity}, approved ${p.approvalCount}x]`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
lines.push("");
|
|
148
|
+
lines.push(
|
|
149
|
+
"Do NOT flag these patterns again — they were approved in previous reviews.",
|
|
150
|
+
);
|
|
151
|
+
return lines.join("\n");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Get count of known patterns. */
|
|
155
|
+
get size(): number {
|
|
156
|
+
return Object.keys(this._data.patterns).length;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Export all patterns as an array. */
|
|
160
|
+
toArray(): ApprovedPattern[] {
|
|
161
|
+
return Object.values(this._data.patterns);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Clear all patterns (for testing). */
|
|
165
|
+
clear(): void {
|
|
166
|
+
this._data = defaultData();
|
|
167
|
+
this._save();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Singleton. */
|
|
172
|
+
let _store: ApprovedPatternStore | undefined;
|
|
173
|
+
export function getApprovedPatternStore(): ApprovedPatternStore {
|
|
174
|
+
if (!_store) _store = new ApprovedPatternStore();
|
|
175
|
+
return _store;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function resetApprovedPatternStore(): void {
|
|
179
|
+
_store = undefined;
|
|
180
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-harness/trajectory
|
|
3
|
+
*
|
|
4
|
+
* Trajectory capture for the write-review loop.
|
|
5
|
+
* Persists completed cycle records to `~/.pi-harness/trajectories/` for learning.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by Hermes Agent's closed learning loop.
|
|
8
|
+
* See: wiki/ROADMAP.md § M7
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
TrajectoryStore,
|
|
13
|
+
getTrajectoryStore,
|
|
14
|
+
} from "./store.js";
|
|
15
|
+
export {
|
|
16
|
+
ApprovedPatternStore,
|
|
17
|
+
getApprovedPatternStore,
|
|
18
|
+
resetApprovedPatternStore,
|
|
19
|
+
} from "./approved-patterns.js";
|
|
20
|
+
export type {
|
|
21
|
+
Trajectory,
|
|
22
|
+
TrajectorySummary,
|
|
23
|
+
TrajectoryStats,
|
|
24
|
+
TrajectoryClassification,
|
|
25
|
+
TrajectoryLabel,
|
|
26
|
+
} from "./types.js";
|