pi-harness-runtime 1.1.392 → 1.1.394

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 (116) hide show
  1. package/harness/console-guard.ts +73 -0
  2. package/index.ts +103 -1
  3. package/package.json +1 -1
  4. package/packages/a2a-adapter/package.json +1 -1
  5. package/packages/agent-policy/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/codex-watcher/package.json +1 -1
  20. package/packages/config-capture/package.json +1 -1
  21. package/packages/connection-runtime/package.json +1 -1
  22. package/packages/context-compiler/package.json +1 -1
  23. package/packages/context-discovery/package.json +1 -1
  24. package/packages/context-manager/package.json +1 -1
  25. package/packages/contracts/package.json +1 -1
  26. package/packages/cookie-sanitizer/package.json +1 -1
  27. package/packages/cost-optimizer/package.json +1 -1
  28. package/packages/dependency-analyzer/package.json +1 -1
  29. package/packages/django-plugin/package.json +1 -1
  30. package/packages/doc-generator/package.json +1 -1
  31. package/packages/durable-review/package.json +1 -1
  32. package/packages/e2e-tester/package.json +1 -1
  33. package/packages/evaluation-engine/package.json +1 -1
  34. package/packages/evaluation-runner/package.json +1 -1
  35. package/packages/event-bus/package.json +1 -1
  36. package/packages/event-sink/package.json +1 -1
  37. package/packages/event-store/package.json +1 -1
  38. package/packages/experience-replay/package.json +1 -1
  39. package/packages/feedback-collector/package.json +1 -1
  40. package/packages/file-copy-helper/package.json +1 -1
  41. package/packages/framework-detector/package.json +1 -1
  42. package/packages/framework-plugin-sdk/package.json +1 -1
  43. package/packages/frappe-plugin/package.json +1 -1
  44. package/packages/generic-web-plugin/package.json +1 -1
  45. package/packages/health-monitor/package.json +1 -1
  46. package/packages/intent-analyzer/package.json +1 -1
  47. package/packages/jev-judge/package.json +1 -1
  48. package/packages/knowledge-graph/package.json +1 -1
  49. package/packages/knowledge-retrieval/package.json +1 -1
  50. package/packages/laravel-plugin/package.json +1 -1
  51. package/packages/learning-engine/package.json +1 -1
  52. package/packages/mcp-adapter/package.json +1 -1
  53. package/packages/memory-engine/package.json +1 -1
  54. package/packages/milestone-manager/package.json +1 -1
  55. package/packages/model-registry/package.json +1 -1
  56. package/packages/nextjs-plugin/package.json +1 -1
  57. package/packages/notification/package.json +1 -1
  58. package/packages/observability/package.json +1 -1
  59. package/packages/okf-indexer/package.json +1 -1
  60. package/packages/paperclip-integration/package.json +1 -1
  61. package/packages/performance-optimizer/package.json +1 -1
  62. package/packages/permission-mode/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/relay/dist/client.d.ts +25 -1
  78. package/packages/relay/dist/client.js +71 -23
  79. package/packages/relay/dist/index.d.ts +1 -1
  80. package/packages/relay/package.json +1 -1
  81. package/packages/relay/src/client.ts +87 -23
  82. package/packages/relay/src/index.ts +1 -1
  83. package/packages/relay/test/client.test.ts +134 -0
  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/package.json +1 -1
  96. package/packages/sprint-planner/package.json +1 -1
  97. package/packages/subscription-engine/package.json +1 -1
  98. package/packages/task-compiler/package.json +1 -1
  99. package/packages/telegram/package.json +1 -1
  100. package/packages/tencentdb-memory/package.json +1 -1
  101. package/packages/tencentdb-sync/package.json +1 -1
  102. package/packages/test-data-generator/package.json +1 -1
  103. package/packages/test-generator/package.json +1 -1
  104. package/packages/todo-bd-sync/package.json +1 -1
  105. package/packages/token-estimation/package.json +1 -1
  106. package/packages/token-optimizer/package.json +1 -1
  107. package/packages/trajectory/package.json +1 -1
  108. package/packages/tui/package.json +1 -1
  109. package/packages/types/package.json +1 -1
  110. package/packages/voice-api/package.json +1 -1
  111. package/packages/webmcp-tester/package.json +1 -1
  112. package/packages/wiki/package.json +1 -1
  113. package/packages/workflow-events/package.json +1 -1
  114. package/packages/workspace-scanner/package.json +1 -1
  115. package/packages/worktree/package.json +1 -1
  116. package/packages/write-review/package.json +1 -1
