pi-harness-runtime 1.1.95 → 1.1.97

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.
Files changed (226) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/harness/loop-runtime.ts +3 -0
  3. package/index.ts +130 -29
  4. package/package.json +2 -1
  5. package/packages/a2a-adapter/package.json +1 -1
  6. package/packages/architecture-generator/package.json +1 -1
  7. package/packages/auth/package.json +1 -1
  8. package/packages/autonomous-refactor/package.json +1 -1
  9. package/packages/autonomous-runtime/package.json +1 -1
  10. package/packages/cache-strategy/package.json +1 -1
  11. package/packages/capability-registry/package.json +1 -1
  12. package/packages/checkpoint/package.json +1 -1
  13. package/packages/cli-plugin-sdk/package.json +1 -1
  14. package/packages/clipboard/package.json +1 -1
  15. package/packages/clipboard-plugin/package.json +1 -1
  16. package/packages/code-generation/package.json +1 -1
  17. package/packages/code-review/package.json +1 -1
  18. package/packages/codex-adapter/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/context-compiler/package.json +1 -1
  21. package/packages/context-discovery/package.json +1 -1
  22. package/packages/context-manager/package.json +1 -1
  23. package/packages/cookie-sanitizer/package.json +1 -1
  24. package/packages/cost-optimizer/package.json +1 -1
  25. package/packages/dependency-analyzer/package.json +1 -1
  26. package/packages/django-plugin/package.json +1 -1
  27. package/packages/doc-generator/package.json +1 -1
  28. package/packages/evaluation-engine/package.json +1 -1
  29. package/packages/evaluation-runner/package.json +1 -1
  30. package/packages/event-bus/package.json +1 -1
  31. package/packages/event-store/package.json +1 -1
  32. package/packages/experience-replay/package.json +1 -1
  33. package/packages/feedback-collector/package.json +1 -1
  34. package/packages/file-copy-helper/package.json +1 -1
  35. package/packages/framework-detector/package.json +1 -1
  36. package/packages/framework-plugin-sdk/package.json +1 -1
  37. package/packages/frappe-plugin/package.json +1 -1
  38. package/packages/generic-web-plugin/package.json +1 -1
  39. package/packages/health-monitor/package.json +1 -1
  40. package/packages/intent-analyzer/package.json +1 -1
  41. package/packages/jev-judge/package.json +1 -1
  42. package/packages/knowledge-graph/package.json +1 -1
  43. package/packages/knowledge-retrieval/package.json +1 -1
  44. package/packages/laravel-plugin/package.json +1 -1
  45. package/packages/learning-engine/package.json +1 -1
  46. package/packages/mcp-adapter/package.json +1 -1
  47. package/packages/memory-engine/package.json +1 -1
  48. package/packages/milestone-manager/package.json +1 -1
  49. package/packages/model-registry/package.json +1 -1
  50. package/packages/nextjs-plugin/package.json +1 -1
  51. package/packages/notification/package.json +1 -1
  52. package/packages/observability/package.json +1 -1
  53. package/packages/okf-indexer/package.json +1 -1
  54. package/packages/performance-optimizer/package.json +1 -1
  55. package/packages/privilege-broker/package.json +1 -1
  56. package/packages/project-analyzer/package.json +1 -1
  57. package/packages/project-bootstrap/package.json +1 -1
  58. package/packages/projection-engine/package.json +1 -1
  59. package/packages/prompt-compiler/package.json +1 -1
  60. package/packages/prompt-versioning/package.json +1 -1
  61. package/packages/provider-adapter-sdk/package.json +1 -1
  62. package/packages/provider-router/package.json +1 -1
  63. package/packages/provider-selector/package.json +1 -1
  64. package/packages/providers/package.json +1 -1
  65. package/packages/qdrant-skills/dist/client.d.ts +51 -0
  66. package/packages/qdrant-skills/dist/client.d.ts.map +1 -0
  67. package/packages/qdrant-skills/dist/client.js +105 -0
  68. package/packages/qdrant-skills/dist/client.js.map +1 -0
  69. package/packages/qdrant-skills/dist/embedder.d.ts +39 -0
  70. package/packages/qdrant-skills/dist/embedder.d.ts.map +1 -0
  71. package/packages/qdrant-skills/dist/embedder.js +101 -0
  72. package/packages/qdrant-skills/dist/embedder.js.map +1 -0
  73. package/packages/qdrant-skills/dist/index.d.ts +25 -0
  74. package/packages/qdrant-skills/dist/index.d.ts.map +1 -0
  75. package/packages/qdrant-skills/dist/index.js +25 -0
  76. package/packages/qdrant-skills/dist/index.js.map +1 -0
  77. package/packages/qdrant-skills/package.json +27 -0
  78. package/packages/qdrant-skills/src/client.ts +178 -0
  79. package/packages/qdrant-skills/src/embedder.ts +137 -0
  80. package/packages/qdrant-skills/src/index.ts +42 -0
  81. package/packages/quota-manager/package.json +1 -1
  82. package/packages/rate-limiter/package.json +1 -1
  83. package/packages/react-vite-plugin/package.json +1 -1
  84. package/packages/release-manager/package.json +1 -1
  85. package/packages/requirement-compiler/package.json +1 -1
  86. package/packages/runtime/package.json +1 -1
  87. package/packages/scheduler/package.json +1 -1
  88. package/packages/scheduler-adapter/package.json +1 -1
  89. package/packages/session/package.json +1 -1
  90. package/packages/session-api/package.json +1 -1
  91. package/packages/session-export/package.json +1 -1
  92. package/packages/shared-context/package.json +1 -1
  93. package/packages/skill-mcp-client/package.json +1 -1
  94. package/packages/skill-registry/package.json +1 -1
  95. package/packages/skills/dist/index.d.ts +40 -0
  96. package/packages/skills/dist/index.d.ts.map +1 -0
  97. package/packages/skills/dist/index.js +84 -0
  98. package/packages/skills/dist/index.js.map +1 -0
  99. package/packages/skills/dist/linter.d.ts +38 -0
  100. package/packages/skills/dist/linter.d.ts.map +1 -0
  101. package/packages/skills/dist/linter.js +323 -0
  102. package/packages/skills/dist/linter.js.map +1 -0
  103. package/packages/skills/dist/loader.d.ts +74 -0
  104. package/packages/skills/dist/loader.d.ts.map +1 -0
  105. package/packages/skills/dist/loader.js +280 -0
  106. package/packages/skills/dist/loader.js.map +1 -0
  107. package/packages/skills/dist/parser.d.ts +32 -0
  108. package/packages/skills/dist/parser.d.ts.map +1 -0
  109. package/packages/skills/dist/parser.js +459 -0
  110. package/packages/skills/dist/parser.js.map +1 -0
  111. package/packages/skills/dist/scanner.d.ts +33 -0
  112. package/packages/skills/dist/scanner.d.ts.map +1 -0
  113. package/packages/skills/dist/scanner.js +257 -0
  114. package/packages/skills/dist/scanner.js.map +1 -0
  115. package/packages/skills/dist/skill-commands.d.ts +33 -0
  116. package/packages/skills/dist/skill-commands.d.ts.map +1 -0
  117. package/packages/skills/dist/skill-commands.js +239 -0
  118. package/packages/skills/dist/skill-commands.js.map +1 -0
  119. package/packages/skills/dist/skill-tool.d.ts +120 -0
  120. package/packages/skills/dist/skill-tool.d.ts.map +1 -0
  121. package/packages/skills/dist/skill-tool.js +239 -0
  122. package/packages/skills/dist/skill-tool.js.map +1 -0
  123. package/packages/skills/dist/skill-versioning.d.ts +123 -0
  124. package/packages/skills/dist/skill-versioning.d.ts.map +1 -0
  125. package/packages/skills/dist/skill-versioning.js +240 -0
  126. package/packages/skills/dist/skill-versioning.js.map +1 -0
  127. package/packages/skills/dist/types.d.ts +226 -0
  128. package/packages/skills/dist/types.d.ts.map +1 -0
  129. package/packages/skills/dist/types.js +21 -0
  130. package/packages/skills/dist/types.js.map +1 -0
  131. package/packages/skills/package.json +46 -0
  132. package/packages/skills/src/index.ts +95 -0
  133. package/packages/skills/src/linter.ts +371 -0
  134. package/packages/skills/src/loader.ts +335 -0
  135. package/packages/skills/src/parser.ts +548 -0
  136. package/packages/skills/src/scanner.ts +322 -0
  137. package/packages/skills/src/skill-commands.ts +318 -0
  138. package/packages/skills/src/skill-tool.ts +324 -0
  139. package/packages/skills/src/skill-versioning.ts +345 -0
  140. package/packages/skills/src/types.ts +259 -0
  141. package/packages/skills/test/init-skills.test.ts +118 -0
  142. package/packages/sprint-planner/package.json +1 -1
  143. package/packages/subscription-engine/package.json +1 -1
  144. package/packages/task-compiler/package.json +1 -1
  145. package/packages/tencentdb-memory/package.json +1 -1
  146. package/packages/tencentdb-sync/package.json +1 -1
  147. package/packages/test-data-generator/package.json +1 -1
  148. package/packages/test-generator/package.json +1 -1
  149. package/packages/todo-bd-sync/package.json +1 -1
  150. package/packages/token-estimation/package.json +1 -1
  151. package/packages/token-optimizer/package.json +1 -1
  152. package/packages/trajectory/dist/src/curator.d.ts +189 -0
  153. package/packages/trajectory/dist/src/curator.d.ts.map +1 -0
  154. package/packages/trajectory/dist/src/curator.js +340 -0
  155. package/packages/trajectory/dist/src/curator.js.map +1 -0
  156. package/packages/trajectory/dist/src/journey-renderer.d.ts +53 -0
  157. package/packages/trajectory/dist/src/journey-renderer.d.ts.map +1 -0
  158. package/packages/trajectory/dist/src/journey-renderer.js +324 -0
  159. package/packages/trajectory/dist/src/journey-renderer.js.map +1 -0
  160. package/packages/trajectory/dist/src/learning-graph.d.ts +159 -0
  161. package/packages/trajectory/dist/src/learning-graph.d.ts.map +1 -0
  162. package/packages/trajectory/dist/src/learning-graph.js +258 -0
  163. package/packages/trajectory/dist/src/learning-graph.js.map +1 -0
  164. package/packages/trajectory/dist/src/memory-store.d.ts +146 -0
  165. package/packages/trajectory/dist/src/memory-store.d.ts.map +1 -0
  166. package/packages/trajectory/dist/src/memory-store.js +472 -0
  167. package/packages/trajectory/dist/src/memory-store.js.map +1 -0
  168. package/packages/trajectory/dist/src/review-fork.d.ts +164 -0
  169. package/packages/trajectory/dist/src/review-fork.d.ts.map +1 -0
  170. package/packages/trajectory/dist/src/review-fork.js +332 -0
  171. package/packages/trajectory/dist/src/review-fork.js.map +1 -0
  172. package/packages/trajectory/dist/src/write-approval.d.ts +136 -0
  173. package/packages/trajectory/dist/src/write-approval.d.ts.map +1 -0
  174. package/packages/trajectory/dist/src/write-approval.js +281 -0
  175. package/packages/trajectory/dist/src/write-approval.js.map +1 -0
  176. package/packages/trajectory/package.json +1 -1
  177. package/packages/trajectory/src/curator.ts +485 -0
  178. package/packages/trajectory/src/journey-renderer.ts +399 -0
  179. package/packages/trajectory/src/learning-graph.ts +363 -0
  180. package/packages/trajectory/src/memory-store.ts +578 -0
  181. package/packages/trajectory/src/review-fork.ts +450 -0
  182. package/packages/trajectory/src/write-approval.ts +372 -0
  183. package/packages/tui/package.json +1 -1
  184. package/packages/types/package.json +1 -1
  185. package/packages/workflow-events/package.json +1 -1
  186. package/packages/workspace-scanner/package.json +1 -1
  187. package/packages/worktree/package.json +1 -1
  188. package/packages/write-review/dist/blackboard.d.ts +98 -0
  189. package/packages/write-review/dist/blackboard.d.ts.map +1 -0
  190. package/packages/write-review/dist/blackboard.js +289 -0
  191. package/packages/write-review/dist/blackboard.js.map +1 -0
  192. package/packages/write-review/dist/event-bus.d.ts +106 -0
  193. package/packages/write-review/dist/event-bus.d.ts.map +1 -0
  194. package/packages/write-review/dist/event-bus.js +294 -0
  195. package/packages/write-review/dist/event-bus.js.map +1 -0
  196. package/packages/write-review/dist/gate.d.ts +37 -0
  197. package/packages/write-review/dist/gate.d.ts.map +1 -0
  198. package/packages/write-review/dist/gate.js +138 -0
  199. package/packages/write-review/dist/gate.js.map +1 -0
  200. package/packages/write-review/dist/index.d.ts +22 -0
  201. package/packages/write-review/dist/index.d.ts.map +1 -0
  202. package/packages/write-review/dist/index.js +60 -0
  203. package/packages/write-review/dist/index.js.map +1 -0
  204. package/packages/write-review/dist/injection.d.ts +40 -0
  205. package/packages/write-review/dist/injection.d.ts.map +1 -0
  206. package/packages/write-review/dist/injection.js +179 -0
  207. package/packages/write-review/dist/injection.js.map +1 -0
  208. package/packages/write-review/dist/review.d.ts +54 -0
  209. package/packages/write-review/dist/review.d.ts.map +1 -0
  210. package/packages/write-review/dist/review.js +154 -0
  211. package/packages/write-review/dist/review.js.map +1 -0
  212. package/packages/write-review/dist/trigger.d.ts +39 -0
  213. package/packages/write-review/dist/trigger.d.ts.map +1 -0
  214. package/packages/write-review/dist/trigger.js +107 -0
  215. package/packages/write-review/dist/trigger.js.map +1 -0
  216. package/packages/write-review/dist/types.d.ts +78 -0
  217. package/packages/write-review/dist/types.d.ts.map +1 -0
  218. package/packages/write-review/dist/types.js +26 -0
  219. package/packages/write-review/dist/types.js.map +1 -0
  220. package/packages/write-review/package.json +1 -1
  221. package/packages/write-review/src/event-bus.ts +351 -0
  222. package/packages/write-review/src/index.ts +1 -0
  223. package/packages/write-review/src/injection.ts +25 -3
  224. package/proactive-compact.ts +51 -7
  225. package/scripts/sync-skills.ts +275 -0
  226. package/scripts/sync-to-qdrant.ts +398 -0
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Write Approval Gate
3
+ *
4
+ * Stage writes (memory, skills) for human review before they land.
5
+ * Based on Hermes Agent's consent-aware learning loop.
6
+ */
7
+
8
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
9
+ import { join, dirname } from "node:path";
10
+ import { homedir } from "node:os";
11
+
12
+ /**
13
+ * Pending write types
14
+ */
15
+ export type PendingWriteType =
16
+ | "memory_add"
17
+ | "memory_replace"
18
+ | "memory_remove"
19
+ | "skill_create"
20
+ | "skill_patch"
21
+ | "skill_delete"
22
+ | "skill_file_write"
23
+ | "skill_file_remove"
24
+ | "user_profile_add";
25
+
26
+ /**
27
+ * Pending write action
28
+ */
29
+ export interface PendingWriteAction {
30
+ type: PendingWriteType;
31
+ target: string; // memory entry id, skill id, etc.
32
+ content?: string; // new content for add/replace
33
+ oldContent?: string; // old content for replace/remove
34
+ metadata?: Record<string, unknown>;
35
+ }
36
+
37
+ /**
38
+ * Pending write record
39
+ */
40
+ export interface PendingWrite {
41
+ id: string;
42
+ action: PendingWriteAction;
43
+ author: "agent" | "user" | "curator";
44
+ timestamp: number;
45
+ summary: string; // One-line summary for display
46
+ details?: string; // Full diff or content for review
47
+ }
48
+
49
+ /**
50
+ * Write approval configuration
51
+ */
52
+ export interface WriteApprovalConfig {
53
+ /** Enable write approval */
54
+ enabled: boolean;
55
+ /** Directory to store pending writes */
56
+ pendingDir: string;
57
+ /** Max pending writes before warning */
58
+ maxPending: number;
59
+ }
60
+
61
+ /**
62
+ * Default configuration
63
+ */
64
+ export const DEFAULT_APPROVAL_CONFIG: WriteApprovalConfig = {
65
+ enabled: false,
66
+ pendingDir: "~/.pi-harness/pending",
67
+ maxPending: 50,
68
+ };
69
+
70
+ /**
71
+ * Expand path with ~
72
+ */
73
+ function expandPath(path: string): string {
74
+ if (path.startsWith("~/")) {
75
+ return join(homedir(), path.slice(2));
76
+ }
77
+ return path;
78
+ }
79
+
80
+ /**
81
+ * Write Approval Gate
82
+ */
83
+ export class WriteApprovalGate {
84
+ private config: WriteApprovalConfig;
85
+ private pending: PendingWrite[] = [];
86
+
87
+ constructor(config?: Partial<WriteApprovalConfig>) {
88
+ this.config = { ...DEFAULT_APPROVAL_CONFIG, ...config };
89
+ this.config.pendingDir = expandPath(this.config.pendingDir);
90
+ this.load();
91
+ }
92
+
93
+ /**
94
+ * Load pending writes from disk
95
+ */
96
+ private load(): void {
97
+ const pendingFile = join(this.config.pendingDir, "writes.json");
98
+
99
+ if (!existsSync(pendingFile)) {
100
+ this.pending = [];
101
+ return;
102
+ }
103
+
104
+ try {
105
+ const content = readFileSync(pendingFile, "utf-8");
106
+ this.pending = JSON.parse(content);
107
+ } catch (err) {
108
+ console.error("[WriteApprovalGate] Failed to load:", err);
109
+ this.pending = [];
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Save pending writes to disk
115
+ */
116
+ private save(): void {
117
+ mkdirSync(this.config.pendingDir, { recursive: true });
118
+ const pendingFile = join(this.config.pendingDir, "writes.json");
119
+ writeFileSync(pendingFile, JSON.stringify(this.pending, null, 2), "utf-8");
120
+ }
121
+
122
+ /**
123
+ * Stage a write for approval
124
+ */
125
+ stage(action: PendingWriteAction, author: "agent" | "user" | "curator"): PendingWrite {
126
+ // Generate ID
127
+ const id = `pw_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
128
+
129
+ // Generate summary
130
+ const summary = this.generateSummary(action);
131
+
132
+ // Generate details (diff)
133
+ const details = this.generateDetails(action);
134
+
135
+ const pending: PendingWrite = {
136
+ id,
137
+ action,
138
+ author,
139
+ timestamp: Date.now(),
140
+ summary,
141
+ details,
142
+ };
143
+
144
+ this.pending.push(pending);
145
+ this.save();
146
+
147
+ return pending;
148
+ }
149
+
150
+ /**
151
+ * Generate one-line summary for action
152
+ */
153
+ private generateSummary(action: PendingWriteAction): string {
154
+ switch (action.type) {
155
+ case "memory_add":
156
+ return `Add memory: "${action.content?.slice(0, 50)}..."`;
157
+ case "memory_replace":
158
+ return `Replace memory`;
159
+ case "memory_remove":
160
+ return `Remove memory`;
161
+ case "skill_create":
162
+ return `Create skill: ${action.target}`;
163
+ case "skill_patch":
164
+ return `Patch skill: ${action.target}`;
165
+ case "skill_delete":
166
+ return `Delete skill: ${action.target}`;
167
+ case "skill_file_write":
168
+ return `Write file to skill ${action.target}: ${action.metadata?.file}`;
169
+ case "skill_file_remove":
170
+ return `Remove file from skill ${action.target}: ${action.metadata?.file}`;
171
+ case "user_profile_add":
172
+ return `Add user preference: "${action.content?.slice(0, 50)}..."`;
173
+ default:
174
+ return `Unknown action: ${action.type}`;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Generate details (diff) for action
180
+ */
181
+ private generateDetails(action: PendingWriteAction): string | undefined {
182
+ switch (action.type) {
183
+ case "memory_add":
184
+ case "user_profile_add":
185
+ return action.content;
186
+ case "memory_replace":
187
+ return `OLD:\n${action.oldContent}\n\nNEW:\n${action.content}`;
188
+ case "skill_create":
189
+ case "skill_patch":
190
+ return action.content;
191
+ default:
192
+ return undefined;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Approve a pending write
198
+ */
199
+ approve(writeId: string): PendingWrite | null {
200
+ const index = this.pending.findIndex((w) => w.id === writeId);
201
+ if (index < 0) return null;
202
+
203
+ const write = this.pending[index];
204
+ this.pending.splice(index, 1);
205
+ this.save();
206
+
207
+ return write;
208
+ }
209
+
210
+ /**
211
+ * Approve all pending writes
212
+ */
213
+ approveAll(): PendingWrite[] {
214
+ const approved = [...this.pending];
215
+ this.pending = [];
216
+ this.save();
217
+ return approved;
218
+ }
219
+
220
+ /**
221
+ * Reject a pending write
222
+ */
223
+ reject(writeId: string): PendingWrite | null {
224
+ const index = this.pending.findIndex((w) => w.id === writeId);
225
+ if (index < 0) return null;
226
+
227
+ const write = this.pending[index];
228
+ this.pending.splice(index, 1);
229
+ this.save();
230
+
231
+ return write;
232
+ }
233
+
234
+ /**
235
+ * Reject all pending writes
236
+ */
237
+ rejectAll(): PendingWrite[] {
238
+ const rejected = [...this.pending];
239
+ this.pending = [];
240
+ this.save();
241
+ return rejected;
242
+ }
243
+
244
+ /**
245
+ * Get all pending writes
246
+ */
247
+ list(): PendingWrite[] {
248
+ return [...this.pending];
249
+ }
250
+
251
+ /**
252
+ * Get pending writes by type
253
+ */
254
+ listByType(type: PendingWriteType): PendingWrite[] {
255
+ return this.pending.filter((w) => w.action.type === type);
256
+ }
257
+
258
+ /**
259
+ * Get pending writes by author
260
+ */
261
+ listByAuthor(author: "agent" | "user" | "curator"): PendingWrite[] {
262
+ return this.pending.filter((w) => w.author === author);
263
+ }
264
+
265
+ /**
266
+ * Get pending count
267
+ */
268
+ count(): number {
269
+ return this.pending.length;
270
+ }
271
+
272
+ /**
273
+ * Check if approval is required
274
+ */
275
+ requiresApproval(): boolean {
276
+ return this.config.enabled;
277
+ }
278
+
279
+ /**
280
+ * Update configuration
281
+ */
282
+ updateConfig(config: Partial<WriteApprovalConfig>): void {
283
+ this.config = { ...this.config, ...config };
284
+ if (config.pendingDir) {
285
+ this.config.pendingDir = expandPath(config.pendingDir);
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Get configuration
291
+ */
292
+ getConfig(): WriteApprovalConfig {
293
+ return { ...this.config };
294
+ }
295
+
296
+ /**
297
+ * Format pending writes for display
298
+ */
299
+ formatList(): string {
300
+ if (this.pending.length === 0) {
301
+ return "No pending writes.";
302
+ }
303
+
304
+ const lines: string[] = [];
305
+ for (const write of this.pending) {
306
+ const age = this.getAgeString(write.timestamp);
307
+ lines.push(`[${write.id}] ${age} ${write.author}: ${write.summary}`);
308
+ }
309
+
310
+ return lines.join("\n");
311
+ }
312
+
313
+ /**
314
+ * Get age string for timestamp
315
+ */
316
+ private getAgeString(timestamp: number): string {
317
+ const age = Date.now() - timestamp;
318
+ const minutes = Math.floor(age / 60000);
319
+
320
+ if (minutes < 1) return "<1m";
321
+ if (minutes < 60) return `${minutes}m`;
322
+
323
+ const hours = Math.floor(minutes / 60);
324
+ if (hours < 24) return `${hours}h`;
325
+
326
+ const days = Math.floor(hours / 24);
327
+ return `${days}d`;
328
+ }
329
+ }
330
+
331
+ // Global instance
332
+ let globalGate: WriteApprovalGate | null = null;
333
+
334
+ export function getGlobalApprovalGate(): WriteApprovalGate {
335
+ if (!globalGate) {
336
+ globalGate = new WriteApprovalGate();
337
+ }
338
+ return globalGate;
339
+ }
340
+
341
+ export function createApprovalGate(config?: Partial<WriteApprovalConfig>): WriteApprovalGate {
342
+ return new WriteApprovalGate(config);
343
+ }
344
+
345
+ /**
346
+ * Helper to stage write with auto-approval if disabled
347
+ */
348
+ export function stageWrite(
349
+ action: PendingWriteAction,
350
+ author: "agent" | "user" | "curator",
351
+ autoApprove: boolean = false
352
+ ): { staged: boolean; write?: PendingWrite } {
353
+ const gate = getGlobalApprovalGate();
354
+
355
+ if (!gate.requiresApproval() || autoApprove) {
356
+ // Return action directly for execution
357
+ return {
358
+ staged: false,
359
+ write: {
360
+ id: "auto",
361
+ action,
362
+ author,
363
+ timestamp: Date.now(),
364
+ summary: `Auto-approved: ${action.type}`,
365
+ },
366
+ };
367
+ }
368
+
369
+ // Stage for approval
370
+ const write = gate.stage(action, author);
371
+ return { staged: true, write };
372
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tui",
3
- "version": "1.1.95",
3
+ "version": "1.1.97",
4
4
  "description": "pi-harness-runtime — tui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/types",
3
- "version": "1.1.95",
3
+ "version": "1.1.97",
4
4
  "description": "Shared TypeScript types for pi-harness-runtime packages",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workflow-events",
3
- "version": "1.1.95",
3
+ "version": "1.1.97",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workspace-scanner",
3
- "version": "1.1.95",
3
+ "version": "1.1.97",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/worktree",
3
- "version": "1.1.95",
3
+ "version": "1.1.97",
4
4
  "description": "pi-harness-runtime — worktree",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Write-Review Blackboard
3
+ *
4
+ * File-based coordination between writer and reviewer agents.
5
+ * Location: {project}/.write-review/status.json
6
+ */
7
+ import type { WriteReviewStatus, ReviewPhase, Verdict, PingPongDecisionSnapshot } from "./types.js";
8
+ export declare class WriteReviewBlackboard {
9
+ private readonly projectPath;
10
+ private readonly dir;
11
+ private status;
12
+ constructor(projectPath: string, dir?: string);
13
+ /**
14
+ * Initialize a new write-review session
15
+ */
16
+ init(): void;
17
+ /**
18
+ * Load status from disk
19
+ */
20
+ load(): WriteReviewStatus | null;
21
+ /**
22
+ * Save status to disk
23
+ */
24
+ save(): void;
25
+ /**
26
+ * Get current status
27
+ */
28
+ getStatus(): WriteReviewStatus | null;
29
+ /**
30
+ * Record the automatic decision that selected or skipped PING-PONG.
31
+ * This is the durable middleware state shared by separate agent terminals.
32
+ */
33
+ recordPingPongDecision(snapshot: PingPongDecisionSnapshot): void;
34
+ /**
35
+ * Start a new write session
36
+ */
37
+ startWriting(): void;
38
+ /**
39
+ * Mark writer as done
40
+ */
41
+ writerDone(message?: string): void;
42
+ /**
43
+ * Start reviewing
44
+ */
45
+ startReview(): void;
46
+ /**
47
+ * Record verdict
48
+ */
49
+ setVerdict(verdict: Verdict, message?: string): void;
50
+ /**
51
+ * Record code files written
52
+ */
53
+ setCodeFiles(files: string[]): void;
54
+ /**
55
+ * Record requested changes
56
+ */
57
+ setChangesRequested(changes: string[]): void;
58
+ /**
59
+ * Get current phase
60
+ */
61
+ getPhase(): ReviewPhase;
62
+ /**
63
+ * Get iteration number
64
+ */
65
+ getIteration(): number;
66
+ /**
67
+ * Check if build is allowed
68
+ */
69
+ canBuild(): boolean;
70
+ /**
71
+ * Get blackboard directory path
72
+ */
73
+ getPath(): string;
74
+ /**
75
+ * Reset to idle
76
+ */
77
+ reset(): void;
78
+ /**
79
+ * Check if a review session exists for this project
80
+ */
81
+ exists(): boolean;
82
+ /**
83
+ * Export status as markdown for display.
84
+ * Injected into every agent prompt so each agent sees the shared scoreboard.
85
+ * Matches the pi-lens pattern: persistent artifact encountered naturally.
86
+ */
87
+ toMarkdown(): string;
88
+ /**
89
+ * Extract file paths from a fenced code block string.
90
+ * Looks for common patterns: ```path/to/file
91
+ */
92
+ extractFilePaths(code: string): string[];
93
+ }
94
+ /**
95
+ * Create or get blackboard for project
96
+ */
97
+ export declare function createBlackboard(projectPath: string, dir?: string): WriteReviewBlackboard;
98
+ //# sourceMappingURL=blackboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blackboard.d.ts","sourceRoot":"","sources":["../src/blackboard.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACX,iBAAiB,EACjB,WAAW,EACX,OAAO,EACP,wBAAwB,EACxB,MAAM,YAAY,CAAC;AAKpB,qBAAa,qBAAqB;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAkC;IAEhD,YAAY,WAAW,EAAE,MAAM,EAAE,GAAG,GAAE,MAAoB,EAGzD;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,CAWX;IAED;;OAEG;IACH,IAAI,IAAI,iBAAiB,GAAG,IAAI,CAW/B;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,CAMX;IAED;;OAEG;IACH,SAAS,IAAI,iBAAiB,GAAG,IAAI,CAEpC;IAED;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAY/D;IAED;;OAEG;IACH,YAAY,IAAI,IAAI,CAMnB;IAED;;OAEG;IACH,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAMjC;IAED;;OAEG;IACH,WAAW,IAAI,IAAI,CAKlB;IAED;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAmBnD;IAED;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAIlC;IAED;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAI3C;IAED;;OAEG;IACH,QAAQ,IAAI,WAAW,CAEtB;IAED;;OAEG;IACH,YAAY,IAAI,MAAM,CAErB;IAED;;OAEG;IACH,QAAQ,IAAI,OAAO,CAElB;IAED;;OAEG;IACH,OAAO,IAAI,MAAM,CAEhB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI,CAYZ;IAED;;OAEG;IACH,MAAM,IAAI,OAAO,CAGhB;IAED;;;;OAIG;IACH,UAAU,IAAI,MAAM,CAkDnB;IAED;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAuBvC;CACD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC/B,WAAW,EAAE,MAAM,EACnB,GAAG,CAAC,EAAE,MAAM,GACV,qBAAqB,CAQvB"}