pi-harness-runtime 1.1.255 → 1.1.257

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 (107) hide show
  1. package/index.ts +10 -2
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/codex-watcher/test/watch.test.ts +303 -0
  20. package/packages/config-capture/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/cookie-sanitizer/package.json +1 -1
  25. package/packages/cost-optimizer/package.json +1 -1
  26. package/packages/dependency-analyzer/package.json +1 -1
  27. package/packages/django-plugin/package.json +1 -1
  28. package/packages/doc-generator/package.json +1 -1
  29. package/packages/e2e-tester/package.json +1 -1
  30. package/packages/evaluation-engine/package.json +1 -1
  31. package/packages/evaluation-runner/package.json +1 -1
  32. package/packages/event-bus/package.json +1 -1
  33. package/packages/event-store/package.json +1 -1
  34. package/packages/experience-replay/package.json +1 -1
  35. package/packages/feedback-collector/package.json +1 -1
  36. package/packages/file-copy-helper/package.json +1 -1
  37. package/packages/framework-detector/package.json +1 -1
  38. package/packages/framework-plugin-sdk/package.json +1 -1
  39. package/packages/frappe-plugin/package.json +1 -1
  40. package/packages/generic-web-plugin/package.json +1 -1
  41. package/packages/health-monitor/package.json +1 -1
  42. package/packages/intent-analyzer/package.json +1 -1
  43. package/packages/jev-judge/package.json +1 -1
  44. package/packages/knowledge-graph/package.json +1 -1
  45. package/packages/knowledge-retrieval/package.json +1 -1
  46. package/packages/laravel-plugin/package.json +1 -1
  47. package/packages/learning-engine/package.json +1 -1
  48. package/packages/mcp-adapter/package.json +1 -1
  49. package/packages/memory-engine/package.json +1 -1
  50. package/packages/milestone-manager/package.json +1 -1
  51. package/packages/model-registry/package.json +1 -1
  52. package/packages/nextjs-plugin/package.json +1 -1
  53. package/packages/notification/dist/adapters/telegram-adapter.d.ts +11 -0
  54. package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
  55. package/packages/notification/dist/adapters/telegram-adapter.js +89 -5
  56. package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
  57. package/packages/notification/package.json +1 -1
  58. package/packages/notification/src/adapters/telegram-adapter.ts +81 -5
  59. package/packages/observability/package.json +1 -1
  60. package/packages/okf-indexer/package.json +1 -1
  61. package/packages/paperclip-integration/package.json +1 -1
  62. package/packages/performance-optimizer/package.json +1 -1
  63. package/packages/privilege-broker/package.json +1 -1
  64. package/packages/project-analyzer/package.json +1 -1
  65. package/packages/project-bootstrap/package.json +1 -1
  66. package/packages/projection-engine/package.json +1 -1
  67. package/packages/prompt-compiler/package.json +1 -1
  68. package/packages/prompt-versioning/package.json +1 -1
  69. package/packages/provider-adapter-sdk/package.json +1 -1
  70. package/packages/provider-router/package.json +1 -1
  71. package/packages/provider-selector/package.json +1 -1
  72. package/packages/providers/package.json +1 -1
  73. package/packages/qdrant-skills/package.json +1 -1
  74. package/packages/quota-manager/package.json +1 -1
  75. package/packages/rate-limiter/package.json +1 -1
  76. package/packages/react-vite-plugin/package.json +1 -1
  77. package/packages/release-manager/package.json +1 -1
  78. package/packages/requirement-compiler/package.json +1 -1
  79. package/packages/runtime/package.json +1 -1
  80. package/packages/scheduler/package.json +1 -1
  81. package/packages/scheduler-adapter/package.json +1 -1
  82. package/packages/session/package.json +1 -1
  83. package/packages/session-api/package.json +1 -1
  84. package/packages/session-export/package.json +1 -1
  85. package/packages/shared-context/package.json +1 -1
  86. package/packages/skill-mcp-client/package.json +1 -1
  87. package/packages/skill-registry/package.json +1 -1
  88. package/packages/skills/package.json +1 -1
  89. package/packages/sprint-planner/package.json +1 -1
  90. package/packages/subscription-engine/package.json +1 -1
  91. package/packages/task-compiler/package.json +1 -1
  92. package/packages/telegram/package.json +1 -1
  93. package/packages/tencentdb-memory/package.json +1 -1
  94. package/packages/tencentdb-sync/package.json +1 -1
  95. package/packages/test-data-generator/package.json +1 -1
  96. package/packages/test-generator/package.json +1 -1
  97. package/packages/todo-bd-sync/package.json +1 -1
  98. package/packages/token-estimation/package.json +1 -1
  99. package/packages/token-optimizer/package.json +1 -1
  100. package/packages/trajectory/package.json +1 -1
  101. package/packages/tui/package.json +1 -1
  102. package/packages/types/package.json +1 -1
  103. package/packages/webmcp-tester/package.json +1 -1
  104. package/packages/workflow-events/package.json +1 -1
  105. package/packages/workspace-scanner/package.json +1 -1
  106. package/packages/worktree/package.json +1 -1
  107. package/packages/write-review/package.json +1 -1
