super-subagents 1.3.13 → 1.3.14

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.
@@ -19,22 +19,12 @@ declare class SDKClientManager {
19
19
  private pendingClients;
20
20
  private isShuttingDown;
21
21
  private staleSessionTimer;
22
+ private sweepCycle;
22
23
  /**
23
24
  * Initialize the client manager (call on MCP connect).
24
25
  * Initializes the account manager and resets to first token.
25
26
  */
26
27
  initialize(): void;
27
- /**
28
- * Start periodic sweeper that detects and destroys orphaned sessions.
29
- * A session is orphaned if it's tracked in a client entry but its corresponding
30
- * task is in a terminal state or no longer exists.
31
- */
32
- private startStaleSessionSweeper;
33
- /**
34
- * Sweep and destroy orphaned sessions across all client entries.
35
- * This is the safety net that catches sessions missed by normal lifecycle cleanup.
36
- */
37
- private sweepStaleSessions;
38
28
  /**
39
29
  * Reset the client manager (call on MCP reconnect).
40
30
  * Clears all clients and resets account rotation to first token.
@@ -89,6 +79,33 @@ declare class SDKClientManager {
89
79
  * Prevents gradual resource leak from accumulated stale clients after rotation.
90
80
  */
91
81
  private cleanupStaleClients;
82
+ /**
83
+ * Start the periodic stale-session sweeper. Timer is unref()'d so it
84
+ * doesn't prevent the process from exiting.
85
+ */
86
+ private startStaleSessionSweeper;
87
+ /**
88
+ * Ping each client to detect dead/crashed CLI processes. Removes dead
89
+ * clients from the pool and force-stops them.
90
+ */
91
+ private checkClientHealth;
92
+ /**
93
+ * Sweep all tracked sessions. If the corresponding task (looked up by
94
+ * sessionId which equals taskId by convention) is terminal or missing,
95
+ * destroy the session with a timeout. Then recycle PTY leakers.
96
+ */
97
+ private sweepStaleSessions;
98
+ /**
99
+ * Detect server-side sessions that we're not tracking locally (orphaned from
100
+ * crashes). Uses listSessions() and deletes any not in our tracking map.
101
+ */
102
+ private detectOrphanedSessions;
103
+ /**
104
+ * For each client entry, check PTY FD count via lsof. If count exceeds
105
+ * threshold and no active sessions remain, recycle the client (stop it
106
+ * and remove from map so the next getClient() creates a fresh one).
107
+ */
108
+ private recyclePtyLeakers;
92
109
  /**
93
110
  * Check if rotation should happen based on error.
94
111
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-client-manager.d.ts","sourceRoot":"","sources":["../../src/services/sdk-client-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAA6B,KAAK,cAAc,EAAE,KAAK,aAAa,EAAiD,MAAM,qBAAqB,CAAC;AAgEvK,cAAM,gBAAgB;IACpB,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,cAAc,CAAkD;IACxE,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,iBAAiB,CAA+B;IAExD;;;OAGG;IACH,UAAU,IAAI,IAAI;IAMlB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA4B1B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B5B;;;OAGG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAiDpD;;OAEG;YACW,YAAY;IAmB1B;;;OAGG;IACG,aAAa,CACjB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,EACxC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC;IA6B1B;;;OAGG;IACG,aAAa,CACjB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC;IA4B1B;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAUzD;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBzD;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAavD;;;;;;OAMG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,aAAa,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IA+B/I;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,mBAAmB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO;IAIxE;;OAEG;IACG,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,eAAe,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAe7G;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAW3E;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA0C/B;;OAEG;IACH,QAAQ,IAAI;QACV,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE;YACR,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,EAAE,MAAM,CAAC;YAChB,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH;CAqBF;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
1
+ {"version":3,"file":"sdk-client-manager.d.ts","sourceRoot":"","sources":["../../src/services/sdk-client-manager.ts"],"names":[],"mappings":"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"}
@@ -14,28 +14,50 @@
14
14
  * - Forwards questions to QuestionRegistry for MCP client handling
15
15
  */
16
16
  import { CopilotClient } from '@github/copilot-sdk';
17
+ import { execSync } from 'node:child_process';
17
18
  import { accountManager } from './account-manager.js';
18
19
  import { questionRegistry } from './question-registry.js';
19
20
  import { createSessionHooks } from './session-hooks.js';
20
- import { taskManager } from './task-manager.js';
21
- import { TERMINAL_STATUSES } from '../types.js';
21
+ import { taskManager, TERMINAL_STATUSES } from './task-manager.js';
22
22
  const COPILOT_PATH = process.env.COPILOT_PATH || '/opt/homebrew/bin/copilot';
23
- // Timeouts for SDK operations that can hang
23
+ const PTY_RECYCLE_THRESHOLD = 80;
24
24
  const DESTROY_SESSION_TIMEOUT_MS = 10_000;
25
25
  const CLIENT_START_TIMEOUT_MS = 30_000;
26
- const SHUTDOWN_DESTROY_TIMEOUT_MS = 5_000;
26
+ const CLIENT_HEALTH_CHECK_TIMEOUT_MS = 5_000;
27
+ const RECYCLE_STOP_TIMEOUT_MS = 10_000;
28
+ const SHUTDOWN_STOP_TIMEOUT_MS = 15_000;
29
+ const STALE_SESSION_CLEANUP_INTERVAL_MS = 60_000;
27
30
  /**
28
- * Race a promise against a timeout. Rejects with a descriptive error if the timeout fires first.
29
- * Clears the timeout when the promise resolves to prevent timer leaks.
31
+ * Generic timeout wrapper for SDK operations that can hang.
30
32
  */
31
33
  function withTimeout(promise, ms, label) {
32
- let timer;
33
- return Promise.race([
34
- promise.then((v) => { clearTimeout(timer); return v; }, (e) => { clearTimeout(timer); throw e; }),
35
- new Promise((_, reject) => {
36
- timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
37
- }),
38
- ]);
34
+ return new Promise((resolve, reject) => {
35
+ const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
36
+ promise.then((v) => { clearTimeout(timer); resolve(v); }, (e) => { clearTimeout(timer); reject(e); });
37
+ });
38
+ }
39
+ /**
40
+ * Retry-with-backoff wrapper for session.destroy(), matching the SDK's own
41
+ * 3-attempt exponential backoff pattern used in stop().
42
+ */
43
+ async function destroySessionWithRetry(session, sessionId, totalTimeoutMs) {
44
+ return withTimeout((async () => {
45
+ const MAX_ATTEMPTS = 3;
46
+ let lastError = null;
47
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
48
+ try {
49
+ await session.destroy();
50
+ return;
51
+ }
52
+ catch (err) {
53
+ lastError = err instanceof Error ? err : new Error(String(err));
54
+ if (attempt < MAX_ATTEMPTS) {
55
+ await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt - 1)));
56
+ }
57
+ }
58
+ }
59
+ throw lastError;
60
+ })(), totalTimeoutMs, `destroy session ${sessionId} (with retries)`);
39
61
  }