@@ -42,6 +42,19 @@ const CONNECT_TIMEOUT_MS = 10000;
42
42
  // Client Callbacks
43
43
  // ============================================================================
44
44
 
45
+ /**
46
+ * Where the client writes its diagnostics.
47
+ *
48
+ * A TUI owns the terminal: a stray console write lands in the prompt input
49
+ * field. The harness therefore injects a logger that writes to the log file
50
+ * (the console stays the default for CLI scripts).
51
+ */
52
+ export interface RelayLogger {
53
+ log?: (message: string) => void;
54
+ warn?: (message: string) => void;
55
+ error?: (message: string) => void;
56
+ }
57
+
45
58
  export interface RelayClientCallbacks {
46
59
  /** Called when connected and authenticated */
47
60
  onConnected?: (sessionId: string) => void;
@@ -69,6 +82,7 @@ export class RelayClient {
69
82
  private ws: WebSocket | null = null;
70
83
  private config: RelayConfig;
71
84
  private callbacks: RelayClientCallbacks;
85
+ private logger: RelayLogger;
72
86
 
73
87
  // State machine
74
88
  private state: ConnectionState = { state: 'disconnected' };
@@ -97,9 +111,57 @@ export class RelayClient {
97
111
  private snapshotReceived = false;
98
112
  private synchronizedReceived = false;
99
113
 
100
- constructor(config: RelayConfig, callbacks: RelayClientCallbacks = {}) {
114
+ constructor(config: RelayConfig, callbacks: RelayClientCallbacks = {}, logger: RelayLogger = {}) {
101
115
  this.config = config;
102
116
  this.callbacks = callbacks;
117
+ this.logger = logger;
118
+ }
119
+
120
+ // ========================================================================
121
+ // Diagnostics
122
+ // ========================================================================
123
+
124
+ /**
125
+ * Write a diagnostic.
126
+ *
127
+ * A TUI owns the terminal: the harness injects a logger so that nothing
128
+ * reaches the console. A partial logger must not reopen that hole - a
129
+ * missing level falls back to a level the logger does provide.
130
+ */
131
+ private log(message: string): void {
132
+ this.emit("log", message);
133
+ }
134
+
135
+ private logWarn(message: string): void {
136
+ this.emit("warn", message);
137
+ }
138
+
139
+ private logError(message: string): void {
140
+ this.emit("error", message);
141
+ }
142
+
143
+ private emit(level: "log" | "warn" | "error", message: string): void {
144
+ const logger = this.logger;
145
+ // A logger was injected: route the level to one the logger provides,
146
+ // never back to the console. The order keeps the severity as close as
147
+ // possible (error -> warn -> log).
148
+ const candidates: Array<"log" | "warn" | "error"> =
149
+ level === "error" ? ["error", "warn", "log"] : level === "warn" ? ["warn", "error", "log"] : ["log", "warn", "error"];
150
+
151
+ for (const candidate of candidates) {
152
+ const sink = candidate === "log" ? logger.log : candidate === "warn" ? logger.warn : logger.error;
153
+ if (!sink) continue;
154
+ try {
155
+ sink(message);
156
+ } catch {
157
+ // A broken logger must never break the relay client.
158
+ }
159
+ return;
160
+ }
161
+
162
+ if (level === "log") console.log(message);
163
+ else if (level === "warn") console.warn(message);
164
+ else console.error(message);
103
165
  }
104
166
 
105
167
  // ========================================================================
@@ -167,7 +229,7 @@ export class RelayClient {
167
229
 
168
230
  const url = this.config.serverUrl;
169
231
  const timeoutMs = this.config.connectTimeout ?? CONNECT_TIMEOUT_MS;
170
- console.log(`[RelayClient] Connecting to ${url}...`);
232
+ this.log(`[RelayClient] Connecting to ${url}...`);
171
233
 
172
234
  return new Promise<void>((resolve, reject) => {
173
235
  const attempt = ++this.connectAttempt;
@@ -176,7 +238,7 @@ export class RelayClient {
176
238
  // Deadline covers connection AND authentication (one timer for the whole handshake)
177
239
  this.connectTimeoutTimer = setTimeout(() => {
178
240
  if (!this.pendingConnect) return;
179
- console.error(`[RelayClient] Connection timeout after ${timeoutMs}ms`);
241
+ this.logError(`[RelayClient] Connection timeout after ${timeoutMs}ms`);
180
242
  this.settleConnect(new Error(`Connection timeout after ${timeoutMs}ms`));
181
243
  this.closeSocket();
182
244
  this.setState({ state: 'disconnected' });
@@ -191,7 +253,7 @@ export class RelayClient {
191
253
  // pending: settle it, disarm the deadline and retry like any other
192
254
  // failed connect.
193
255
  const error = err instanceof Error ? err : new Error(String(err));
194
- console.error("[RelayClient] WebSocket construction failed:", error.message);
256
+ this.logError(`[RelayClient] WebSocket construction failed: ${error.message}`);
195
257
  this.settleConnect(error);
196
258
  this.setState({ state: 'disconnected' });
197
259
  this.scheduleReconnectAfterFailure();
@@ -201,7 +263,7 @@ export class RelayClient {
201
263
 
202
264
  ws.on("open", () => {
203
265
  if (this.ws !== ws) return;
204
- console.log("[RelayClient] WebSocket opened");
266
+ this.log("[RelayClient] WebSocket opened");
205
267
  this.setState({ state: 'authenticating' });
206
268
 
207
269
  // Send authentication
@@ -223,7 +285,7 @@ export class RelayClient {
223
285
  // would otherwise corrupt the live connection (state, heartbeat) and
224
286
  // schedule a spurious reconnect.
225
287
  if (this.ws !== ws) return;
226
- console.log("[RelayClient] WebSocket closed");
288
+ this.log("[RelayClient] WebSocket closed");
227
289
  this.ws = null;
228
290
  const wasPending = this.pendingConnect !== null;
229
291
  this.handleDisconnect();
@@ -235,7 +297,7 @@ export class RelayClient {
235
297
 
236
298
  ws.on("error", (err) => {
237
299
  if (this.ws !== ws) return;
238
- console.error("[RelayClient] WebSocket error:", err.message);
300
+ this.logError(`[RelayClient] WebSocket error: ${err.message}`);
239
301
  this.callbacks.onError?.(err);
240
302
  const wasPending = this.pendingConnect !== null;
241
303
  this.ws = null;
@@ -275,7 +337,7 @@ export class RelayClient {
275
337
  * Disconnect from relay server
276
338
  */
277
339
  disconnect(): void {
278
- console.log("[RelayClient] Disconnecting...");
340
+ this.log("[RelayClient] Disconnecting...");
279
341
  // Deliberate stop: do not schedule a reconnect afterwards
280
342
  this.shuttingDown = true;
281
343
  // Never leave a caller awaiting connect() hanging
@@ -301,7 +363,7 @@ export class RelayClient {
301
363
  if (this.ws?.readyState === WebSocket.OPEN) {
302
364
  this.ws.send(JSON.stringify(message));
303
365
  } else {
304
- console.warn("[RelayClient] Cannot send: WebSocket not open");
366
+ this.logWarn("[RelayClient] Cannot send: WebSocket not open");
305
367
  }
306
368
  }
307
369
 
@@ -344,18 +406,20 @@ export class RelayClient {
344
406
 
345
407
  default: {
346
408
  const unknownMsg = msg as { type: string };
347
- console.warn(`[RelayClient] Unknown message type: ${unknownMsg.type}`);
409
+ this.logWarn(`[RelayClient] Unknown message type: ${unknownMsg.type}`);
348
410
  }
349
411
  }
350
412
  } catch (err) {
351
- console.error("[RelayClient] Failed to parse message:", err);
413
+ const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
414
+ // One line per log record: the stack must not break the format.
415
+ this.logError(`[RelayClient] Failed to parse message: ${detail.replace(/\s*\n\s*/g, " | ")}`);
352
416
  this.callbacks.onError?.(err as Error);
353
417
  }
354
418
  }
355
419
 
356
420
  private handleConnected(msg: ConnectedMessage): void {
357
421
  this.clientId = msg.payload.clientId;
358
- console.log(`[RelayClient] Connected, clientId: ${this.clientId}`);
422
+ this.log(`[RelayClient] Connected, clientId: ${this.clientId}`);
359
423
  }
360
424
 
361
425
  private handleAuth(msg: AuthResponseMessage): void {
@@ -372,13 +436,13 @@ export class RelayClient {
372
436
 
373
437
  // Notify callback
374
438
  this.callbacks.onConnected?.(this.sessionId || '');
375
- console.log(`[RelayClient] Authenticated, sessionId: ${this.sessionId}`);
439
+ this.log(`[RelayClient] Authenticated, sessionId: ${this.sessionId}`);
376
440
 
377
441
  // Readiness milestone reached - settle the connect() promise
378
442
  this.settleConnect();
379
443
  } else {
380
444
  const message = msg.payload.error || 'Authentication failed';
381
- console.error(`[RelayClient] Auth failed: ${message}`);
445
+ this.logError(`[RelayClient] Auth failed: ${message}`);
382
446
  const error = new Error(message);
383
447
  this.callbacks.onError?.(error);
384
448
  // Reject the caller before tearing the socket down
@@ -392,7 +456,7 @@ export class RelayClient {
392
456
  }
393
457
 
394
458
  private handleSnapshot(msg: SnapshotMessage): void {
395
- console.log(`[RelayClient] Snapshot received: ${msg.payload.recentCallbacks.length} recent callbacks`);
459
+ this.log(`[RelayClient] Snapshot received: ${msg.payload.recentCallbacks.length} recent callbacks`);
396
460
  this.snapshotReceived = true;
397
461
 
398
462
  // Notify callback
@@ -400,7 +464,7 @@ export class RelayClient {
400
464
  }
401
465
 
402
466
  private handleSynchronized(_msg: SynchronizedMessage): void {
403
- console.log("[RelayClient] Synchronized - live events starting");
467
+ this.log("[RelayClient] Synchronized - live events starting");
404
468
  this.synchronizedReceived = true;
405
469
 
406
470
  // Notify callback
@@ -410,7 +474,7 @@ export class RelayClient {
410
474
  private handleCallback(msg: CallbackMessage): void {
411
475
  // Only process callbacks after synchronized
412
476
  if (!this.synchronizedReceived) {
413
- console.warn("[RelayClient] Ignoring callback before synchronized");
477
+ this.logWarn("[RelayClient] Ignoring callback before synchronized");
414
478
  return;
415
479
  }
416
480
 
@@ -418,7 +482,7 @@ export class RelayClient {
418
482
  }
419
483
 
420
484
  private handleStatus(msg: StatusMessage): void {
421
- console.log(`[RelayClient] Status [${msg.payload.kind}]: ${msg.payload.code} - ${msg.payload.message}`);
485
+ this.log(`[RelayClient] Status [${msg.payload.kind}]: ${msg.payload.code} - ${msg.payload.message}`);
422
486
  this.callbacks.onStatus?.(msg.payload);
423
487
  }
424
488
 
@@ -434,7 +498,7 @@ export class RelayClient {
434
498
 
435
499
  // A deliberate stop must not trigger a reconnect
436
500
  if (this.shuttingDown) {
437
- console.log("[RelayClient] Disconnected (intentional stop, no reconnect)");
501
+ this.log("[RelayClient] Disconnected (intentional stop, no reconnect)");
438
502
  return;
439
503
  }
440
504
 
@@ -464,12 +528,12 @@ export class RelayClient {
464
528
  this.reconnectAttempt++;
465
529
  this.setState({ state: 'reconnecting', attempt: this.reconnectAttempt });
466
530
 
467
- console.log(`[RelayClient] Scheduling reconnect attempt ${this.reconnectAttempt} in ${this.reconnectDelay}ms`);
531
+ this.log(`[RelayClient] Scheduling reconnect attempt ${this.reconnectAttempt} in ${this.reconnectDelay}ms`);
468
532
 
469
533
  this.reconnectTimer = setTimeout(() => {
470
534
  this.reconnectTimer = null;
471
535
  this.connect().catch((err) => {
472
- console.error(`[RelayClient] Reconnect failed: ${err.message}`);
536
+ this.logError(`[RelayClient] Reconnect failed: ${err.message}`);
473
537
  });
474
538
  }, this.reconnectDelay);
475
539
 
@@ -545,13 +609,13 @@ export class RelayClient {
545
609
  ws.removeAllListeners("error");
546
610
  // An 'error' without a listener throws inside the ws implementation.
547
611
  ws.on("error", (err: Error) => {
548
- console.warn("[RelayClient] Socket error while closing:", err.message);
612
+ this.logWarn(`[RelayClient] Socket error while closing: ${err.message}`);
549
613
  });
550
614
  ws.on("close", () => {});
551
615
  try {
552
616
  ws.close();
553
617
  } catch (err) {
554
- console.warn("[RelayClient] Error while closing socket:", String(err));
618
+ this.logWarn(`[RelayClient] Error while closing socket: ${String(err)}`);
555
619
  }
556
620
  }
557
621
 
@@ -15,7 +15,7 @@ export type { RelayServerOptions } from './server.js';
15
15
 
16
16
  // Client
17
17
  export { RelayClient } from './client.js';
18
- export type { RelayClientCallbacks } from './client.js';
18
+ export type { RelayClientCallbacks, RelayLogger } from './client.js';
19
19
 
20
20
  // Endpoint resolution (relay URL + auth token discovery)
21
21
  export {
@@ -135,6 +135,140 @@ describe("RelayClient.connect", () => {
135
135
  await fixture.close();
136
136
  });
137
137
 
138
+ it("writes its diagnostics to the injected logger, never to the console", async () => {
139
+ const fixture = await startFixture("ok");
140
+ const logged: string[] = [];
141
+ const consoleWrites: string[] = [];
142
+ const originalLog = console.log;
143
+ const originalWarn = console.warn;
144
+ const originalError = console.error;
145
+ console.log = (...args: unknown[]) => {
146
+ consoleWrites.push(args.map(String).join(" "));
147
+ };
148
+ console.warn = console.log;
149
+ console.error = console.log;
150
+
151
+ try {
152
+ const client = new RelayClient(
153
+ { serverUrl: fixture.url, token: TEST_TOKEN, connectTimeout: 2000 },
154
+ {},
155
+ {
156
+ log: (message) => logged.push(message),
157
+ warn: (message) => logged.push(message),
158
+ error: (message) => logged.push(message),
159
+ },
160
+ );
161
+
162
+ await client.connect();
163
+ client.disconnect();
164
+ await fixture.close();
165
+ } finally {
166
+ console.log = originalLog;
167
+ console.warn = originalWarn;
168
+ console.error = originalError;
169
+ }
170
+
171
+ // The TUI owns the terminal: nothing may reach the console.
172
+ expect(consoleWrites.length).toBe(0);
173
+ expect(logged.some((message) => message.includes("[RelayClient]"))).toBe(true);
174
+ });
175
+
176
+ it("routes a partial logger to the levels it has, never to the console", async () => {
177
+ const fixture = await startFixture("ok");
178
+ const logged: string[] = [];
179
+ const consoleWrites: string[] = [];
180
+ const originalLog = console.log;
181
+ const originalWarn = console.warn;
182
+ const originalError = console.error;
183
+ console.log = (...args: unknown[]) => {
184
+ consoleWrites.push(args.map(String).join(" "));
185
+ };
186
+ console.warn = console.log;
187
+ console.error = console.log;
188
+
189
+ try {
190
+ // Only "log" is provided: a warning must NOT reach the console.
191
+ const client = new RelayClient(
192
+ { serverUrl: fixture.url, token: TEST_TOKEN, connectTimeout: 2000 },
193
+ {},
194
+ { log: (message) => logged.push(message) },
195
+ );
196
+
197
+ await client.connect();
198
+ client.disconnect();
199
+ await fixture.close();
200
+ } finally {
201
+ console.log = originalLog;
202
+ console.warn = originalWarn;
203
+ console.error = originalError;
204
+ }
205
+
206
+ expect(consoleWrites.length).toBe(0);
207
+ expect(logged.length).toBeGreaterThan(0);
208
+ });
209
+
210
+ it("never reaches the console for any partial logger shape", async () => {
211
+ const shapes: Array<{ name: string; logger: { log?: (m: string) => void; warn?: (m: string) => void; error?: (m: string) => void } }> = [
212
+ { name: "only log", logger: { log: () => {} } },
213
+ { name: "only warn", logger: { warn: () => {} } },
214
+ { name: "only error", logger: { error: () => {} } },
215
+ { name: "log + error", logger: { log: () => {}, error: () => {} } },
216
+ ];
217
+
218
+ for (const shape of shapes) {
219
+ const fixture = await startFixture("ok");
220
+ const consoleWrites: string[] = [];
221
+ const originalLog = console.log;
222
+ const originalWarn = console.warn;
223
+ const originalError = console.error;
224
+ console.log = (...args: unknown[]) => {
225
+ consoleWrites.push(args.map(String).join(" "));
226
+ };
227
+ console.warn = console.log;
228
+ console.error = console.log;
229
+
230
+ try {
231
+ const client = new RelayClient(
232
+ { serverUrl: fixture.url, token: TEST_TOKEN, connectTimeout: 2000 },
233
+ {},
234
+ shape.logger,
235
+ );
236
+
237
+ await client.connect();
238
+ client.disconnect();
239
+ await fixture.close();
240
+ } finally {
241
+ console.log = originalLog;
242
+ console.warn = originalWarn;
243
+ console.error = originalError;
244
+ }
245
+
246
+ expect(consoleWrites.length).toBe(0);
247
+ }
248
+ });
249
+
250
+ it("survives a logger that throws", async () => {
251
+ const fixture = await startFixture("ok");
252
+ let calls = 0;
253
+ const broken = (): void => {
254
+ calls++;
255
+ throw new Error("logger is broken");
256
+ };
257
+ const client = new RelayClient(
258
+ { serverUrl: fixture.url, token: TEST_TOKEN, connectTimeout: 2000 },
259
+ {},
260
+ { log: broken, warn: broken, error: broken },
261
+ );
262
+
263
+ await client.connect();
264
+ expect(client.getState().state).toBe("connected");
265
+ // The logger really ran (the test would be vacuous otherwise).
266
+ expect(calls).toBeGreaterThan(0);
267
+
268
+ client.disconnect();
269
+ await fixture.close();
270
+ });
271
+
138
272
  it("rejects when the server never answers authentication", async () => {
139
273
  const fixture = await startFixture("silent");
140
274
  const client = new RelayClient({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/release-manager",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "type": "module",
5
5
  "description": "Release management for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/requirement-compiler",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Compiles raw requirements into validated CompiledRequirement with ambiguity detection and risk tagging",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/runtime",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "pi-harness-runtime — runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/scheduler",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "pi-harness-runtime — scheduler",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/scheduler-adapter",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Scheduler Adapter — cron, systemd, launchd, internal adapters for autonomous runtime (RFC-0101 §6)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/session",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Session management system for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/session-api",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/session-export",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/shared-context",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "pi-harness-runtime — shared-context",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/skill-mcp-client",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "MCP client for Skills SaaS backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/skill-registry",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Skill Registry for pi-harness-runtime — dynamic skill discovery and invocation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/skills",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "File-based skill system with pi.dev compatibility and Hermes self-improvement",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/sprint-planner",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "type": "module",
5
5
  "description": "Sprint planning engine for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/subscription-engine",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/task-compiler",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Task Compiler for pi-harness-runtime — produces deterministic task DAG from CompiledRequirement",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/telegram",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "pi-harness-runtime — Telegram inbound/outbound",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/tencentdb-memory",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "TencentDB Agent Memory client - sync skills to centralized knowledge base",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tencentdb-sync",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Sync SKILL.md skills to TencentDB-Agent-Memory server (RFC-0105/0106)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/test-data-generator",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Test data generation for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/test-generator",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Auto-Test Generator — generate unit and E2E tests from source code (RFC-0013)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/todo-bd-sync",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Two-way sync between rpiv-todo overlay and bd issue tracker",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-estimation",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "pi-harness-runtime — token-estimation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-optimizer",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Token Budget Optimizer — maximize prompt value within token limits (RFC-0011)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/trajectory",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
4
4
  "description": "Trajectory capture for the write-review loop — persists cycle records for learning",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tui",
3
- "version": "1.1.392",
3
+ "version": "1.1.394",
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.392",
3
+ "version": "1.1.394",
4
4
  "description": "Shared TypeScript types for pi-harness-runtime packages",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",