package/index.ts CHANGED
@@ -946,7 +946,12 @@ async function initTelegram(pi: ExtensionAPI): Promise<void> {
946
946
 
947
947
  // Log the actual bot username (fetched from Telegram API via getMe)
948
948
  const botUsername = center.getTelegramBotUsername();
949
- const greeting = await center.getTelegramAdapter()?.sendStartupGreeting();
949
+ const telegram = center.getTelegramAdapter();
950
+ telegram?.setGreetingVerifiedHandler(() => {
951
+ addHarnessMessage("Telegram", "ready (Hi reply received; two-way verified)", "ok");
952
+ });
953
+ pi.on("session_shutdown", () => telegram?.stopPolling());
954
+ const greeting = await telegram?.sendStartupGreeting();
950
955
  if (!greeting?.success) {
951
956
  logStartup("[pi-harness] Telegram: startup greeting delivery failed");
952
957
  addHarnessMessage("Telegram", "bot verified; Hi delivery failed", "err", [
@@ -955,7 +960,10 @@ async function initTelegram(pi: ExtensionAPI): Promise<void> {
955
960
  ]);
956
961
  return;
957
962
  }
958
- addHarnessMessage("Telegram", "ready (Hi sent to your chat)", "ok");
963
+ telegram?.startPolling();
964
+ if (!telegram?.isGreetingVerified()) {
965
+ addHarnessMessage("Telegram", "Hi sent; tap Say Hi back to verify inbound", "wait");
966
+ }
959
967
  if (botUsername) {
960
968
  logStartup(`[pi-harness] Telegram (@${botUsername}) ready — DM the bot to receive alerts`);
961
969
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "[BETA] Codex-style /usage status + autonomous coding harness for pi. Not production ready — expect breaking changes.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/a2a-adapter",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "description": "Google Agent-to-Agent (A2A) protocol adapter for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/agent-policy",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Agent policy runtime for pi-harness - enforces mandatory rule delivery and mutation gating",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/architecture-generator",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Generate ADR and Mermaid diagrams from project analysis",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/auth",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "pi-harness-runtime — auth",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-refactor",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "description": "Autonomous refactoring engine",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-runtime",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Autonomous Operations Runtime — task inbox, lease management, and worker lifecycle (RFC-0101)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cache-strategy",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "description": "Smart caching with TTL and invalidation strategies (RFC-0022)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/capability-registry",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/checkpoint",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Enhanced checkpoint system with incremental snapshots, diff-based storage, and recovery strategies",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cli-plugin-sdk",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "description": "CLI Plugin SDK for pi-harness — install, list, remove, update, invoke plugins",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard-plugin",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/code-generation",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Code generation pipeline for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/code-review",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Automated code review engine for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/codex-adapter",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "description": "OpenAI Codex provider adapter for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/codex-watcher",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Watches Codex CLI sessions and emits visible messages for PING-PONG monitoring",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -148,3 +148,306 @@ describe("classifyMessage", () => {
148
148
  expect(classifyMessage(" ")).toBe("unknown");
149
149
  });
150
150
  });
