@rk0429/agentic-relay 0.8.0 → 0.9.1

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 (2) hide show
  1. package/dist/relay.mjs +161 -58
  2. package/package.json +1 -1
package/dist/relay.mjs CHANGED
@@ -18,6 +18,9 @@ function resolveLogLevel() {
18
18
  }
19
19
  return 3;
20
20
  }
21
+ function redirectToStderr() {
22
+ logger.options.stdout = process.stderr;
23
+ }
21
24
  var LOG_LEVEL_MAP, logger;
22
25
  var init_logger = __esm({
23
26
  "src/infrastructure/logger.ts"() {
@@ -307,6 +310,12 @@ function buildChildMcpServers(parentMcpServers, childHttpUrl) {
307
310
  }
308
311
  return result;
309
312
  }
313
+ function inferFailureReason(stderr, stdout) {
314
+ const combined = `${stderr} ${stdout}`.toLowerCase();
315
+ if (combined.includes("timed out") || combined.includes("timeout")) return "timeout";
316
+ if (combined.includes("max turns") || combined.includes("max_turns") || combined.includes("turn limit")) return "max_turns_exhausted";
317
+ return "adapter_error";
318
+ }
310
319
  function buildContextFromEnv() {
311
320
  const traceId = process.env["RELAY_TRACE_ID"] ?? `trace-${nanoid2()}`;
312
321
  const parentSessionId = process.env["RELAY_PARENT_SESSION_ID"] ?? null;
@@ -344,7 +353,8 @@ async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooks
344
353
  sessionId: "",
345
354
  exitCode: 1,
346
355
  stdout: "",
347
- stderr: `Spawn blocked: ${guardResult.reason}`
356
+ stderr: `Spawn blocked: ${guardResult.reason}`,
357
+ failureReason: "recursion_blocked"
348
358
  };
349
359
  }
350
360
  const adapter = registry2.get(effectiveBackend);
@@ -354,7 +364,8 @@ async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooks
354
364
  sessionId: "",
355
365
  exitCode: 1,
356
366
  stdout: "",
357
- stderr: `Backend "${effectiveBackend}" is not available. Use list_available_backends to see available options.`
367
+ stderr: `Backend "${effectiveBackend}" is not available. Use list_available_backends to see available options.`,
368
+ failureReason: "backend_unavailable"
358
369
  };
359
370
  }
360
371
  const spawnStartedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -446,7 +457,8 @@ ${wrapped}` : wrapped;
446
457
  sessionId: "",
447
458
  exitCode: 1,
448
459
  stdout: "",
449
- stderr: `Task instruction file not found: ${input.taskInstructionPath}`
460
+ stderr: `Task instruction file not found: ${input.taskInstructionPath}`,
461
+ failureReason: "instruction_file_error"
450
462
  };
451
463
  }
452
464
  const instructionContent = readFileSync(safePath, "utf-8");
@@ -459,7 +471,8 @@ ${input.prompt}`;
459
471
  sessionId: "",
460
472
  exitCode: 1,
461
473
  stdout: "",
462
- stderr: `Failed to read task instruction file: ${message}`
474
+ stderr: `Failed to read task instruction file: ${message}`,
475
+ failureReason: "instruction_file_error"
463
476
  };
464
477
  }
465
478
  }
@@ -473,7 +486,8 @@ ${input.prompt}`;
473
486
  exitCode: 1,
474
487
  stdout: "",
475
488
  stderr: `Backend "${effectiveBackend}" does not support session continuation (continueSession).`,
476
- _noSession: true
489
+ _noSession: true,
490
+ _failureReason: "session_continuation_unsupported"
477
491
  };
478
492
  }
479
493
  return adapter.continueSession(input.resumeSessionId, effectivePrompt);
@@ -497,7 +511,8 @@ ${input.prompt}`;
497
511
  maxDepth: guard.getConfig().maxDepth,
498
512
  traceId: envContext.traceId
499
513
  },
500
- ...mcpServers ? { mcpServers } : {}
514
+ ...mcpServers ? { mcpServers } : {},
515
+ ...input.timeoutMs ? { timeoutMs: input.timeoutMs } : {}
501
516
  });
502
517
  }
503
518
  })();