40
62
  /**
41
63
  * Create a user input handler that forwards questions to the QuestionRegistry.
@@ -49,13 +71,12 @@ function createUserInputHandler(taskId) {
49
71
  return response;
50
72
  };
51
73
  }
52
- // Interval for periodic stale session cleanup (every 60 seconds)
53
- const STALE_SESSION_CLEANUP_INTERVAL_MS = 60_000;
54
74
  class SDKClientManager {
55
75
  clients = new Map();
56
76
  pendingClients = new Map(); // RC-3: Dedup concurrent getClient calls
57
77
  isShuttingDown = false;
58
78
  staleSessionTimer = null;
79
+ sweepCycle = 0;
59
80
  /**
60
81
  * Initialize the client manager (call on MCP connect).
61
82
  * Initializes the account manager and resets to first token.
@@ -65,72 +86,28 @@ class SDKClientManager {
65
86
  this.startStaleSessionSweeper();
66
87
  console.error(`[sdk-client-manager] Initialized with ${accountManager.getTokenCount()} account(s)`);
67
88
  }
68
- /**
69
- * Start periodic sweeper that detects and destroys orphaned sessions.
70
- * A session is orphaned if it's tracked in a client entry but its corresponding
71
- * task is in a terminal state or no longer exists.
72
- */
73
- startStaleSessionSweeper() {
74
- if (this.staleSessionTimer) {
75
- clearInterval(this.staleSessionTimer);
76
- }
77
- this.staleSessionTimer = setInterval(() => {
78
- this.sweepStaleSessions();
79
- }, STALE_SESSION_CLEANUP_INTERVAL_MS);
80
- this.staleSessionTimer.unref();
81
- }
82
- /**
83
- * Sweep and destroy orphaned sessions across all client entries.
84
- * This is the safety net that catches sessions missed by normal lifecycle cleanup.
85
- */
86
- sweepStaleSessions() {
87
- if (this.isShuttingDown)
88
- return;
89
- let destroyed = 0;
90
- for (const [clientKey, entry] of this.clients) {
91
- for (const [sessionId, session] of entry.sessions) {
92
- // Check if any active (non-terminal) task references this session
93
- const task = taskManager.getTask(sessionId); // sessionId === taskId by convention
94
- const isOrphaned = !task || TERMINAL_STATUSES.has(task.status);
95
- if (isOrphaned) {
96
- // Remove from tracking first, then fire-and-forget destroy with timeout
97
- entry.sessions.delete(sessionId);
98
- withTimeout(session.destroy(), DESTROY_SESSION_TIMEOUT_MS, `sweeper:destroy(${sessionId})`).catch((err) => {
99
- console.error(`[sdk-client-manager] Sweeper: failed to destroy session ${sessionId}:`, err);
100
- });
101
- destroyed++;
102
- }
103
- }
104
- }
105
- if (destroyed > 0) {
106
- console.error(`[sdk-client-manager] Sweeper: destroyed ${destroyed} orphaned session(s)`);
107
- // Now that sessions are cleared, try to clean up stale clients too
108
- this.cleanupStaleClients();
109
- }
110
- }
111
89
  /**
112
90
  * Reset the client manager (call on MCP reconnect).
113
91
  * Clears all clients and resets account rotation to first token.
114
92
  */