151
+
152
+ // -----------------------------------------------------------------------
153
+ // UTF-8 Byte Buffering (simulated)
154
+ // -----------------------------------------------------------------------
155
+
156
+ describe("UTF-8 byte buffering", () => {
157
+ /**
158
+ * Simulates the byte buffering logic from index.ts
159
+ * This tests the core algorithm without needing actual file I/O
160
+ */
161
+ function simulateByteBuffering(chunks: Buffer[], newlineTerminated: boolean[]): {
162
+ completeLines: string[];
163
+ remainingBytes: Buffer | null;
164
+ } {
165
+ let pendingBytes: Buffer | null = null;
166
+
167
+ for (let i = 0; i < chunks.length; i++) {
168
+ const chunk = chunks[i];
169
+
170
+ // Buffer raw bytes
171
+ if (pendingBytes) {
172
+ pendingBytes = Buffer.concat([pendingBytes, chunk]);
173
+ } else {
174
+ pendingBytes = chunk;
175
+ }
176
+
177
+ // If this chunk is newline-terminated, we can decode
178
+ if (newlineTerminated[i]) {
179
+ const text = pendingBytes.toString("utf8");
180
+ pendingBytes = null;
181
+ return {
182
+ completeLines: text.split("\n").filter(l => l.trim()),
183
+ remainingBytes: pendingBytes,
184
+ };
185
+ }
186
+ }
187
+
188
+ return {
189
+ completeLines: [],
190
+ remainingBytes: pendingBytes,
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Stateful simulation that properly chains multiple reads
196
+ */
197
+ function createBufferingState() {
198
+ let pendingBytes: Buffer | null = null;
199
+ let linesEmitted: string[] = [];
200
+
201
+ function processChunk(chunk: Buffer, endsWithNewline: boolean): string[] {
202
+ // Buffer raw bytes
203
+ if (pendingBytes) {
204
+ pendingBytes = Buffer.concat([pendingBytes, chunk]);
205
+ } else {
206
+ pendingBytes = chunk;
207
+ }
208
+
209
+ if (endsWithNewline) {
210
+ const text = pendingBytes.toString("utf8");
211
+ const lines = text.split("\n").filter(l => l.trim());
212
+ linesEmitted.push(...lines);
213
+ pendingBytes = null;
214
+ return lines;
215
+ }
216
+ return [];
217
+ }
218
+
219
+ return { processChunk, getPending: () => pendingBytes, getEmitted: () => linesEmitted };
220
+ }
221
+
222
+ it("preserves split multi-byte UTF-8 characters", () => {
223
+ // 中文 is 3 bytes per character: \u4E2D\u6587
224
+ // If we split between bytes, each chunk should still decode correctly
225
+ const fullText = '{"text": "Hello 中文 world"}\n';
226
+ const fullBuffer = Buffer.from(fullText, "utf8");
227
+
228
+ // Split in the middle of 中文 (after 2nd byte of first char)
229
+ const chineseStart = fullText.indexOf("中文");
230
+ const splitPoint = chineseStart + 2; // Split in middle of first Chinese char
231
+
232
+ const chunk1 = fullBuffer.subarray(0, splitPoint);
233
+ const chunk2 = fullBuffer.subarray(splitPoint);
234
+
235
+ // Use stateful buffering to properly chain reads
236
+ const state = createBufferingState();
237
+
238
+ // Process chunk 1 (not newline terminated)
239
+ const lines1 = state.processChunk(chunk1, false);
240
+ expect(lines1).toHaveLength(0); // No complete lines yet
241
+ expect(state.getPending()).not.toBeNull();
242
+
243
+ // Process chunk 2 (newline terminated)
244
+ const lines2 = state.processChunk(chunk2, true);
245
+ expect(lines2).toHaveLength(1);
246
+ // The full text including Chinese should be preserved
247
+ expect(lines2[0]).toContain("Hello");
248
+ expect(lines2[0]).toContain("中文");
249
+ expect(lines2[0]).toContain("world");
250
+ });
251
+
252
+ it("handles chunk split in middle of 3-byte UTF-8 sequence", () => {
253
+ // Test specifically the bug case: ���文 → proper 中文
254
+ const fullText = '{"text": "test 日本語"}\n';
255
+ const fullBuffer = Buffer.from(fullText, "utf8");
256
+
257
+ // 日 = E6 97 A5 (3 bytes), 本 = E6 9C AC (3 bytes), 語 = E8 87 AA (3 bytes)
258
+ // Split after byte 10 (somewhere in 日本語)
259
+ const splitPoint = 10;
260
+
261
+ const chunk1 = fullBuffer.subarray(0, splitPoint);
262
+ const chunk2 = fullBuffer.subarray(splitPoint);
263
+
264
+ const result1 = simulateByteBuffering([chunk1], [false]);
265
+ expect(result1.completeLines).toHaveLength(0);
266
+
267
+ const result2 = simulateByteBuffering([chunk2], [true]);
268
+ expect(result2.completeLines).toHaveLength(1);
269
+ // Should NOT have replacement character (U+FFFD)
270
+ expect(result2.completeLines[0]).not.toContain("\uFFFD");
271
+ expect(result2.completeLines[0]).toContain("日本語");
272
+ });
273
+ });
274
+
275
+ // ---------------------------------------------------------------------------
276
+ // Partial JSON Line Preservation
277
+ // ---------------------------------------------------------------------------
278
+
279
+ describe("Partial JSON line preservation", () => {
280
+ function simulatePartialLineBuffering(readResults: {text: string; endsWithNewline: boolean}[]): {
281
+ completeLines: string[];
282
+ pendingText: string;
283
+ } {
284
+ let pendingText = "";
285
+
286
+ for (const read of readResults) {
287
+ const fullText = pendingText + read.text;
288
+ const lines = fullText.split("\n");
289
+
290
+ if (read.endsWithNewline) {
291
+ // All lines complete
292
+ pendingText = "";
293
+ return {
294
+ completeLines: lines.filter(l => l.trim()),
295
+ pendingText,
296
+ };
297
+ } else {
298
+ // Last line is incomplete
299
+ pendingText = lines.pop() ?? "";
300
+ }
301
+ }
302
+
303
+ return { completeLines: [], pendingText };
304
+ }
305
+
306
+ it("waits for complete line before emitting", () => {
307
+ // Simulate: partial JSON written, then newline, then next record
308
+ const read1 = { text: '{"ordinal": 1, "text": "part', endsWithNewline: false };
309
+ const read2 = { text: 'ial record"}\n', endsWithNewline: true };
310
+
311
+ const result = simulatePartialLineBuffering([read1, read2]);
312
+
313
+ expect(result.completeLines).toHaveLength(1);
314
+ expect(result.completeLines[0]).toContain("partial record");
315
+ expect(result.completeLines[0]).toContain('"ordinal": 1');
316
+ });
317
+
318
+ it("preserves multiple incomplete reads", () => {
319
+ const read1 = { text: '{"ordinal": 1', endsWithNewline: false };
320
+ const read2 = { text: ', "text": "', endsWithNewline: false };
321
+ const read3 = { text: 'hello"}\n', endsWithNewline: true };
322
+
323
+ const result = simulatePartialLineBuffering([read1, read2, read3]);
324
+
325
+ expect(result.completeLines).toHaveLength(1);
326
+ expect(result.completeLines[0]).toContain("hello");
327
+ });
328
+
329
+ it("handles empty pending text correctly", () => {
330
+ const read = { text: '{"ordinal": 1, "text": "complete"}\n', endsWithNewline: true };
331
+
332
+ const result = simulatePartialLineBuffering([read]);
333
+
334
+ expect(result.completeLines).toHaveLength(1);
335
+ expect(result.pendingText).toBe("");
336
+ });
337
+
338
+ it("does not emit incomplete lines", () => {
339
+ const read1 = { text: '{"incomplete": true}', endsWithNewline: false };
340
+
341
+ const result = simulatePartialLineBuffering([read1]);
342
+
343
+ expect(result.completeLines).toHaveLength(0);
344
+ expect(result.pendingText).toBe('{"incomplete": true}');
345
+ });
346
+ });
347
+
348
+ // ---------------------------------------------------------------------------
349
+ // Pending Delivery Queue Logic
350
+ // ---------------------------------------------------------------------------
351
+
352
+ describe("Pending delivery queue", () => {
353
+ interface DeliveryItem {
354
+ ordinal: number;
355
+ text: string;
356
+ delivered: boolean;
357
+ }
358
+
359
+ function simulateDeliveryQueue(
360
+ events: Array<{
361
+ type: "new_message" | "poll_retry" | "delivered";
362
+ ordinal?: number;
363
+ text?: string;
364
+ deliverySucceeds?: boolean;
365
+ }>
366
+ ): {
367
+ queue: DeliveryItem[];
368
+ deliveryAttempts: number[];
369
+ } {
370
+ const queue: DeliveryItem[] = [];
371
+ const deliveryAttempts: number[] = [];
372
+
373
+ for (const event of events) {
374
+ if (event.type === "new_message" && event.ordinal !== undefined) {
375
+ queue.push({
376
+ ordinal: event.ordinal,
377
+ text: event.text ?? "",
378
+ delivered: false,
379
+ });
380
+ } else if (event.type === "poll_retry") {
381
+ for (const item of queue) {
382
+ if (!item.delivered) {
383
+ deliveryAttempts.push(item.ordinal);
384
+ if (event.deliverySucceeds) {
385
+ item.delivered = true;
386
+ }
387
+ }
388
+ }
389
+ // Clean up delivered
390
+ while (queue.length > 0 && queue[0].delivered) {
391
+ queue.shift();
392
+ }
393
+ }
394
+ }
395
+
396
+ return { queue, deliveryAttempts };
397
+ }
398
+
399
+ it("queues message on delivery failure", () => {
400
+ const result = simulateDeliveryQueue([
401
+ { type: "new_message", ordinal: 1, text: "test" },
402
+ ]);
403
+
404
+ expect(result.queue).toHaveLength(1);
405
+ expect(result.queue[0].delivered).toBe(false);
406
+ });
407
+
408
+ it("retries queued messages on next poll", () => {
409
+ const result = simulateDeliveryQueue([
410
+ { type: "new_message", ordinal: 1, text: "test" },
411
+ { type: "poll_retry", deliverySucceeds: true },
412
+ ]);
413
+
414
+ expect(result.deliveryAttempts).toContain(1);
415
+ expect(result.queue).toHaveLength(0); // Cleaned up
416
+ });
417
+
418
+ it("retries until successful", () => {
419
+ const result = simulateDeliveryQueue([
420
+ { type: "new_message", ordinal: 1, text: "test" },
421
+ { type: "poll_retry", deliverySucceeds: false },
422
+ { type: "poll_retry", deliverySucceeds: false },
423
+ { type: "poll_retry", deliverySucceeds: true },
424
+ ]);
425
+
426
+ expect(result.deliveryAttempts).toEqual([1, 1, 1]); // 3 attempts
427
+ expect(result.queue).toHaveLength(0);
428
+ });
429
+
430
+ it("processes multiple queued messages in order", () => {
431
+ const result = simulateDeliveryQueue([
432
+ { type: "new_message", ordinal: 1, text: "first" },
433
+ { type: "new_message", ordinal: 2, text: "second" },
434
+ { type: "poll_retry", deliverySucceeds: true }, // delivers both
435
+ ]);
436
+
437
+ expect(result.deliveryAttempts).toContain(1);
438
+ expect(result.deliveryAttempts).toContain(2);
439
+ expect(result.queue).toHaveLength(0);
440
+ });
441
+
442
+ it("does not re-queue already queued messages", () => {
443
+ // This simulates the case where a message fails but is already in queue
444
+ // It should not be added twice
445
+ const result = simulateDeliveryQueue([
446
+ { type: "new_message", ordinal: 1, text: "test" },
447
+ { type: "poll_retry", deliverySucceeds: false },
448
+ ]);
449
+
450
+ expect(result.queue).toHaveLength(1); // Still only one
451
+ expect(result.deliveryAttempts).toEqual([1]);
452
+ });
453
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/config-capture",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Auto-capture API-fetched configuration values to AGENTS.md",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/context-compiler",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Bounded context selection engine for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/context-discovery",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/context-manager",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Context Window Manager — prevents model context overflow via compaction and resume prompts (RFC-0010)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cookie-sanitizer",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "pi-harness-runtime — cookie sanitizer (provider-quota cookie file normaliser)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cost-optimizer",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Cost Optimizer for pi-harness-runtime — budget tracking, forecasting, and model selection optimization",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/dependency-analyzer",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/django-plugin",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Django framework analysis — models, management commands, settings",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/doc-generator",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Auto-Doc Generator — generate documentation from source code (RFC-0014)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/e2e-tester",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/evaluation-engine",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Evaluation engine for pi-harness runtime (RFC-0057)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/evaluation-runner",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic evaluation framework for AI outputs (RFC-0020)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-bus",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-store",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/experience-replay",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Experience replay for debugging and reproducibility (RFC-0059)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/feedback-collector",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "type": "module",
5
5
  "description": "Collect and aggregate human/automated feedback (RFC-0021)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/file-copy-helper",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Injects cp/sudo cp rule when user asks to mimic/copy files",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/framework-detector",
3
- "version": "1.1.255",
3
+ "version": "1.1.257",
4
4
  "description": "Framework detection system for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",