@agent-relay/wrapper 2.0.13 → 2.0.15

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.
@@ -16,7 +16,8 @@
16
16
  * @see docs/RUST_WRAPPER_DESIGN.md for protocol details
17
17
  */
18
18
  import { BaseWrapper, type BaseWrapperConfig } from './base-wrapper.js';
19
- import type { SendPayload, SendMeta } from '@agent-relay/protocol/types';
19
+ import type { SendPayload, SendMeta, Envelope } from '@agent-relay/protocol/types';
20
+ import type { ChannelMessagePayload } from '@agent-relay/protocol/channels';
20
21
  interface StatusResponse {
21
22
  type: 'status';
22
23
  agent_idle: boolean;
@@ -92,6 +93,7 @@ export declare class RelayPtyOrchestrator extends BaseWrapper {
92
93
  private lastParsedLength;
93
94
  private isInteractive;
94
95
  private pendingInjections;
96
+ private pendingSendEnter;
95
97
  private backpressureActive;
96
98
  private readyForMessages;
97
99
  private throttle;
@@ -116,10 +118,12 @@ export declare class RelayPtyOrchestrator extends BaseWrapper {
116
118
  constructor(config: RelayPtyOrchestratorConfig);
117
119
  /**
118
120
  * Debug log - only outputs when debug is enabled
121
+ * Writes to log file to avoid polluting TUI output
119
122
  */
120
123
  private log;
121
124
  /**
122
125
  * Error log - always outputs (errors are important)
126
+ * Writes to log file to avoid polluting TUI output
123
127
  */
124
128
  private logError;
125
129
  /**
@@ -210,6 +214,14 @@ export declare class RelayPtyOrchestrator extends BaseWrapper {
210
214
  * Disconnect from socket
211
215
  */
212
216
  private disconnectSocket;
217
+ /** Timer for socket reconnection */
218
+ private socketReconnectTimer?;
219
+ /** Current reconnection attempt count */
220
+ private socketReconnectAttempt;
221
+ /**
222
+ * Schedule a socket reconnection attempt with exponential backoff
223
+ */
224
+ private scheduleSocketReconnect;
213
225
  /**
214
226
  * Send a request to the socket and optionally wait for response
215
227
  */
@@ -224,6 +236,15 @@ export declare class RelayPtyOrchestrator extends BaseWrapper {
224
236
  * If verification fails, retries up to MAX_RETRIES times.
225
237
  */
226
238
  private handleInjectResult;
239
+ /**
240
+ * Handle SendEnter result (stuck input recovery)
241
+ * Called when relay-pty responds to a SendEnter request
242
+ */
243
+ private handleSendEnterResult;
244
+ /**
245
+ * Do a full retry with message content (used when SendEnter fails or for subsequent retries)
246
+ */
247
+ private doFullRetry;
227
248
  /**
228
249
  * Handle backpressure notification
229
250
  */
@@ -240,6 +261,12 @@ export declare class RelayPtyOrchestrator extends BaseWrapper {
240
261
  * Override handleIncomingMessage to trigger queue processing
241
262
  */
242
263
  protected handleIncomingMessage(from: string, payload: SendPayload, messageId: string, meta?: SendMeta, originalTo?: string): void;
264
+ /**
265
+ * Override handleIncomingChannelMessage to trigger queue processing.
266
+ * Without this override, channel messages would be queued but processMessageQueue()
267
+ * would never be called, causing messages to get stuck until the queue monitor runs.
268
+ */
269
+ protected handleIncomingChannelMessage(from: string, channel: string, body: string, envelope: Envelope<ChannelMessagePayload>): void;
243
270
  /**
244
271
  * Start the queue monitor to periodically check for stuck messages.
245
272
  * This ensures messages don't get orphaned in the queue when the agent is idle.
@@ -1 +1 @@
1
- {"version":3,"file":"relay-pty-orchestrator.d.ts","sourceRoot":"","sources":["../src/relay-pty-orchestrator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relay-pty-orchestrator.d.ts","sourceRoot":"","sources":["../src/relay-pty-orchestrator.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ import { spawn } from 'node:child_process';
19
19
  import { createConnection } from 'node:net';
20
20
  import { createHash } from 'node:crypto';
21
21
  import { join, dirname } from 'node:path';
22
- import { existsSync, unlinkSync, mkdirSync, symlinkSync, lstatSync, rmSync, watch, readdirSync, readlinkSync } from 'node:fs';
22
+ import { existsSync, unlinkSync, mkdirSync, symlinkSync, lstatSync, rmSync, watch, readdirSync, readlinkSync, writeFileSync, appendFileSync } from 'node:fs';
23
23
  import { getProjectPaths } from '@agent-relay/config/project-namespace';
24
24
  import { fileURLToPath } from 'node:url';
25
25
  // Get the directory where this module is located
@@ -63,6 +63,8 @@ export class RelayPtyOrchestrator extends BaseWrapper {
63
63
  isInteractive = false;
64
64
  // Injection state
65
65
  pendingInjections = new Map();
66
+ // Pending SendEnter requests (for stuck input recovery)
67
+ pendingSendEnter = new Map();
66
68
  backpressureActive = false;
67
69
  readyForMessages = false;
68
70
  // Adaptive throttle for message queue - adjusts delay based on success/failure
@@ -168,17 +170,39 @@ export class RelayPtyOrchestrator extends BaseWrapper {
168
170
  }
169
171
  /**
170
172
  * Debug log - only outputs when debug is enabled
173
+ * Writes to log file to avoid polluting TUI output
171
174
  */
172
175
  log(message) {
173
176
  if (this.config.debug) {
174
- console.log(`[relay-pty-orchestrator:${this.config.name}] ${message}`);
177
+ const logLine = `${new Date().toISOString()} [relay-pty-orchestrator:${this.config.name}] ${message}\n`;
178
+ try {
179
+ const logDir = dirname(this._logPath);
180
+ if (!existsSync(logDir)) {
181
+ mkdirSync(logDir, { recursive: true });
182
+ }
183
+ appendFileSync(this._logPath, logLine);
184
+ }
185
+ catch {
186
+ // Fallback to stderr if file write fails (only during init before _logPath is set)
187
+ }
175
188
  }
176
189
  }
177
190
  /**
178
191
  * Error log - always outputs (errors are important)
192
+ * Writes to log file to avoid polluting TUI output
179
193
  */
180
194
  logError(message) {
181
- console.error(`[relay-pty-orchestrator:${this.config.name}] ERROR: ${message}`);
195
+ const logLine = `${new Date().toISOString()} [relay-pty-orchestrator:${this.config.name}] ERROR: ${message}\n`;
196
+ try {
197
+ const logDir = dirname(this._logPath);
198
+ if (!existsSync(logDir)) {
199
+ mkdirSync(logDir, { recursive: true });
200
+ }
201
+ appendFileSync(this._logPath, logLine);
202
+ }
203
+ catch {
204
+ // Fallback to stderr if file write fails (only during init before _logPath is set)
205
+ }
182
206
  }
183
207
  /**
184
208
  * Get the outbox path for this agent (for documentation purposes)
@@ -336,6 +360,25 @@ export class RelayPtyOrchestrator extends BaseWrapper {
336
360
  catch (err) {
337
361
  this.logError(` Failed to set up outbox: ${err.message}`);
338
362
  }
363
+ // Write MCP identity file so MCP servers can discover their agent name
364
+ // This is needed because Claude Code may not pass through env vars to MCP server processes
365
+ try {
366
+ const projectPaths = getProjectPaths(this.config.cwd);
367
+ const identityDir = join(projectPaths.dataDir);
368
+ if (!existsSync(identityDir)) {
369
+ mkdirSync(identityDir, { recursive: true });
370
+ }
371
+ // Write a per-process identity file (using PPID so MCP server finds parent's identity)
372
+ const identityPath = join(identityDir, `mcp-identity-${process.pid}`);
373
+ writeFileSync(identityPath, this.config.name, 'utf-8');
374
+ this.log(` Wrote MCP identity file: ${identityPath}`);
375
+ // Also write a simple identity file (for single-agent scenarios)
376
+ const simpleIdentityPath = join(identityDir, 'mcp-identity');
377
+ writeFileSync(simpleIdentityPath, this.config.name, 'utf-8');
378
+ }
379
+ catch (err) {
380
+ this.logError(` Failed to write MCP identity file: ${err.message}`);
381
+ }
339
382
  // Find relay-pty binary
340
383
  const binaryPath = this.findRelayPtyBinary();
341
384
  if (!binaryPath) {
@@ -378,6 +421,11 @@ export class RelayPtyOrchestrator extends BaseWrapper {
378
421
  this.stopQueueMonitor();
379
422
  this.stopProtocolMonitor();
380
423
  this.stopPeriodicReminder();
424
+ // Clear socket reconnect timer
425
+ if (this.socketReconnectTimer) {
426
+ clearTimeout(this.socketReconnectTimer);
427
+ this.socketReconnectTimer = undefined;
428
+ }
381
429
  // Unregister from memory monitor
382
430
  this.memoryMonitor.unregister(this.config.name);
383
431
  if (this.memoryAlertHandler) {
@@ -492,6 +540,7 @@ export class RelayPtyOrchestrator extends BaseWrapper {
492
540
  ...process.env,
493
541
  ...this.config.env,
494
542
  AGENT_RELAY_NAME: this.config.name,
543
+ RELAY_AGENT_NAME: this.config.name, // MCP server uses this env var
495
544
  AGENT_RELAY_OUTBOX: this._canonicalOutboxPath, // Agents use this for outbox path
496
545
  TERM: 'xterm-256color',
497
546
  },
@@ -907,6 +956,15 @@ export class RelayPtyOrchestrator extends BaseWrapper {
907
956
  */
908
957
  attemptSocketConnection(timeout) {
909
958
  return new Promise((resolve, reject) => {
959
+ // Clean up any existing socket before creating new one
960
+ // This prevents orphaned sockets with stale event handlers
961
+ if (this.socket) {
962
+ // Remove all listeners to prevent the old socket's 'close' event
963
+ // from triggering another reconnect cycle
964
+ this.socket.removeAllListeners();
965
+ this.socket.destroy();
966
+ this.socket = undefined;
967
+ }
910
968
  const timer = setTimeout(() => {
911
969
  reject(new Error('Socket connection timeout'));
912
970
  }, timeout);
@@ -920,9 +978,18 @@ export class RelayPtyOrchestrator extends BaseWrapper {
920
978
  this.socketConnected = false;
921
979
  reject(err);
922
980
  });
981
+ // Handle 'end' event - server closed its write side (half-close)
982
+ this.socket.on('end', () => {
983
+ this.socketConnected = false;
984
+ this.log(` Socket received end (server closed write side)`);
985
+ });
923
986
  this.socket.on('close', () => {
924
987
  this.socketConnected = false;
925
988
  this.log(` Socket closed`);
989
+ // Auto-reconnect if not intentionally stopped
990
+ if (this.running && !this.isGracefulStop) {
991
+ this.scheduleSocketReconnect();
992
+ }
926
993
  });
927
994
  // Handle incoming data (responses)
928
995
  let buffer = '';
@@ -955,6 +1022,55 @@ export class RelayPtyOrchestrator extends BaseWrapper {
955
1022
  }
956
1023
  this.pendingInjections.clear();
957
1024
  }
1025
+ /** Timer for socket reconnection */
1026
+ socketReconnectTimer;
1027
+ /** Current reconnection attempt count */
1028
+ socketReconnectAttempt = 0;
1029
+ /**
1030
+ * Schedule a socket reconnection attempt with exponential backoff
1031
+ */
1032
+ scheduleSocketReconnect() {
1033
+ const maxAttempts = this.config.socketReconnectAttempts ?? 3;
1034
+ // Clear any existing timer
1035
+ if (this.socketReconnectTimer) {
1036
+ clearTimeout(this.socketReconnectTimer);
1037
+ this.socketReconnectTimer = undefined;
1038
+ }
1039
+ if (this.socketReconnectAttempt >= maxAttempts) {
1040
+ this.logError(` Socket reconnect failed after ${maxAttempts} attempts`);
1041
+ // Reset counter for future reconnects (processMessageQueue can trigger new cycle)
1042
+ this.socketReconnectAttempt = 0;
1043
+ // Note: socketReconnectTimer is already undefined, allowing processMessageQueue
1044
+ // to trigger a new reconnection cycle when new messages arrive
1045
+ return;
1046
+ }
1047
+ this.socketReconnectAttempt++;
1048
+ const delay = Math.min(1000 * Math.pow(2, this.socketReconnectAttempt - 1), 10000); // Max 10s
1049
+ this.log(` Scheduling socket reconnect in ${delay}ms (attempt ${this.socketReconnectAttempt}/${maxAttempts})`);
1050
+ this.socketReconnectTimer = setTimeout(async () => {
1051
+ // Clear timer reference now that callback is executing
1052
+ this.socketReconnectTimer = undefined;
1053
+ if (!this.running || this.isGracefulStop) {
1054
+ return;
1055
+ }
1056
+ try {
1057
+ const timeout = this.config.socketConnectTimeoutMs ?? 5000;
1058
+ await this.attemptSocketConnection(timeout);
1059
+ this.log(` Socket reconnected successfully`);
1060
+ this.socketReconnectAttempt = 0; // Reset on success
1061
+ // Process any queued messages that were waiting
1062
+ if (this.messageQueue.length > 0 && !this.isInjecting) {
1063
+ this.log(` Processing ${this.messageQueue.length} queued messages after reconnect`);
1064
+ this.processMessageQueue();
1065
+ }
1066
+ }
1067
+ catch (err) {
1068
+ this.logError(` Socket reconnect attempt ${this.socketReconnectAttempt} failed: ${err.message}`);
1069
+ // Schedule another attempt
1070
+ this.scheduleSocketReconnect();
1071
+ }
1072
+ }, delay);
1073
+ }
958
1074
  /**
959
1075
  * Send a request to the socket and optionally wait for response
960
1076
  */
@@ -1002,6 +1118,12 @@ export class RelayPtyOrchestrator extends BaseWrapper {
1002
1118
  case 'shutdown_ack':
1003
1119
  this.log(` Shutdown acknowledged`);
1004
1120
  break;
1121
+ case 'send_enter_result':
1122
+ // Handle SendEnter result (stuck input recovery)
1123
+ this.handleSendEnterResult(response).catch((err) => {
1124
+ this.logError(` Error handling send_enter result: ${err.message}`);
1125
+ });
1126
+ break;
1005
1127
  }
1006
1128
  }
1007
1129
  catch (err) {
@@ -1084,7 +1206,6 @@ export class RelayPtyOrchestrator extends BaseWrapper {
1084
1206
  this.log(` Message ${pending.shortId} NOT found in output after delivery`);
1085
1207
  // Check if we should retry
1086
1208
  if (pending.retryCount < INJECTION_CONSTANTS.MAX_RETRIES - 1) {
1087
- this.log(` Retrying injection (attempt ${pending.retryCount + 2}/${INJECTION_CONSTANTS.MAX_RETRIES})`);
1088
1209
  clearTimeout(pending.timeout);
1089
1210
  this.pendingInjections.delete(response.id);
1090
1211
  // Wait before retry with backoff
@@ -1104,37 +1225,54 @@ export class RelayPtyOrchestrator extends BaseWrapper {
1104
1225
  pending.resolve(true);
1105
1226
  return;
1106
1227
  }
1107
- // Re-inject by sending another socket request
1108
- // The original promise will be resolved when this retry completes
1109
- // Prepend [RETRY] to help agent notice this is a retry
1110
- const retryBody = pending.originalBody.startsWith('[RETRY]')
1111
- ? pending.originalBody
1112
- : `[RETRY] ${pending.originalBody}`;
1113
- const retryRequest = {
1114
- type: 'inject',
1115
- id: response.id,
1116
- from: pending.from,
1117
- body: retryBody,
1118
- priority: 1, // Higher priority for retries
1119
- };
1120
- // Create new pending entry with incremented retry count
1121
- const newTimeout = setTimeout(() => {
1122
- this.logError(` Retry timeout for ${pending.shortId}`);
1123
- this.pendingInjections.delete(response.id);
1124
- pending.resolve(false);
1125
- }, 30000);
1126
- this.pendingInjections.set(response.id, {
1127
- ...pending,
1128
- timeout: newTimeout,
1129
- retryCount: pending.retryCount + 1,
1130
- originalBody: retryBody, // Use retry body for subsequent retries
1131
- });
1132
- this.sendSocketRequest(retryRequest).catch((err) => {
1133
- this.logError(` Retry request failed: ${err.message}`);
1134
- clearTimeout(newTimeout);
1135
- this.pendingInjections.delete(response.id);
1136
- pending.resolve(false);
1137
- });
1228
+ // On first retry attempt (retryCount === 0), try SendEnter first
1229
+ // This handles the case where message content was written but Enter wasn't processed
1230
+ if (pending.retryCount === 0) {
1231
+ this.log(` Trying SendEnter first for ${pending.shortId} (stuck input recovery)`);
1232
+ // Send just the Enter key
1233
+ const sendEnterRequest = {
1234
+ type: 'send_enter',
1235
+ id: response.id,
1236
+ };
1237
+ // Track this SendEnter request for verification
1238
+ const sendEnterTimeout = setTimeout(() => {
1239
+ this.logError(` SendEnter timeout for ${pending.shortId}`);
1240
+ this.pendingSendEnter.delete(response.id);
1241
+ // Fall back to full retry after SendEnter timeout
1242
+ this.doFullRetry(response.id, pending);
1243
+ }, 5000); // 5 second timeout for SendEnter
1244
+ this.pendingSendEnter.set(response.id, {
1245
+ resolve: (verified) => {
1246
+ if (verified) {
1247
+ // SendEnter worked!
1248
+ this.injectionMetrics.successWithRetry++;
1249
+ this.injectionMetrics.total++;
1250
+ pending.resolve(true);
1251
+ }
1252
+ else {
1253
+ // SendEnter didn't work, do full retry
1254
+ this.doFullRetry(response.id, pending);
1255
+ }
1256
+ },
1257
+ timeout: sendEnterTimeout,
1258
+ from: pending.from,
1259
+ shortId: pending.shortId,
1260
+ retryCount: pending.retryCount,
1261
+ originalBody: pending.originalBody,
1262
+ originalResolve: pending.resolve,
1263
+ });
1264
+ this.sendSocketRequest(sendEnterRequest).catch((err) => {
1265
+ this.logError(` SendEnter request failed: ${err.message}`);
1266
+ clearTimeout(sendEnterTimeout);
1267
+ this.pendingSendEnter.delete(response.id);
1268
+ // Fall back to full retry
1269
+ this.doFullRetry(response.id, pending);
1270
+ });
1271
+ }
1272
+ else {
1273
+ // On subsequent retries (retryCount > 0), do full retry directly
1274
+ this.doFullRetry(response.id, pending);
1275
+ }
1138
1276
  }
1139
1277
  else {
1140
1278
  // Max retries exceeded
@@ -1167,6 +1305,77 @@ export class RelayPtyOrchestrator extends BaseWrapper {
1167
1305
  }
1168
1306
  // queued/injecting are intermediate states - wait for final status
1169
1307
  }
1308
+ /**
1309
+ * Handle SendEnter result (stuck input recovery)
1310
+ * Called when relay-pty responds to a SendEnter request
1311
+ */
1312
+ async handleSendEnterResult(response) {
1313
+ this.log(` handleSendEnterResult: id=${response.id.substring(0, 8)} success=${response.success}`);
1314
+ const pendingEnter = this.pendingSendEnter.get(response.id);
1315
+ if (!pendingEnter) {
1316
+ this.log(` No pending SendEnter found for ${response.id.substring(0, 8)}`);
1317
+ return;
1318
+ }
1319
+ clearTimeout(pendingEnter.timeout);
1320
+ this.pendingSendEnter.delete(response.id);
1321
+ if (!response.success) {
1322
+ this.log(` SendEnter failed for ${pendingEnter.shortId}, will try full retry`);
1323
+ pendingEnter.resolve(false);
1324
+ return;
1325
+ }
1326
+ // SendEnter succeeded - wait and verify
1327
+ this.log(` SendEnter sent for ${pendingEnter.shortId}, waiting to verify...`);
1328
+ await sleep(150); // Give time for Enter to be processed
1329
+ // Verify the message appeared in output
1330
+ const verified = await verifyInjection(pendingEnter.shortId, pendingEnter.from, async () => this.getCleanOutput());
1331
+ if (verified) {
1332
+ this.log(` Message ${pendingEnter.shortId} verified after SendEnter ✓`);
1333
+ pendingEnter.resolve(true);
1334
+ }
1335
+ else {
1336
+ this.log(` Message ${pendingEnter.shortId} still not verified after SendEnter, will try full retry`);
1337
+ pendingEnter.resolve(false);
1338
+ }
1339
+ }
1340
+ /**
1341
+ * Do a full retry with message content (used when SendEnter fails or for subsequent retries)
1342
+ */
1343
+ doFullRetry(messageId, pending) {
1344
+ this.log(` Doing full retry for ${pending.shortId} (attempt ${pending.retryCount + 2}/${INJECTION_CONSTANTS.MAX_RETRIES})`);
1345
+ // Re-inject by sending another socket request
1346
+ // Prepend [RETRY] to help agent notice this is a retry
1347
+ const retryBody = pending.originalBody.startsWith('[RETRY]')
1348
+ ? pending.originalBody
1349
+ : `[RETRY] ${pending.originalBody}`;
1350
+ const retryRequest = {
1351
+ type: 'inject',
1352
+ id: messageId,
1353
+ from: pending.from,
1354
+ body: retryBody,
1355
+ priority: 1, // Higher priority for retries
1356
+ };
1357
+ // Create new pending entry with incremented retry count
1358
+ const newTimeout = setTimeout(() => {
1359
+ this.logError(` Retry timeout for ${pending.shortId}`);
1360
+ this.pendingInjections.delete(messageId);
1361
+ pending.resolve(false);
1362
+ }, 30000);
1363
+ this.pendingInjections.set(messageId, {
1364
+ resolve: pending.resolve,
1365
+ reject: pending.reject,
1366
+ timeout: newTimeout,
1367
+ from: pending.from,
1368
+ shortId: pending.shortId,
1369
+ retryCount: pending.retryCount + 1,
1370
+ originalBody: retryBody,
1371
+ });
1372
+ this.sendSocketRequest(retryRequest).catch((err) => {
1373
+ this.logError(` Full retry request failed: ${err.message}`);
1374
+ clearTimeout(newTimeout);
1375
+ this.pendingInjections.delete(messageId);
1376
+ pending.resolve(false);
1377
+ });
1378
+ }
1170
1379
  /**
1171
1380
  * Handle backpressure notification
1172
1381
  */
@@ -1240,12 +1449,42 @@ export class RelayPtyOrchestrator extends BaseWrapper {
1240
1449
  * Process queued messages
1241
1450
  */
1242
1451
  async processMessageQueue() {
1243
- if (!this.readyForMessages || this.backpressureActive || this.isInjecting) {
1244
- return;
1452
+ // Debug: Log blocking conditions when queue has messages
1453
+ if (this.messageQueue.length > 0) {
1454
+ if (!this.readyForMessages) {
1455
+ this.log(` Queue blocked: readyForMessages=false (queue=${this.messageQueue.length})`);
1456
+ return;
1457
+ }
1458
+ if (this.backpressureActive) {
1459
+ this.log(` Queue blocked: backpressure active (queue=${this.messageQueue.length})`);
1460
+ return;
1461
+ }
1462
+ if (this.isInjecting) {
1463
+ // Already injecting - the finally block will process next message
1464
+ // But add a safety timeout in case injection gets stuck
1465
+ const elapsed = this.injectionStartTime > 0 ? Date.now() - this.injectionStartTime : 0;
1466
+ if (elapsed > 35000) {
1467
+ this.logError(` Injection stuck for ${elapsed}ms, forcing reset`);
1468
+ this.isInjecting = false;
1469
+ this.injectionStartTime = 0;
1470
+ }
1471
+ return;
1472
+ }
1245
1473
  }
1246
1474
  if (this.messageQueue.length === 0) {
1247
1475
  return;
1248
1476
  }
1477
+ // Proactively reconnect socket if disconnected and we have messages to send
1478
+ if (!this.socketConnected && !this.socketReconnectTimer) {
1479
+ this.log(` Socket disconnected, triggering reconnect before processing queue`);
1480
+ this.scheduleSocketReconnect();
1481
+ return; // Wait for reconnection to complete
1482
+ }
1483
+ if (!this.socketConnected) {
1484
+ // Reconnection in progress, wait for it
1485
+ this.log(` Queue waiting: socket reconnecting (queue=${this.messageQueue.length})`);
1486
+ return;
1487
+ }
1249
1488
  // Check if agent is in editor mode - delay injection if so
1250
1489
  const idleResult = this.idleDetector.checkIdle();
1251
1490
  if (idleResult.inEditorMode) {
@@ -1304,6 +1543,18 @@ export class RelayPtyOrchestrator extends BaseWrapper {
1304
1543
  this.log(` Queue length after add: ${this.messageQueue.length}`);
1305
1544
  this.processMessageQueue();
1306
1545
  }
1546
+ /**
1547
+ * Override handleIncomingChannelMessage to trigger queue processing.
1548
+ * Without this override, channel messages would be queued but processMessageQueue()
1549
+ * would never be called, causing messages to get stuck until the queue monitor runs.
1550
+ */
1551
+ handleIncomingChannelMessage(from, channel, body, envelope) {
1552
+ this.log(` === CHANNEL MESSAGE RECEIVED: ${envelope.id.substring(0, 8)} from ${from} on ${channel} ===`);
1553
+ this.log(` Body preview: ${body?.substring(0, 100) ?? '(no body)'}...`);
1554
+ super.handleIncomingChannelMessage(from, channel, body, envelope);
1555
+ this.log(` Queue length after add: ${this.messageQueue.length}`);
1556
+ this.processMessageQueue();
1557
+ }
1307
1558
  // =========================================================================
1308
1559
  // Queue monitor - Detect and process stuck messages
1309
1560
  // =========================================================================
@@ -1901,6 +2152,10 @@ Then output: \`->relay-file:spawn\`
1901
2152
  */
1902
2153
  async kill() {
1903
2154
  this.isGracefulStop = true; // Mark as intentional to prevent crash broadcast
2155
+ if (this.socketReconnectTimer) {
2156
+ clearTimeout(this.socketReconnectTimer);
2157
+ this.socketReconnectTimer = undefined;
2158
+ }
1904
2159
  if (this.relayPtyProcess && !this.relayPtyProcess.killed) {
1905
2160
  this.relayPtyProcess.kill('SIGKILL');
1906
2161
  }