115
93
  async reset() {
116
- // Invalidate any in-flight client creations before stopping existing clients
117
94
  this.pendingClients.clear();
118
- // Destroy all sessions first, then stop clients
119
- for (const [key, entry] of this.clients) {
120
- // Destroy sessions in parallel to release PTY FDs (with timeout so hung sessions don't block reset)
121
- await Promise.allSettled([...entry.sessions.entries()].map(([sessionId, session]) => withTimeout(session.destroy(), DESTROY_SESSION_TIMEOUT_MS, `reset:destroy(${sessionId})`)
122
- .catch(err => console.error(`[sdk-client-manager] Error destroying session ${sessionId} during reset:`, err))));
95
+ // Clear our tracking first let SDK stop() handle session cleanup
96
+ for (const entry of this.clients.values()) {
123
97
  entry.sessions.clear();
124
- // Then stop the client
98
+ }
99
+ for (const [key, entry] of this.clients) {
125
100
  try {
126
- await entry.client.stop();
101
+ await withTimeout(entry.client.stop(), SHUTDOWN_STOP_TIMEOUT_MS, `stop client ${key}`);
127
102
  }
128
- catch (err) {
129
- console.error(`[sdk-client-manager] Error stopping client ${key}:`, err);
103
+ catch {
104
+ try {
105
+ await entry.client.forceStop();
106
+ }
107
+ catch { /* ignore */ }
130
108
  }
131
109
  }
132
110
  this.clients.clear();
133
- // Reset account manager to first token
134
111
  accountManager.reset();
135
112
  console.error('[sdk-client-manager] Reset complete - all clients cleared, starting from first account');
136
113
  }
@@ -190,7 +167,8 @@ class SDKClientManager {
190
167
  cwd,
191
168
  logLevel: 'error',
192
169
  autoStart: true,
193
- autoRestart: false, // RC-4 fix: Disable auto-restart to prevent unbounded CLI respawning and PTY accumulation
170
+ autoRestart: false,
171
+ useStdio: false, // TCP mode: avoids macOS stdio pipe destruction race
194
172
  };
195
173
  if (githubToken) {
196
174
  options.githubToken = githubToken;
@@ -271,10 +249,10 @@ class SDKClientManager {
271
249
  for (const entry of this.clients.values()) {
272
250
  const session = entry.sessions.get(sessionId);
273
251
  if (session) {
274
- // Remove from tracking first to prevent double-destroy from sweeper
252
+ // Delete from tracking first to prevent double-destroy race with sweeper
275
253
  entry.sessions.delete(sessionId);
276
254
  try {
277
- await withTimeout(session.destroy(), DESTROY_SESSION_TIMEOUT_MS, `destroySession(${sessionId})`);
255
+ await destroySessionWithRetry(session, sessionId, DESTROY_SESSION_TIMEOUT_MS);
278
256
  }
279
257
  catch (err) {
280
258
  console.error(`[sdk-client-manager] Failed to destroy session ${sessionId}:`, err);
@@ -348,6 +326,152 @@ class SDKClientManager {
348
326
  }
349
327
  }
350
328
  }
329
+ /**
330
+ * Start the periodic stale-session sweeper. Timer is unref()'d so it
331
+ * doesn't prevent the process from exiting.
332
+ */
333
+ startStaleSessionSweeper() {
334
+ if (this.staleSessionTimer)
335
+ return;
336
+ this.staleSessionTimer = setInterval(() => {
337
+ this.sweepStaleSessions().catch((err) => {
338
+ console.error('[sdk-client-manager] Sweeper error:', err);
339
+ });
340
+ }, STALE_SESSION_CLEANUP_INTERVAL_MS);
341
+ this.staleSessionTimer.unref();
342
+ console.error('[sdk-client-manager] Stale-session sweeper started');
343
+ }
344
+ /**
345
+ * Ping each client to detect dead/crashed CLI processes. Removes dead
346
+ * clients from the pool and force-stops them.
347
+ */
348
+ async checkClientHealth() {
349
+ for (const [key, entry] of this.clients.entries()) {
350
+ try {
351
+ await withTimeout(entry.client.ping('health'), CLIENT_HEALTH_CHECK_TIMEOUT_MS, `ping client ${key}`);
352
+ }
353
+ catch {
354
+ console.error(`[sdk-client-manager] Health check failed for client ${key}, removing dead client`);
355
+ entry.sessions.clear();
356
+ this.clients.delete(key);
357
+ try {
358
+ await entry.client.forceStop();
359
+ }
360
+ catch { /* already dead */ }
361
+ }
362
+ }
363
+ }
364
+ /**
365
+ * Sweep all tracked sessions. If the corresponding task (looked up by
366
+ * sessionId which equals taskId by convention) is terminal or missing,
367
+ * destroy the session with a timeout. Then recycle PTY leakers.
368
+ */
369
+ async sweepStaleSessions() {
370
+ // Remove dead clients first so we don't try to destroy sessions on them
371
+ await this.checkClientHealth();
372
+ let destroyed = 0;
373
+ for (const entry of this.clients.values()) {
374
+ for (const [sessionId, session] of entry.sessions) {
375
+ const task = taskManager.getTask(sessionId);
376
+ if (!task || TERMINAL_STATUSES.has(task.status)) {
377
+ entry.sessions.delete(sessionId);
378
+ try {
379
+ await withTimeout(entry.client.deleteSession(sessionId), DESTROY_SESSION_TIMEOUT_MS, `delete session ${sessionId}`);
380
+ }
381
+ catch {
382
+ // Fall back to local destroy if deleteSession fails
383
+ try {
384
+ await destroySessionWithRetry(session, sessionId, DESTROY_SESSION_TIMEOUT_MS);
385
+ }
386
+ catch (err) {
387
+ console.error(`[sdk-client-manager] Sweeper: destroy failed for ${sessionId}: ${err}`);
388
+ }
389
+ }
390
+ destroyed++;
391
+ }
392
+ }
393
+ }
394
+ if (destroyed > 0) {
395
+ console.error(`[sdk-client-manager] Sweeper: destroyed ${destroyed} stale session(s)`);
396
+ }
397
+ // Every 5th sweep cycle, detect and clean up orphaned server-side sessions
398
+ this.sweepCycle++;
399
+ if (this.sweepCycle % 5 === 0) {
400
+ await this.detectOrphanedSessions();
401
+ }
402
+ await this.recyclePtyLeakers();
403
+ }
404
+ /**
405
+ * Detect server-side sessions that we're not tracking locally (orphaned from
406
+ * crashes). Uses listSessions() and deletes any not in our tracking map.
407
+ */
408
+ async detectOrphanedSessions() {
409
+ for (const [key, entry] of this.clients.entries()) {
410
+ try {
411
+ const serverSessions = await withTimeout(entry.client.listSessions(), CLIENT_HEALTH_CHECK_TIMEOUT_MS, `listSessions ${key}`);
412
+ const trackedIds = new Set(entry.sessions.keys());
413
+ for (const meta of serverSessions) {
414
+ if (!trackedIds.has(meta.sessionId)) {
415
+ console.error(`[sdk-client-manager] Sweeper: deleting orphaned session ${meta.sessionId}`);
416
+ try {
417
+ await entry.client.deleteSession(meta.sessionId);
418
+ }
419
+ catch { /* best effort */ }
420
+ }
421
+ }
422
+ }
423
+ catch { /* listSessions failure is non-fatal */ }
424
+ }
425
+ }
426
+ /**
427
+ * For each client entry, check PTY FD count via lsof. If count exceeds
428
+ * threshold and no active sessions remain, recycle the client (stop it
429
+ * and remove from map so the next getClient() creates a fresh one).
430
+ */
431
+ async recyclePtyLeakers() {
432
+ for (const [key, entry] of this.clients.entries()) {
433
+ // Skip dead clients — health check will handle cleanup
434
+ try {
435
+ await withTimeout(entry.client.ping('recycle-check'), 3_000, `ping ${key}`);
436
+ }
437
+ catch {
438
+ continue;
439
+ }
440
+ // SDK has no public API for process PID or FD count.
441
+ // cliProcess is private (client.ts:121). This cast is the only way
442
+ // to count ptmx FDs until the SDK adds a resource health endpoint.
443
+ const pid = entry.client.cliProcess?.pid;
444
+ if (!pid)
445
+ continue;
446
+ let ptmxCount = 0;
447
+ try {
448
+ const output = execSync(`lsof -p ${pid} 2>/dev/null | grep -c ptmx`, {
449
+ encoding: 'utf8',
450
+ timeout: 5_000,
451
+ });
452
+ ptmxCount = parseInt(output.trim(), 10) || 0;
453
+ }
454
+ catch {
455
+ // grep returns exit code 1 when no matches → ptmxCount stays 0
456
+ continue;
457
+ }
458
+ if (ptmxCount > PTY_RECYCLE_THRESHOLD && entry.sessions.size === 0) {
459
+ console.error(`[sdk-client-manager] Sweeper: recycling client ${key} (${ptmxCount} ptmx FDs, 0 active sessions)`);
460
+ // Escalating shutdown: graceful → force
461
+ try {
462
+ await withTimeout(entry.client.stop(), RECYCLE_STOP_TIMEOUT_MS, `stop client ${key}`);
463
+ }
464
+ catch (stopErr) {
465
+ console.error(`[sdk-client-manager] Sweeper: graceful stop failed for ${key}, forcing: ${stopErr}`);
466
+ try {
467
+ await entry.client.forceStop();
468
+ }
469
+ catch { /* ignore */ }
470
+ }
471
+ this.clients.delete(key);
472
+ }
473
+ }
474
+ }
351
475
  /**
352
476
  * Check if rotation should happen based on error.
353
477
  */
@@ -392,28 +516,29 @@ class SDKClientManager {
392
516
  async shutdown() {
393
517
  this.isShuttingDown = true;
394
518
  this.pendingClients.clear();
395
- // Stop the stale session sweeper
396
519
  if (this.staleSessionTimer) {
397
520
  clearInterval(this.staleSessionTimer);
398
521
  this.staleSessionTimer = null;
399
522
  }
400
523
  const errors = [];
401
- for (const [key, entry] of this.clients) {
402
- // Destroy all sessions in parallel (with timeout so one hung session doesn't block shutdown)
403
- const results = await Promise.allSettled([...entry.sessions.entries()].map(([sessionId, session]) => withTimeout(session.destroy(), SHUTDOWN_DESTROY_TIMEOUT_MS, `shutdown:destroy(${sessionId})`)));
404
- for (const r of results) {
405
- if (r.status === 'rejected') {
406
- errors.push(r.reason instanceof Error ? r.reason : new Error(String(r.reason)));
407
- }
408
- }
524
+ // Clear our tracking first (prevents sweeper interference)
525
+ for (const entry of this.clients.values()) {
409
526
  entry.sessions.clear();
410
- // Stop the client
527
+ }
528
+ // Let SDK stop() handle cleanup with its own retry+backoff, with forceStop fallback
529
+ for (const [key, entry] of this.clients) {
411
530
  try {
412
- const clientErrors = await entry.client.stop();
531
+ const clientErrors = await withTimeout(entry.client.stop(), SHUTDOWN_STOP_TIMEOUT_MS, `stop client ${key}`);
413
532
  errors.push(...clientErrors);
414
533
  }
415
534
  catch (err) {
416
- errors.push(new Error(`Failed to stop client for ${key}: ${err}`));
535
+ console.error(`[sdk-client-manager] Shutdown: stop timed out for ${key}, forcing`);
536
+ try {
537
+ await entry.client.forceStop();
538
+ }
539
+ catch (e) {
540
+ errors.push(new Error(`Failed to force-stop client ${key}: ${e}`));
541
+ }
417
542
  }
418
543
  }
419
544
  this.clients.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-client-manager.js","sourceRoot":"","sources":["../../src/services/sdk-client-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAqH,MAAM,qBAAqB,CAAC;AACvK,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,2BAA2B,CAAC;AAE7E,4CAA4C;AAC5C,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAC1C,MAAM,uBAAuB,GAAG,MAAM,CAAC;AACvC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAE1C;;;GAGG;AACH,SAAS,WAAW,CAAI,OAAmB,EAAE,EAAU,EAAE,KAAa;IACpE,IAAI,KAAqB,CAAC;IAC1B,OAAO,OAAO,CAAC,IAAI,CAAC;QAClB,OAAO,CAAC,IAAI,CACV,CAAC,CAAC,EAAE,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EACzC,CAAC,CAAC,EAAE,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CACzC;QACD,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YAC/B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtF,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAWD;;;GAGG;AACH,SAAS,sBAAsB,CAAC,MAAc;IAC5C,OAAO,KAAK,EAAE,OAAyB,EAAE,UAAiC,EAA8B,EAAE;QACxG,OAAO,CAAC,KAAK,CAAC,sDAAsD,MAAM,aAAa,UAAU,CAAC,SAAS,MAAM,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QAEtI,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAC9C,MAAM,EACN,UAAU,CAAC,SAAS,EACpB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,aAAa,IAAI,IAAI,CAC9B,CAAC;QAEF,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,iCAAiC,GAAG,MAAM,CAAC;AAEjD,MAAM,gBAAgB;IACZ,OAAO,GAA6B,IAAI,GAAG,EAAE,CAAC;IAC9C,cAAc,GAAwC,IAAI,GAAG,EAAE,CAAC,CAAC,yCAAyC;IAC1G,cAAc,GAAG,KAAK,CAAC;IACvB,iBAAiB,GAA0B,IAAI,CAAC;IAExD;;;OAGG;IACH,UAAU;QACR,cAAc,CAAC,UAAU,EAAE,CAAC;QAC5B,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,yCAAyC,cAAc,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IACtG,CAAC;IAED;;;;OAIG;IACK,wBAAwB;QAC9B,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;YACxC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,CAAC,EAAE,iCAAiC,CAAC,CAAC;QACtC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;IACjC,CAAC;IAED;;;OAGG;IACK,kBAAkB;QACxB,IAAI,IAAI,CAAC,cAAc;YAAE,OAAO;QAEhC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9C,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAClD,kEAAkE;gBAClE,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,qCAAqC;gBAClF,MAAM,UAAU,GAAG,CAAC,IAAI,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAE/D,IAAI,UAAU,EAAE,CAAC;oBACf,wEAAwE;oBACxE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBACjC,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,0BAA0B,EAAE,mBAAmB,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;wBACxG,OAAO,CAAC,KAAK,CAAC,2DAA2D,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;oBAC9F,CAAC,CAAC,CAAC;oBACH,SAAS,EAAE,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,2CAA2C,SAAS,sBAAsB,CAAC,CAAC;YAC1F,mEAAmE;YACnE,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,6EAA6E;QAC7E,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,gDAAgD;QAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACxC,oGAAoG;YACpG,MAAM,OAAO,CAAC,UAAU,CACtB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CACzD,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,0BAA0B,EAAE,iBAAiB,SAAS,GAAG,CAAC;iBACtF,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,iDAAiD,SAAS,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAChH,CACF,CAAC;YACF,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YAEvB,uBAAuB;YACvB,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,8CAA8C,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAErB,uCAAuC;QACvC,cAAc,CAAC,KAAK,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC1G,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS,CAAC,GAAW;QACzB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QAED,MAAM,YAAY,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;QACtD,MAAM,UAAU,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAEzC,mCAAmC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC,MAAM,CAAC;QACtB,CAAC;QAED,8DAA8D;QAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,uEAAuE;QACvE,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;YACjE,+EAA+E;YAC/E,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,OAAO,EAAE,CAAC;gBAC1E,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;YACnE,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE;gBAC1B,MAAM;gBACN,GAAG;gBACH,UAAU;gBACV,SAAS,EAAE,IAAI,IAAI,EAAE;gBACrB,UAAU,EAAE,IAAI,IAAI,EAAE;gBACtB,QAAQ,EAAE,IAAI,GAAG,EAAE;aACpB,CAAC,CAAC;YACH,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACtC,OAAO,CAAC,KAAK,CAAC,+CAA+C,GAAG,gBAAgB,UAAU,GAAG,CAAC,IAAI,cAAc,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;YACpI,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;YACb,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACtC,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,GAAW,EAAE,WAAoB;QAC1D,MAAM,OAAO,GAAyB;YACpC,OAAO,EAAE,YAAY;YACrB,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,KAAK,EAAE,0FAA0F;SAC/G,CAAC;QAEF,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;YAClC,OAAO,CAAC,eAAe,GAAG,KAAK,CAAC;QAClC,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,uBAAuB,EAAE,gBAAgB,CAAC,CAAC;QAC7E,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CACjB,GAAW,EACX,SAAiB,EACjB,MAAwC,EACxC,MAAe;QAEf,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAEzC,4EAA4E;QAC5E,MAAM,aAAa,GAAkB;YACnC,SAAS;YACT,GAAG,MAAM;SACV,CAAC;QAEF,mEAAmE;QACnE,IAAI,MAAM,EAAE,CAAC;YACX,aAAa,CAAC,kBAAkB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;YAClE,kFAAkF;YAClF,aAAa,CAAC,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAE1D,wCAAwC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CACjB,GAAW,EACX,SAAiB,EACjB,MAA+B,EAC/B,MAAe;QAEf,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAEzC,qEAAqE;QACrE,4DAA4D;QAC5D,MAAM,UAAU,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;QAEzC,2EAA2E;QAC3E,MAAM,YAAY,GAA2B,EAAE,GAAG,MAAM,EAAE,CAAC;QAE3D,IAAI,MAAM,EAAE,CAAC;YACX,YAAY,CAAC,kBAAkB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;YACjE,kFAAkF;YAClF,YAAY,CAAC,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAEpE,gDAAgD;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,SAAiB;QAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC9C,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,OAAO,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC9C,IAAI,OAAO,EAAE,CAAC;gBACZ,oEAAoE;gBACpE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACjC,IAAI,CAAC;oBACH,MAAM,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,0BAA0B,EAAE,kBAAkB,SAAS,GAAG,CAAC,CAAC;gBACnG,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,kDAAkD,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;gBACrF,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,SAAiB;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,gDAAgD,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,aAAa,CAAC,GAAW,EAAE,MAAc;QAC7C,MAAM,MAAM,GAAG,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAEnD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBACxB,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,YAAY,EAAE,IAAI;oBAClB,KAAK,EAAE,OAAO,cAAc,CAAC,aAAa,EAAE,wBAAwB,MAAM,CAAC,KAAK,EAAE;iBACnF,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QACjD,CAAC;QAED,4BAA4B;QAC5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,0CAA0C,MAAM,CAAC,UAAW,GAAG,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;YAEpG,oDAAoD;YACpD,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAE3B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,2CAA2C,GAAG,EAAE;aACxD,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,mBAAmB;QACzB,MAAM,YAAY,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;QACtD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAClD,IAAI,KAAK,CAAC,UAAU,KAAK,YAAY,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACnE,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBACtC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACzB,OAAO,CAAC,KAAK,CAAC,iDAAiD,GAAG,EAAE,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,UAAmB,EAAE,YAAqB;QAC5D,OAAO,cAAc,CAAC,YAAY,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,GAAW;QAC/B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;YAC5C,OAAO;gBACL,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,UAAU,EAAE,cAAc,CAAC,eAAe,EAAE;aAC7C,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,gDAAgD,EAAE,GAAG,CAAC,CAAC;YACrE,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,CAAC,eAAe,EAAE,EAAE,CAAC;QAClF,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,GAAW;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;YACzC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAA+B,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACvF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,6CAA6C,EAAE,GAAG,CAAC,CAAC;YAClE,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,iCAAiC;QACjC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACtC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;QAED,MAAM,MAAM,GAAY,EAAE,CAAC;QAE3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACxC,6FAA6F;YAC7F,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CACzD,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,2BAA2B,EAAE,oBAAoB,SAAS,GAAG,CAAC,CAC9F,CACF,CAAC;YACF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;oBAC5B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAClF,CAAC;YACH,CAAC;YACD,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YAEvB,kBAAkB;YAClB,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,6BAA6B,GAAG,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAErB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,MAAM,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED;;OAEG;IACH,QAAQ;QAWN,IAAI,QAAQ,GAAG,CAAC,CAAC;QAEjB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAClC,CAAC;QAED,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;QAE/C,OAAO;YACL,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YAC1B,QAAQ;YACR,QAAQ,EAAE;gBACR,KAAK,EAAE,YAAY,CAAC,WAAW;gBAC/B,OAAO,EAAE,YAAY,CAAC,YAAY,GAAG,CAAC;gBACtC,SAAS,EAAE,YAAY,CAAC,aAAa;gBACrC,SAAS,EAAE,YAAY,CAAC,eAAe;aACxC;SACF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"sdk-client-manager.js","sourceRoot":"","sources":["../../src/services/sdk-client-manager.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-subagents",
3
- "version": "1.3.13",
3
+ "version": "1.3.14",
4
4
  "description": "MCP server for spawning and managing GitHub Copilot CLI tasks",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -11,7 +11,8 @@
11
11
  "build": "tsc --noEmitOnError false; cp src/templates/*.mdx build/templates/; mkdir -p build/templates/overlays && cp src/templates/overlays/*.mdx build/templates/overlays/",
12
12
  "dev": "tsx watch src/index.ts",
13
13
  "start": "node build/index.js",
14
- "clean": "rm -rf build"
14
+ "clean": "rm -rf build",
15
+ "mcp:smoke": "node scripts/mcp-stdio-smoke.mjs"
15
16
  },
16
17
  "keywords": [
17
18
  "mcp",
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs/promises';
4
+ import { createHash } from 'node:crypto';
5
+ import { homedir } from 'node:os';
6
+ import path from 'node:path';
7
+ import process from 'node:process';
8
+ import { setTimeout as sleep } from 'node:timers/promises';
9
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
10
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
11
+
12
+ const TERMINAL_STATUSES = new Set([
13
+ 'completed',
14
+ 'failed',
15
+ 'cancelled',
16
+ 'rate_limited',
17
+ 'timed_out',
18
+ ]);
19
+
20
+ const DEFAULT_TASK_TIMEOUT_MS = 45_000;
21
+ const DEFAULT_WAIT_TIMEOUT_MS = 180_000;
22
+ const POLL_INTERVAL_MS = 2_000;
23
+ const DEFAULT_SCENARIO = 'basic';
24
+
25
+ function log(message) {
26
+ process.stdout.write(`[mcp-smoke] ${message}\n`);
27
+ }
28
+
29
+ function assert(condition, message) {
30
+ if (!condition) {
31
+ throw new Error(message);
32
+ }
33
+ }
34
+
35
+ function extractText(result) {
36
+ const chunks = Array.isArray(result?.content) ? result.content : [];
37
+ return chunks
38
+ .filter((item) => item && item.type === 'text' && typeof item.text === 'string')
39
+ .map((item) => item.text)
40
+ .join('\n');
41
+ }
42
+
43
+ function extractTaskId(toolResultText) {
44
+ const backtickMatch = toolResultText.match(/task_id:\s*`([^`]+)`/i);
45
+ if (backtickMatch) return backtickMatch[1];
46
+
47
+ const plainMatch = toolResultText.match(/task_id:\s*([A-Za-z0-9-]+)/i);
48
+ if (plainMatch) return plainMatch[1];
49
+
50
+ throw new Error(`Could not parse task_id from tool response:\n${toolResultText}`);
51
+ }
52
+
53
+ function parseResourceJson(readResult, uri) {
54
+ const first = readResult?.contents?.[0];
55
+ if (!first || typeof first.text !== 'string') {
56
+ throw new Error(`Resource ${uri} did not return text content`);
57
+ }
58
+ try {
59
+ return JSON.parse(first.text);
60
+ } catch (error) {
61
+ throw new Error(`Failed to parse JSON from ${uri}: ${error}`);
62
+ }
63
+ }
64
+
65
+ function resolveProvider(task) {
66
+ return task?.provider ?? task?.sessionMetrics?.provider ?? 'n/a';
67
+ }
68
+
69
+ async function loadPersistedTaskMap(cwd) {
70
+ const hash = createHash('md5').update(cwd).digest('hex');
71
+ const storagePath = path.join(homedir(), '.super-agents', `${hash}.json`);
72
+ let raw = '[]';
73
+
74
+ try {
75
+ raw = await fs.readFile(storagePath, 'utf8');
76
+ } catch {
77
+ return new Map();
78
+ }
79
+
80
+ let parsed;
81
+ try {
82
+ parsed = JSON.parse(raw);
83
+ } catch {
84
+ return new Map();
85
+ }
86
+
87
+ const map = new Map();
88
+ if (Array.isArray(parsed)) {
89
+ for (const task of parsed) {
90
+ if (task && typeof task.id === 'string') {
91
+ map.set(task.id, task);
92
+ }
93
+ }
94
+ }
95
+ return map;
96
+ }
97
+
98
+ function mergeTaskFromPersisted(task, persistedTask) {
99
+ if (!persistedTask) return task;
100
+ return {
101
+ ...task,
102
+ provider: task?.provider ?? persistedTask.provider,
103
+ sessionMetrics: {
104
+ ...(task?.sessionMetrics ?? {}),
105
+ ...(persistedTask?.sessionMetrics ?? {}),
106
+ },
107
+ };
108
+ }
109
+
110
+ async function waitForTerminalStatus(client, taskId, waitTimeoutMs) {
111
+ const startedAt = Date.now();
112
+ let lastStatus = '<unknown>';
113
+
114
+ while (Date.now() - startedAt < waitTimeoutMs) {
115
+ const taskRead = await client.readResource({ uri: `task:///${taskId}` });
116
+ const task = parseResourceJson(taskRead, `task:///${taskId}`);
117
+ const status = task?.status;
118
+
119
+ if (status !== lastStatus) {
120
+ log(`task ${taskId} status: ${status}`);
121
+ lastStatus = status;
122
+ }
123
+
124
+ if (TERMINAL_STATUSES.has(status)) {
125
+ return task;
126
+ }
127
+
128
+ await sleep(POLL_INTERVAL_MS);
129
+ }
130
+
131
+ throw new Error(`Timed out waiting for terminal status on task ${taskId}`);
132
+ }
133
+
134
+ async function spawnAndWait(client, cwd, index, prompt, taskTimeoutMs, waitTimeoutMs) {
135
+
136
+ const spawnResult = await client.callTool({
137
+ name: 'spawn_task',
138
+ arguments: {
139
+ prompt,
140
+ task_type: 'super-coder',
141
+ cwd,
142
+ timeout: taskTimeoutMs,
143
+ autonomous: true,
144
+ model: 'claude-haiku-4.5',
145
+ labels: ['mcp-smoke', `case-${index}`],
146
+ },
147
+ });
148
+
149
+ const spawnText = extractText(spawnResult);
150
+ const taskId = extractTaskId(spawnText);
151
+ log(`spawned task #${index}: ${taskId}`);
152
+
153
+ // Must be readable immediately in the same persistent session.
154
+ await client.readResource({ uri: `task:///${taskId}` });
155
+
156
+ const finalTask = await waitForTerminalStatus(client, taskId, waitTimeoutMs);
157
+ assert(finalTask.status, `Task ${taskId} returned without status`);
158
+
159
+ if (finalTask.timeoutReason === 'server_restart') {
160
+ throw new Error(`Task ${taskId} ended with server_restart (persistent session check failed)`);
161
+ }
162
+
163
+ log(`task ${taskId} terminal: ${finalTask.status}`);
164
+ return finalTask;
165
+ }
166
+
167
+ async function main() {
168
+ const cwd = path.resolve(process.argv[2] || process.cwd());
169
+ const serverEntry = path.join(cwd, 'build', 'index.js');
170
+ const taskTimeoutMs = Number(process.env.MCP_SMOKE_TASK_TIMEOUT_MS || DEFAULT_TASK_TIMEOUT_MS);
171
+ const waitTimeoutMs = Number(process.env.MCP_SMOKE_WAIT_TIMEOUT_MS || DEFAULT_WAIT_TIMEOUT_MS);
172
+ const scenario = process.env.MCP_SMOKE_SCENARIO || DEFAULT_SCENARIO;
173
+ const requireCompleted = process.env.MCP_SMOKE_REQUIRE_COMPLETED === 'true';
174
+ const expectedProvider = process.env.MCP_SMOKE_EXPECT_PROVIDER;
175
+ const expectFallbackActivated = process.env.MCP_SMOKE_EXPECT_FALLBACK_ACTIVATED;
176
+ const loremPath = path.join(cwd, 'lorem.txt');
177
+
178
+ const client = new Client(
179
+ { name: 'mcp-stdio-smoke-client', version: '1.0.0' },
180
+ { capabilities: { tools: {}, resources: {}, prompts: {} } },
181
+ );
182
+
183
+ const transport = new StdioClientTransport({
184
+ command: process.execPath,
185
+ args: [serverEntry],
186
+ cwd,
187
+ env: process.env,
188
+ stderr: 'pipe',
189
+ });
190
+
191
+ if (transport.stderr) {
192
+ transport.stderr.on('data', (chunk) => {
193
+ const text = String(chunk).trim();
194
+ if (text.length > 0) {
195
+ process.stderr.write(`[mcp-server] ${text}\n`);
196
+ }
197
+ });
198
+ }
199
+
200
+ try {
201
+ log(`connecting to stdio server: ${serverEntry}`);
202
+ await client.connect(transport);
203
+ log('connected');
204
+
205
+ const tools = await client.listTools();
206
+ assert(Array.isArray(tools.tools), 'tools/list did not return a tools array');
207
+ assert(tools.tools.some((t) => t.name === 'spawn_task'), 'spawn_task tool not found');
208
+ assert(tools.tools.some((t) => t.name === 'cancel_task'), 'cancel_task tool not found');
209
+ log(`tools/list ok (${tools.tools.length} tools)`);
210
+
211
+ const resources = await client.listResources();
212
+ assert(Array.isArray(resources.resources), 'resources/list did not return resources array');
213
+ log(`resources/list ok (${resources.resources.length} resources)`);
214
+
215
+ const systemStatus = await client.readResource({ uri: 'system:///status' });
216
+ const systemJson = parseResourceJson(systemStatus, 'system:///status');
217
+ log(`system/status ok (accounts.total=${systemJson?.accounts?.total ?? 'n/a'})`);
218
+
219
+ // Start from a clean workspace state.
220
+ await client.callTool({
221
+ name: 'cancel_task',
222
+ arguments: { task_id: 'all', clear: true, confirm: true },
223
+ });
224
+ log('cleared previous tasks');
225
+
226
+ let firstPrompt =
227
+ 'MCP smoke test #1. Print exactly one short line that contains MCP_SMOKE_OK and then stop.';
228
+ let secondPrompt =
229
+ 'MCP smoke test #2. Print exactly one short line that contains MCP_SMOKE_OK and then stop.';
230
+
231
+ if (scenario === 'lorem') {
232
+ await fs.rm(loremPath, { force: true });
233
+ firstPrompt =
234
+ 'Create a file named lorem.txt in the current working directory with exactly one line: LOREM_AGENT_1. Then stop.';
235
+ secondPrompt =
236
+ 'Append a new line to lorem.txt in the current working directory with exactly: LOREM_AGENT_2. Then stop.';
237
+ log(`scenario=lorem (target=${loremPath})`);
238
+ } else {
239
+ log(`scenario=${scenario}`);
240
+ }
241
+
242
+ const first = await spawnAndWait(client, cwd, 1, firstPrompt, taskTimeoutMs, waitTimeoutMs);
243
+ const second = await spawnAndWait(client, cwd, 2, secondPrompt, taskTimeoutMs, waitTimeoutMs);
244
+
245
+ const persistedMap = await loadPersistedTaskMap(cwd);
246
+ const mergedFirst = mergeTaskFromPersisted(first, persistedMap.get(first.id));
247
+ const mergedSecond = mergeTaskFromPersisted(second, persistedMap.get(second.id));
248
+
249
+ const allTasksRead = await client.readResource({ uri: 'task:///all' });
250
+ const allTasks = parseResourceJson(allTasksRead, 'task:///all');
251
+ assert(Array.isArray(allTasks.tasks), 'task:///all did not return tasks array');
252
+ log(`task:///all visible in-session (count=${allTasks.count})`);
253
+
254
+ log('final task outcomes:');
255
+ log(`- ${mergedFirst.id}: ${mergedFirst.status}`);
256
+ log(`- ${mergedSecond.id}: ${mergedSecond.status}`);
257
+ const firstProvider = resolveProvider(mergedFirst);
258
+ const secondProvider = resolveProvider(mergedSecond);
259
+ log(`- ${mergedFirst.id} provider: ${firstProvider} fallbackActivated: ${mergedFirst.sessionMetrics?.fallbackActivated ?? false}`);
260
+ log(`- ${mergedSecond.id} provider: ${secondProvider} fallbackActivated: ${mergedSecond.sessionMetrics?.fallbackActivated ?? false}`);
261
+
262
+ if (requireCompleted) {
263
+ assert(mergedFirst.status === 'completed', `Task ${mergedFirst.id} not completed (status=${mergedFirst.status})`);
264
+ assert(mergedSecond.status === 'completed', `Task ${mergedSecond.id} not completed (status=${mergedSecond.status})`);
265
+ log('requireCompleted=true check passed');
266
+ }
267
+
268
+ if (expectedProvider) {
269
+ assert(firstProvider === expectedProvider, `Task ${mergedFirst.id} provider mismatch: expected=${expectedProvider} actual=${firstProvider}`);
270
+ assert(secondProvider === expectedProvider, `Task ${mergedSecond.id} provider mismatch: expected=${expectedProvider} actual=${secondProvider}`);
271
+ log(`expectedProvider=${expectedProvider} check passed`);
272
+ }
273
+
274
+ if (expectFallbackActivated === 'true') {
275
+ assert(mergedFirst.sessionMetrics?.fallbackActivated === true, `Task ${mergedFirst.id} fallbackActivated was not true`);
276
+ assert(mergedSecond.sessionMetrics?.fallbackActivated === true, `Task ${mergedSecond.id} fallbackActivated was not true`);
277
+ log('expectFallbackActivated=true check passed');
278
+ } else if (expectFallbackActivated === 'false') {
279
+ assert(mergedFirst.sessionMetrics?.fallbackActivated !== true, `Task ${mergedFirst.id} fallbackActivated unexpectedly true`);
280
+ assert(mergedSecond.sessionMetrics?.fallbackActivated !== true, `Task ${mergedSecond.id} fallbackActivated unexpectedly true`);
281
+ log('expectFallbackActivated=false check passed');
282
+ }
283
+
284
+ if (scenario === 'lorem') {
285
+ const lorem = await fs.readFile(loremPath, 'utf8');
286
+ assert(lorem.includes('LOREM_AGENT_1'), 'lorem.txt missing LOREM_AGENT_1');
287
+ assert(lorem.includes('LOREM_AGENT_2'), 'lorem.txt missing LOREM_AGENT_2');
288
+ log(`lorem.txt verification passed (${loremPath})`);
289
+ }
290
+
291
+ log('PASS: persistent MCP stdio session handled back-to-back task lifecycle without server_restart');
292
+ } finally {
293
+ await client.close().catch(() => {});
294
+ }
295
+ }
296
+
297
+ main().catch((error) => {
298
+ process.stderr.write(`[mcp-smoke] FAIL: ${error instanceof Error ? error.message : String(error)}\n`);
299
+ process.exit(1);
300
+ });
@@ -15,35 +15,21 @@
15
15
  */
16
16
 
17
17
  import { CopilotClient, type CopilotClientOptions, type CopilotSession, type SessionConfig, type UserInputRequest, type UserInputResponse } from '@github/copilot-sdk';
18
+ import { execSync } from 'node:child_process';
18
19
  import { accountManager } from './account-manager.js';
19
20
  import { questionRegistry } from './question-registry.js';
20
21
  import { createSessionHooks } from './session-hooks.js';
21
- import { taskManager } from './task-manager.js';
22
- import { TERMINAL_STATUSES } from '../types.js';
22
+ import { taskManager, TERMINAL_STATUSES } from './task-manager.js';
23
23
 
24
24
  const COPILOT_PATH = process.env.COPILOT_PATH || '/opt/homebrew/bin/copilot';
25
25
 
26
- // Timeouts for SDK operations that can hang
26
+ const PTY_RECYCLE_THRESHOLD = 80;
27
27
  const DESTROY_SESSION_TIMEOUT_MS = 10_000;
28
28
  const CLIENT_START_TIMEOUT_MS = 30_000;
29
- const SHUTDOWN_DESTROY_TIMEOUT_MS = 5_000;
30
-
31
- /**
32
- * Race a promise against a timeout. Rejects with a descriptive error if the timeout fires first.
33
- * Clears the timeout when the promise resolves to prevent timer leaks.
34
- */
35
- function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {
36
- let timer: NodeJS.Timeout;
37
- return Promise.race([
38
- promise.then(
39
- (v) => { clearTimeout(timer); return v; },
40
- (e) => { clearTimeout(timer); throw e; },
41
- ),
42
- new Promise<never>((_, reject) => {
43
- timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
44
- }),
45
- ]);
46
- }
29
+ const CLIENT_HEALTH_CHECK_TIMEOUT_MS = 5_000;
30
+ const RECYCLE_STOP_TIMEOUT_MS = 10_000;
31
+ const SHUTDOWN_STOP_TIMEOUT_MS = 15_000;
32
+ const STALE_SESSION_CLEANUP_INTERVAL_MS = 60_000;
47
33
 
48
34
  interface ClientEntry {
49
35
  client: CopilotClient;
@@ -54,6 +40,50 @@ interface ClientEntry {
54
40
  sessions: Map<string, CopilotSession>;
55
41
  }
56
42
 
43
+ /**
44
+ * Generic timeout wrapper for SDK operations that can hang.
45
+ */
46
+ function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {
47
+ return new Promise<T>((resolve, reject) => {
48
+ const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
49
+ promise.then(
50
+ (v) => { clearTimeout(timer); resolve(v); },
51
+ (e) => { clearTimeout(timer); reject(e); },
52
+ );
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Retry-with-backoff wrapper for session.destroy(), matching the SDK's own
58
+ * 3-attempt exponential backoff pattern used in stop().
59
+ */
60
+ async function destroySessionWithRetry(
61
+ session: CopilotSession,
62
+ sessionId: string,
63
+ totalTimeoutMs: number,
64
+ ): Promise<void> {
65
+ return withTimeout(
66
+ (async () => {
67
+ const MAX_ATTEMPTS = 3;
68
+ let lastError: Error | null = null;
69
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
70
+ try {
71
+ await session.destroy();
72
+ return;
73
+ } catch (err) {
74
+ lastError = err instanceof Error ? err : new Error(String(err));
75
+ if (attempt < MAX_ATTEMPTS) {
76
+ await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt - 1)));
77
+ }
78
+ }
79
+ }
80
+ throw lastError!;
81
+ })(),
82
+ totalTimeoutMs,
83
+ `destroy session ${sessionId} (with retries)`,
84
+ );
85
+ }
86
+
57
87
  /**
58
88
  * Create a user input handler that forwards questions to the QuestionRegistry.
59
89
  * This enables SDK's ask_user tool to pause and wait for MCP client response.
@@ -75,14 +105,12 @@ function createUserInputHandler(taskId: string): (request: UserInputRequest, inv
75
105
  };
76
106
  }
77
107
 
78
- // Interval for periodic stale session cleanup (every 60 seconds)
79
- const STALE_SESSION_CLEANUP_INTERVAL_MS = 60_000;
80
-
81
108
  class SDKClientManager {
82
109
  private clients: Map<string, ClientEntry> = new Map();
83
110
  private pendingClients: Map<string, Promise<CopilotClient>> = new Map(); // RC-3: Dedup concurrent getClient calls
84
111
  private isShuttingDown = false;
85
- private staleSessionTimer: NodeJS.Timeout | null = null;
112
+ private staleSessionTimer: ReturnType<typeof setInterval> | null = null;
113
+ private sweepCycle = 0;
86
114
 
87
115
  /**
88
116
  * Initialize the client manager (call on MCP connect).
@@ -94,82 +122,27 @@ class SDKClientManager {
94
122
  console.error(`[sdk-client-manager] Initialized with ${accountManager.getTokenCount()} account(s)`);
95
123
  }
96
124
 
97
- /**
98
- * Start periodic sweeper that detects and destroys orphaned sessions.
99
- * A session is orphaned if it's tracked in a client entry but its corresponding
100
- * task is in a terminal state or no longer exists.
101
- */
102
- private startStaleSessionSweeper(): void {
103
- if (this.staleSessionTimer) {
104
- clearInterval(this.staleSessionTimer);
105
- }
106
- this.staleSessionTimer = setInterval(() => {
107
- this.sweepStaleSessions();
108
- }, STALE_SESSION_CLEANUP_INTERVAL_MS);
109
- this.staleSessionTimer.unref();
110
- }
111
-
112
- /**
113
- * Sweep and destroy orphaned sessions across all client entries.
114
- * This is the safety net that catches sessions missed by normal lifecycle cleanup.
115
- */
116
- private sweepStaleSessions(): void {
117
- if (this.isShuttingDown) return;
118
-
119
- let destroyed = 0;
120
- for (const [clientKey, entry] of this.clients) {
121
- for (const [sessionId, session] of entry.sessions) {
122
- // Check if any active (non-terminal) task references this session
123
- const task = taskManager.getTask(sessionId); // sessionId === taskId by convention
124
- const isOrphaned = !task || TERMINAL_STATUSES.has(task.status);
125
-
126
- if (isOrphaned) {
127
- // Remove from tracking first, then fire-and-forget destroy with timeout
128
- entry.sessions.delete(sessionId);
129
- withTimeout(session.destroy(), DESTROY_SESSION_TIMEOUT_MS, `sweeper:destroy(${sessionId})`).catch((err) => {
130
- console.error(`[sdk-client-manager] Sweeper: failed to destroy session ${sessionId}:`, err);
131
- });
132
- destroyed++;
133
- }
134
- }
135
- }
136
-
137
- if (destroyed > 0) {
138
- console.error(`[sdk-client-manager] Sweeper: destroyed ${destroyed} orphaned session(s)`);
139
- // Now that sessions are cleared, try to clean up stale clients too
140
- this.cleanupStaleClients();
141
- }
142
- }
143
-
144
125
  /**
145
126
  * Reset the client manager (call on MCP reconnect).
146
127
  * Clears all clients and resets account rotation to first token.
147
128
  */
148
129
  async reset(): Promise<void> {
149
- // Invalidate any in-flight client creations before stopping existing clients
150
130
  this.pendingClients.clear();
151
131
 
152
- // Destroy all sessions first, then stop clients
153
- for (const [key, entry] of this.clients) {
154
- // Destroy sessions in parallel to release PTY FDs (with timeout so hung sessions don't block reset)
155
- await Promise.allSettled(
156
- [...entry.sessions.entries()].map(([sessionId, session]) =>
157
- withTimeout(session.destroy(), DESTROY_SESSION_TIMEOUT_MS, `reset:destroy(${sessionId})`)
158
- .catch(err => console.error(`[sdk-client-manager] Error destroying session ${sessionId} during reset:`, err))
159
- )
160
- );
132
+ // Clear our tracking first let SDK stop() handle session cleanup
133
+ for (const entry of this.clients.values()) {
161
134
  entry.sessions.clear();
135
+ }
162
136
 
163
- // Then stop the client
137
+ for (const [key, entry] of this.clients) {
164
138
  try {
165
- await entry.client.stop();
166
- } catch (err) {
167
- console.error(`[sdk-client-manager] Error stopping client ${key}:`, err);
139
+ await withTimeout(entry.client.stop(), SHUTDOWN_STOP_TIMEOUT_MS, `stop client ${key}`);
140
+ } catch {
141
+ try { await entry.client.forceStop(); } catch { /* ignore */ }
168
142
  }
169
143
  }
170
144
  this.clients.clear();
171
145
 
172
- // Reset account manager to first token
173
146
  accountManager.reset();
174
147
  console.error('[sdk-client-manager] Reset complete - all clients cleared, starting from first account');
175
148
  }
@@ -236,7 +209,8 @@ class SDKClientManager {
236
209
  cwd,
237
210
  logLevel: 'error',
238
211
  autoStart: true,
239
- autoRestart: false, // RC-4 fix: Disable auto-restart to prevent unbounded CLI respawning and PTY accumulation
212
+ autoRestart: false,
213
+ useStdio: false, // TCP mode: avoids macOS stdio pipe destruction race
240
214
  };
241
215
 
242
216
  if (githubToken) {
@@ -344,10 +318,10 @@ class SDKClientManager {
344
318
  for (const entry of this.clients.values()) {
345
319
  const session = entry.sessions.get(sessionId);
346
320
  if (session) {
347
- // Remove from tracking first to prevent double-destroy from sweeper
321
+ // Delete from tracking first to prevent double-destroy race with sweeper
348
322
  entry.sessions.delete(sessionId);
349
323
  try {
350
- await withTimeout(session.destroy(), DESTROY_SESSION_TIMEOUT_MS, `destroySession(${sessionId})`);
324
+ await destroySessionWithRetry(session, sessionId, DESTROY_SESSION_TIMEOUT_MS);
351
325
  } catch (err) {
352
326
  console.error(`[sdk-client-manager] Failed to destroy session ${sessionId}:`, err);
353
327
  }
@@ -426,6 +400,157 @@ class SDKClientManager {
426
400
  }
427
401
  }
428
402
 
403
+ /**
404
+ * Start the periodic stale-session sweeper. Timer is unref()'d so it
405
+ * doesn't prevent the process from exiting.
406
+ */
407
+ private startStaleSessionSweeper(): void {
408
+ if (this.staleSessionTimer) return;
409
+ this.staleSessionTimer = setInterval(() => {
410
+ this.sweepStaleSessions().catch((err) => {
411
+ console.error('[sdk-client-manager] Sweeper error:', err);
412
+ });
413
+ }, STALE_SESSION_CLEANUP_INTERVAL_MS);
414
+ this.staleSessionTimer.unref();
415
+ console.error('[sdk-client-manager] Stale-session sweeper started');
416
+ }
417
+
418
+ /**
419
+ * Ping each client to detect dead/crashed CLI processes. Removes dead
420
+ * clients from the pool and force-stops them.
421
+ */
422
+ private async checkClientHealth(): Promise<void> {
423
+ for (const [key, entry] of this.clients.entries()) {
424
+ try {
425
+ await withTimeout(
426
+ entry.client.ping('health'),
427
+ CLIENT_HEALTH_CHECK_TIMEOUT_MS,
428
+ `ping client ${key}`,
429
+ );
430
+ } catch {
431
+ console.error(`[sdk-client-manager] Health check failed for client ${key}, removing dead client`);
432
+ entry.sessions.clear();
433
+ this.clients.delete(key);
434
+ try { await entry.client.forceStop(); } catch { /* already dead */ }
435
+ }
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Sweep all tracked sessions. If the corresponding task (looked up by
441
+ * sessionId which equals taskId by convention) is terminal or missing,
442
+ * destroy the session with a timeout. Then recycle PTY leakers.
443
+ */
444
+ private async sweepStaleSessions(): Promise<void> {
445
+ // Remove dead clients first so we don't try to destroy sessions on them
446
+ await this.checkClientHealth();
447
+
448
+ let destroyed = 0;
449
+ for (const entry of this.clients.values()) {
450
+ for (const [sessionId, session] of entry.sessions) {
451
+ const task = taskManager.getTask(sessionId);
452
+ if (!task || TERMINAL_STATUSES.has(task.status as any)) {
453
+ entry.sessions.delete(sessionId);
454
+ try {
455
+ await withTimeout(
456
+ entry.client.deleteSession(sessionId),
457
+ DESTROY_SESSION_TIMEOUT_MS,
458
+ `delete session ${sessionId}`,
459
+ );
460
+ } catch {
461
+ // Fall back to local destroy if deleteSession fails
462
+ try {
463
+ await destroySessionWithRetry(session, sessionId, DESTROY_SESSION_TIMEOUT_MS);
464
+ } catch (err) {
465
+ console.error(`[sdk-client-manager] Sweeper: destroy failed for ${sessionId}: ${err}`);
466
+ }
467
+ }
468
+ destroyed++;
469
+ }
470
+ }
471
+ }
472
+ if (destroyed > 0) {
473
+ console.error(`[sdk-client-manager] Sweeper: destroyed ${destroyed} stale session(s)`);
474
+ }
475
+
476
+ // Every 5th sweep cycle, detect and clean up orphaned server-side sessions
477
+ this.sweepCycle++;
478
+ if (this.sweepCycle % 5 === 0) {
479
+ await this.detectOrphanedSessions();
480
+ }
481
+
482
+ await this.recyclePtyLeakers();
483
+ }
484
+
485
+ /**
486
+ * Detect server-side sessions that we're not tracking locally (orphaned from
487
+ * crashes). Uses listSessions() and deletes any not in our tracking map.
488
+ */
489
+ private async detectOrphanedSessions(): Promise<void> {
490
+ for (const [key, entry] of this.clients.entries()) {
491
+ try {
492
+ const serverSessions = await withTimeout(
493
+ entry.client.listSessions(),
494
+ CLIENT_HEALTH_CHECK_TIMEOUT_MS,
495
+ `listSessions ${key}`,
496
+ );
497
+ const trackedIds = new Set(entry.sessions.keys());
498
+ for (const meta of serverSessions) {
499
+ if (!trackedIds.has(meta.sessionId)) {
500
+ console.error(`[sdk-client-manager] Sweeper: deleting orphaned session ${meta.sessionId}`);
501
+ try { await entry.client.deleteSession(meta.sessionId); } catch { /* best effort */ }
502
+ }
503
+ }
504
+ } catch { /* listSessions failure is non-fatal */ }
505
+ }
506
+ }
507
+
508
+ /**
509
+ * For each client entry, check PTY FD count via lsof. If count exceeds
510
+ * threshold and no active sessions remain, recycle the client (stop it
511
+ * and remove from map so the next getClient() creates a fresh one).
512
+ */
513
+ private async recyclePtyLeakers(): Promise<void> {
514
+ for (const [key, entry] of this.clients.entries()) {
515
+ // Skip dead clients — health check will handle cleanup
516
+ try {
517
+ await withTimeout(entry.client.ping('recycle-check'), 3_000, `ping ${key}`);
518
+ } catch {
519
+ continue;
520
+ }
521
+
522
+ // SDK has no public API for process PID or FD count.
523
+ // cliProcess is private (client.ts:121). This cast is the only way
524
+ // to count ptmx FDs until the SDK adds a resource health endpoint.
525
+ const pid = (entry.client as any).cliProcess?.pid as number | undefined;
526
+ if (!pid) continue;
527
+
528
+ let ptmxCount = 0;
529
+ try {
530
+ const output = execSync(`lsof -p ${pid} 2>/dev/null | grep -c ptmx`, {
531
+ encoding: 'utf8',
532
+ timeout: 5_000,
533
+ });
534
+ ptmxCount = parseInt(output.trim(), 10) || 0;
535
+ } catch {
536
+ // grep returns exit code 1 when no matches → ptmxCount stays 0
537
+ continue;
538
+ }
539
+
540
+ if (ptmxCount > PTY_RECYCLE_THRESHOLD && entry.sessions.size === 0) {
541
+ console.error(`[sdk-client-manager] Sweeper: recycling client ${key} (${ptmxCount} ptmx FDs, 0 active sessions)`);
542
+ // Escalating shutdown: graceful → force
543
+ try {
544
+ await withTimeout(entry.client.stop(), RECYCLE_STOP_TIMEOUT_MS, `stop client ${key}`);
545
+ } catch (stopErr) {
546
+ console.error(`[sdk-client-manager] Sweeper: graceful stop failed for ${key}, forcing: ${stopErr}`);
547
+ try { await entry.client.forceStop(); } catch { /* ignore */ }
548
+ }
549
+ this.clients.delete(key);
550
+ }
551
+ }
552
+ }
553
+
429
554
  /**
430
555
  * Check if rotation should happen based on error.
431
556
  */
@@ -472,7 +597,6 @@ class SDKClientManager {
472
597
  this.isShuttingDown = true;
473
598
  this.pendingClients.clear();
474
599
 
475
- // Stop the stale session sweeper
476
600
  if (this.staleSessionTimer) {
477
601
  clearInterval(this.staleSessionTimer);
478
602
  this.staleSessionTimer = null;
@@ -480,31 +604,25 @@ class SDKClientManager {
480
604
 
481
605
  const errors: Error[] = [];
482
606
 
483
- for (const [key, entry] of this.clients) {
484
- // Destroy all sessions in parallel (with timeout so one hung session doesn't block shutdown)
485
- const results = await Promise.allSettled(
486
- [...entry.sessions.entries()].map(([sessionId, session]) =>
487
- withTimeout(session.destroy(), SHUTDOWN_DESTROY_TIMEOUT_MS, `shutdown:destroy(${sessionId})`)
488
- )
489
- );
490
- for (const r of results) {
491
- if (r.status === 'rejected') {
492
- errors.push(r.reason instanceof Error ? r.reason : new Error(String(r.reason)));
493
- }
494
- }
607
+ // Clear our tracking first (prevents sweeper interference)
608
+ for (const entry of this.clients.values()) {
495
609
  entry.sessions.clear();
610
+ }
496
611
 
497
- // Stop the client
612
+ // Let SDK stop() handle cleanup with its own retry+backoff, with forceStop fallback
613
+ for (const [key, entry] of this.clients) {
498
614
  try {
499
- const clientErrors = await entry.client.stop();
615
+ const clientErrors = await withTimeout(entry.client.stop(), SHUTDOWN_STOP_TIMEOUT_MS, `stop client ${key}`);
500
616
  errors.push(...clientErrors);
501
617
  } catch (err) {
502
- errors.push(new Error(`Failed to stop client for ${key}: ${err}`));
618
+ console.error(`[sdk-client-manager] Shutdown: stop timed out for ${key}, forcing`);
619
+ try { await entry.client.forceStop(); } catch (e) {
620
+ errors.push(new Error(`Failed to force-stop client ${key}: ${e}`));
621
+ }
503
622
  }
504
623
  }
505
624
 
506
625
  this.clients.clear();
507
-
508
626
  if (errors.length > 0) {
509
627
  console.error('[sdk-client-manager] Shutdown errors:', errors);
510
628
  }