@@ -517,7 +532,8 @@ ${input.prompt}`;
517
532
  sessionId: session.relaySessionId,
518
533
  exitCode: result.exitCode,
519
534
  stdout: result.stdout,
520
- stderr: result.stderr
535
+ stderr: result.stderr,
536
+ ..."_failureReason" in result ? { failureReason: result._failureReason } : {}
521
537
  };
522
538
  }
523
539
  onProgress?.({ stage: "executing", percent: 50 });
@@ -533,6 +549,7 @@ ${input.prompt}`;
533
549
  }
534
550
  guard.recordSpawn(context);
535
551
  const status = result.exitCode === 0 ? "completed" : "error";
552
+ const failureReason = result.exitCode !== 0 ? inferFailureReason(result.stderr, result.stdout) : void 0;
536
553
  await sessionManager2.update(session.relaySessionId, { status });
537
554
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
538
555
  const metadata = {
@@ -541,7 +558,8 @@ ${input.prompt}`;
541
558
  ...input.preferredBackend ? { requestedBackend: input.preferredBackend } : {},
542
559
  selectionReason,
543
560
  startedAt: spawnStartedAt,
544
- completedAt
561
+ completedAt,
562
+ ...result.tokenUsage ? { tokenUsage: result.tokenUsage } : {}
545
563
  };
546
564
  onProgress?.({ stage: "completed", percent: 100 });
547
565
  if (hooksEngine2) {
@@ -581,16 +599,19 @@ ${input.prompt}`;
581
599
  stdout: result.stdout,
582
600
  stderr: result.stderr,
583
601
  nativeSessionId: result.nativeSessionId,
584
- metadata
602
+ metadata,
603
+ ...failureReason ? { failureReason } : {}
585
604
  };
586
605
  } catch (error) {
587
606
  await sessionManager2.update(session.relaySessionId, { status: "error" });
588
607
  const message = error instanceof Error ? error.message : String(error);
608
+ const catchFailureReason = message.toLowerCase().includes("timed out") || message.toLowerCase().includes("timeout") ? "timeout" : "unknown";
589
609
  return {
590
610
  sessionId: session.relaySessionId,
591
611
  exitCode: 1,
592
612
  stdout: "",
593
- stderr: message
613
+ stderr: message,
614
+ failureReason: catchFailureReason
594
615
  };
595
616
  }
596
617
  }
@@ -620,7 +641,8 @@ var init_spawn_agent = __esm({
620
641
  timeoutMs: z2.number().optional().describe("Timeout in milliseconds for agent execution. Default: no timeout."),
621
642
  taskInstructionPath: z2.string().optional().describe(
622
643
  "Path to a file containing task instructions. Content is prepended to the prompt. Path is resolved relative to the project root and validated against path traversal."
623
- )
644
+ ),
645
+ label: z2.string().optional().describe("Human-readable label for identifying this agent in parallel results")
624
646
  });
625
647
  }
626
648
  });
@@ -723,13 +745,15 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
723
745
  const reason = `Max depth exceeded: ${envContext.depth} >= ${guard.getConfig().maxDepth}`;
724
746
  logger.warn(`Batch spawn blocked by RecursionGuard: ${reason}`);
725
747
  return {
726
- results: agents.map((_, index) => ({
748
+ results: agents.map((agent, index) => ({
727
749
  index,
728
750
  sessionId: "",
729
751
  exitCode: 1,
730
752
  stdout: "",
731
753
  stderr: `Batch spawn blocked: ${reason}`,
732
- error: reason
754
+ error: reason,
755
+ failureReason: "recursion_blocked",
756
+ ...agent.label ? { label: agent.label } : {}
733
757
  })),
734
758
  totalCount: agents.length,
735
759
  successCount: 0,
@@ -742,13 +766,15 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
742
766
  const reason = `Batch would exceed max calls per session: ${currentCount} + ${agents.length} > ${maxCalls}`;
743
767
  logger.warn(`Batch spawn blocked by RecursionGuard: ${reason}`);
744
768
  return {
745
- results: agents.map((_, index) => ({
769
+ results: agents.map((agent, index) => ({
746
770
  index,
747
771
  sessionId: "",
748
772
  exitCode: 1,
749
773
  stdout: "",
750
774
  stderr: `Batch spawn blocked: ${reason}`,
751
- error: reason
775
+ error: reason,
776
+ failureReason: "recursion_blocked",
777
+ ...agent.label ? { label: agent.label } : {}
752
778
  })),
753
779
  totalCount: agents.length,
754
780
  successCount: 0,
@@ -790,7 +816,9 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
790
816
  stdout: r.stdout,
791
817
  stderr: r.stderr,
792
818
  ...r.nativeSessionId ? { nativeSessionId: r.nativeSessionId } : {},
793
- ...r.metadata ? { metadata: r.metadata } : {}
819
+ ...r.metadata ? { metadata: r.metadata } : {},
820
+ ...r.failureReason ? { failureReason: r.failureReason } : {},
821
+ ...agents[index]?.label ? { label: agents[index].label } : {}
794
822
  };
795
823
  if (r.exitCode !== 0) {
796
824
  base.originalInput = agents[index];
@@ -805,7 +833,9 @@ async function executeSpawnAgentsParallel(agents, registry2, sessionManager2, gu
805
833
  stdout: "",
806
834
  stderr: errorMessage,
807
835
  error: errorMessage,
808
- originalInput: agents[index]
836
+ originalInput: agents[index],
837
+ failureReason: "unknown",
838
+ ...agents[index]?.label ? { label: agents[index].label } : {}
809
839
  };
810
840
  });
811
841
  const successCount = results.filter((r) => r.exitCode === 0).length;
@@ -979,6 +1009,70 @@ var init_backend_selector = __esm({
979
1009
  }
980
1010
  });
981
1011
 
1012
+ // src/mcp-server/response-formatter.ts
1013
+ function formatSpawnAgentResponse(result) {
1014
+ const isError = result.exitCode !== 0;
1015
+ let text;
1016
+ if (isError) {
1017
+ const reasonPart = result.failureReason ? `, reason: ${result.failureReason}` : "";
1018
+ text = `FAILED (exit ${result.exitCode}${reasonPart})`;
1019
+ if (result.stderr) {
1020
+ text += `
1021
+ ${result.stderr}`;
1022
+ }
1023
+ } else {
1024
+ text = `Session: ${result.sessionId}
1025
+
1026
+ ${result.stdout}`;
1027
+ }
1028
+ if (result.metadata) {
1029
+ text += `
1030
+
1031
+ <metadata>
1032
+ ${JSON.stringify(result.metadata, null, 2)}
1033
+ </metadata>`;
1034
+ }
1035
+ return { text, isError };
1036
+ }
1037
+ function formatParallelResponse(result) {
1038
+ const isError = result.failureCount === result.totalCount;
1039
+ const parts = [];
1040
+ if (result.hasConflicts && result.conflicts) {
1041
+ parts.push(`\u26A0 FILE CONFLICTS DETECTED: Multiple agents modified the same files.`);
1042
+ parts.push(`Conflicting files: ${result.conflicts.map((c) => c.path).join(", ")}
1043
+ `);
1044
+ }
1045
+ const durations = result.results.map((r) => r.metadata?.durationMs).filter((d) => d !== void 0);
1046
+ const avgDuration = durations.length > 0 ? (durations.reduce((a, b) => a + b, 0) / durations.length / 1e3).toFixed(1) : "?";
1047
+ parts.push(
1048
+ `${result.totalCount} agents: ${result.successCount} succeeded, ${result.failureCount} failed (avg ${avgDuration}s)
1049
+ `
1050
+ );
1051
+ for (const r of result.results) {
1052
+ const labelPart = r.label ? ` [${r.label}]` : "";
1053
+ const backend = r.metadata?.selectedBackend ?? "?";
1054
+ const duration = r.metadata?.durationMs !== void 0 ? `${(r.metadata.durationMs / 1e3).toFixed(1)}s` : "?s";
1055
+ if (r.exitCode === 0) {
1056
+ parts.push(`--- Agent ${r.index}${labelPart} (${backend}, ${duration}) SUCCESS ---`);
1057
+ parts.push(r.stdout || "(no output)");
1058
+ } else {
1059
+ const reasonPart = r.failureReason ? `, reason: ${r.failureReason}` : "";
1060
+ parts.push(`--- Agent ${r.index}${labelPart} FAILED (${backend}, ${duration}${reasonPart}) ---`);
1061
+ parts.push(r.stderr || r.error || "(no output)");
1062
+ }
1063
+ parts.push("");
1064
+ }
1065
+ parts.push(`<metadata>
1066
+ ${JSON.stringify(result, null, 2)}
1067
+ </metadata>`);
1068
+ return { text: parts.join("\n"), isError };
1069
+ }
1070
+ var init_response_formatter = __esm({
1071
+ "src/mcp-server/response-formatter.ts"() {
1072
+ "use strict";
1073
+ }
1074
+ });
1075
+
982
1076
  // src/mcp-server/server.ts
983
1077
  var server_exports = {};
984
1078
  __export(server_exports, {
@@ -1002,6 +1096,7 @@ var init_server = __esm({
1002
1096
  init_list_available_backends();
1003
1097
  init_backend_selector();
1004
1098
  init_logger();
1099
+ init_response_formatter();
1005
1100
  spawnAgentsParallelInputShape = {
1006
1101
  agents: z5.array(spawnAgentInputSchema).min(1).max(10).describe(
1007
1102
  "Array of agent configurations to execute in parallel (1-10 agents)"
@@ -1018,7 +1113,7 @@ var init_server = __esm({
1018
1113
  this.backendSelector = new BackendSelector();
1019
1114
  this.server = new McpServer({
1020
1115
  name: "agentic-relay",
1021
- version: "0.8.0"
1116
+ version: "0.9.1"
1022
1117
  });
1023
1118
  this.registerTools(this.server);
1024
1119
  }
@@ -1067,17 +1162,7 @@ var init_server = __esm({
1067
1162
  this._childHttpUrl,
1068
1163
  onProgress
1069
1164
  );
1070
- const isError = result.exitCode !== 0;
1071
- let text = isError ? `Error (exit ${result.exitCode}): ${result.stderr || result.stdout}` : `Session: ${result.sessionId}
1072
-
1073
- ${result.stdout}`;
1074
- if (result.metadata) {
1075
- text += `
1076
-
1077
- <metadata>
1078
- ${JSON.stringify(result.metadata, null, 2)}
1079
- </metadata>`;
1080
- }
1165
+ const { text, isError } = formatSpawnAgentResponse(result);
1081
1166
  return {
1082
1167
  content: [{ type: "text", text }],
1083
1168
  isError
@@ -1122,15 +1207,7 @@ ${JSON.stringify(result.metadata, null, 2)}
1122
1207
  this._childHttpUrl,
1123
1208
  onProgress
1124
1209
  );
1125
- const isError = result.failureCount === result.totalCount;
1126
- let text = "";
1127
- if (result.hasConflicts) {
1128
- text += "\u26A0 FILE CONFLICTS DETECTED: Multiple agents modified the same files.\n";
1129
- text += `Conflicting files: ${result.conflicts.map((c) => c.path).join(", ")}
1130
-
1131
- `;
1132
- }
1133
- text += JSON.stringify(result, null, 2);
1210
+ const { text, isError } = formatParallelResponse(result);
1134
1211
  return {
1135
1212
  content: [{ type: "text", text }],
1136
1213
  isError
@@ -1151,11 +1228,24 @@ ${JSON.stringify(result.metadata, null, 2)}
1151
1228
  failedResults: z5.array(z5.object({
1152
1229
  index: z5.number(),
1153
1230
  originalInput: spawnAgentInputSchema
1154
- })).min(1).describe("Array of failed results with their original input configurations")
1231
+ })).min(1).describe("Array of failed results with their original input configurations"),
1232
+ overrides: z5.object({
1233
+ maxTurns: z5.number().optional(),
1234
+ timeoutMs: z5.number().optional(),
1235
+ preferredBackend: z5.enum(["claude", "codex", "gemini"]).optional()
1236
+ }).optional().describe("Parameter overrides applied to all retried agents")
1155
1237
  },
1156
1238
  async (params) => {
1157
1239
  try {
1158
- const agents = params.failedResults.map((r) => r.originalInput);
1240
+ const agents = params.failedResults.map((r) => {
1241
+ const input = { ...r.originalInput };
1242
+ if (params.overrides) {
1243
+ if (params.overrides.maxTurns !== void 0) input.maxTurns = params.overrides.maxTurns;
1244
+ if (params.overrides.timeoutMs !== void 0) input.timeoutMs = params.overrides.timeoutMs;
1245
+ if (params.overrides.preferredBackend !== void 0) input.preferredBackend = params.overrides.preferredBackend;
1246
+ }
1247
+ return input;
1248
+ });
1159
1249
  const result = await executeSpawnAgentsParallel(
1160
1250
  agents,
1161
1251
  this.registry,
@@ -1166,8 +1256,7 @@ ${JSON.stringify(result.metadata, null, 2)}
1166
1256
  this.backendSelector,
1167
1257
  this._childHttpUrl
1168
1258
  );
1169
- const isError = result.failureCount === result.totalCount;
1170
- const text = JSON.stringify(result, null, 2);
1259
+ const { text, isError } = formatParallelResponse(result);
1171
1260
  return {
1172
1261
  content: [{ type: "text", text }],
1173
1262
  isError
@@ -1269,6 +1358,7 @@ ${JSON.stringify(result.metadata, null, 2)}
1269
1358
  async start(options) {
1270
1359
  const transportType = options?.transport ?? "stdio";
1271
1360
  if (transportType === "stdio") {
1361
+ redirectToStderr();
1272
1362
  logger.info("Starting agentic-relay MCP server (stdio transport)...");
1273
1363
  const transport = new StdioServerTransport();
1274
1364
  await this.server.connect(transport);
@@ -1331,7 +1421,7 @@ ${JSON.stringify(result.metadata, null, 2)}
1331
1421
  });
1332
1422
  const server = new McpServer({
1333
1423
  name: "agentic-relay",
1334
- version: "0.8.0"
1424
+ version: "0.9.1"
1335
1425
  });
1336
1426
  this.registerTools(server);
1337
1427
  transport.onclose = () => {
@@ -1695,8 +1785,10 @@ var CLAUDE_NESTING_ENV_VARS = [
1695
1785
  "CLAUDE_CODE_SSE_PORT",
1696
1786
  "CLAUDE_CODE_ENTRYPOINT"
1697
1787
  ];
1698
- var DEFAULT_CLAUDE_SDK_TIMEOUT_MS = 5 * 60 * 1e3;
1699
- function getClaudeSdkTimeoutMs() {
1788
+ function resolveClaudeSdkTimeoutMs(flagsTimeoutMs) {
1789
+ if (flagsTimeoutMs !== void 0 && flagsTimeoutMs > 0) {
1790
+ return flagsTimeoutMs;
1791
+ }
1700
1792
  const envVal = process.env["RELAY_CLAUDE_TIMEOUT_MS"];
1701
1793
  if (envVal) {
1702
1794
  const parsed = Number(envVal);
@@ -1704,7 +1796,7 @@ function getClaudeSdkTimeoutMs() {
1704
1796
  return parsed;
1705
1797
  }
1706
1798
  }
1707
- return DEFAULT_CLAUDE_SDK_TIMEOUT_MS;
1799
+ return void 0;
1708
1800
  }
1709
1801
  var ClaudeAdapter = class extends BaseAdapter {
1710
1802
  id = "claude";
@@ -1743,9 +1835,9 @@ var ClaudeAdapter = class extends BaseAdapter {
1743
1835
  }
1744
1836
  const env = this.buildCleanEnv(flags);
1745
1837
  const permissionMode = this.getPermissionMode();
1746
- const timeoutMs = getClaudeSdkTimeoutMs();
1838
+ const timeoutMs = resolveClaudeSdkTimeoutMs(flags.timeoutMs);
1747
1839
  const abortController = new AbortController();
1748
- const timer = setTimeout(() => abortController.abort(), timeoutMs);
1840
+ const timer = timeoutMs !== void 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
1749
1841
  try {
1750
1842
  const { query } = await loadClaudeSDK();
1751
1843
  const options = {
@@ -1770,7 +1862,16 @@ var ClaudeAdapter = class extends BaseAdapter {
1770
1862
  let sessionId = "";
1771
1863
  let isError = false;
1772
1864
  let errorMessages = [];
1865
+ let totalInputTokens = 0;
1866
+ let totalOutputTokens = 0;
1773
1867
  for await (const message of q) {
1868
+ if (message.type === "assistant") {
1869
+ const betaMessage = message.message;
1870
+ if (betaMessage?.usage) {
1871
+ totalInputTokens += betaMessage.usage.input_tokens ?? 0;
1872
+ totalOutputTokens += betaMessage.usage.output_tokens ?? 0;
1873
+ }
1874
+ }
1774
1875
  if (message.type === "result") {
1775
1876
  sessionId = message.session_id;
1776
1877
  if (message.subtype === "success") {
@@ -1782,11 +1883,13 @@ var ClaudeAdapter = class extends BaseAdapter {
1782
1883
  }
1783
1884
  }
1784
1885
  logger.debug(`Claude SDK session: ${sessionId}`);
1886
+ const hasTokenUsage = totalInputTokens > 0 || totalOutputTokens > 0;
1785
1887
  return {
1786
1888
  exitCode: isError ? 1 : 0,
1787
1889
  stdout: resultText,
1788
1890
  stderr: errorMessages.join("\n"),
1789
- ...sessionId ? { nativeSessionId: sessionId } : {}
1891
+ ...sessionId ? { nativeSessionId: sessionId } : {},
1892
+ ...hasTokenUsage ? { tokenUsage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } } : {}
1790
1893
  };
1791
1894
  } catch (error) {
1792
1895
  if (abortController.signal.aborted) {
@@ -1802,7 +1905,7 @@ var ClaudeAdapter = class extends BaseAdapter {
1802
1905
  stderr: error instanceof Error ? error.message : String(error)
1803
1906
  };
1804
1907
  } finally {
1805
- clearTimeout(timer);
1908
+ if (timer !== void 0) clearTimeout(timer);
1806
1909
  }
1807
1910
  }
1808
1911
  async *executeStreaming(flags) {
@@ -1811,9 +1914,9 @@ var ClaudeAdapter = class extends BaseAdapter {
1811
1914
  }
1812
1915
  const env = this.buildCleanEnv(flags);
1813
1916
  const permissionMode = this.getPermissionMode();
1814
- const timeoutMs = getClaudeSdkTimeoutMs();
1917
+ const timeoutMs = resolveClaudeSdkTimeoutMs(flags.timeoutMs);
1815
1918
  const abortController = new AbortController();
1816
- const timer = setTimeout(() => abortController.abort(), timeoutMs);
1919
+ const timer = timeoutMs !== void 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
1817
1920
  try {
1818
1921
  const { query } = await loadClaudeSDK();
1819
1922
  const options = {
@@ -1888,13 +1991,13 @@ var ClaudeAdapter = class extends BaseAdapter {
1888
1991
  result: { exitCode: 1, stdout: "", stderr: errorMessage }
1889
1992
  };
1890
1993
  } finally {
1891
- clearTimeout(timer);
1994
+ if (timer !== void 0) clearTimeout(timer);
1892
1995
  }
1893
1996
  }
1894
1997
  async continueSession(nativeSessionId, prompt) {
1895
- const timeoutMs = getClaudeSdkTimeoutMs();
1998
+ const timeoutMs = resolveClaudeSdkTimeoutMs();
1896
1999
  const abortController = new AbortController();
1897
- const timer = setTimeout(() => abortController.abort(), timeoutMs);
2000
+ const timer = timeoutMs !== void 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
1898
2001
  try {
1899
2002
  const { query } = await loadClaudeSDK();
1900
2003
  const permissionMode = this.getPermissionMode();
@@ -1940,7 +2043,7 @@ var ClaudeAdapter = class extends BaseAdapter {
1940
2043
  stderr: error instanceof Error ? error.message : String(error)
1941
2044
  };
1942
2045
  } finally {
1943
- clearTimeout(timer);
2046
+ if (timer !== void 0) clearTimeout(timer);
1944
2047
  }
1945
2048
  }
1946
2049
  async resumeSession(sessionId, flags) {
@@ -4266,7 +4369,7 @@ function createVersionCommand(registry2) {
4266
4369
  description: "Show relay and backend versions"
4267
4370
  },
4268
4371
  async run() {
4269
- const relayVersion = "0.8.0";
4372
+ const relayVersion = "0.9.1";
4270
4373
  console.log(`agentic-relay v${relayVersion}`);
4271
4374
  console.log("");
4272
4375
  console.log("Backends:");
@@ -4616,7 +4719,7 @@ void configManager.getConfig().then((config) => {
4616
4719
  var main = defineCommand10({
4617
4720
  meta: {
4618
4721
  name: "relay",
4619
- version: "0.8.0",
4722
+ version: "0.9.1",
4620
4723
  description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
4621
4724
  },
4622
4725
  subCommands: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rk0429/agentic-relay",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI with MCP-based multi-layer sub-agent orchestration",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",