@rk0429/agentic-relay 0.12.3 → 0.14.0

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 +54 -36
  2. package/package.json +1 -1
package/dist/relay.mjs CHANGED
@@ -438,7 +438,11 @@ function buildChildMcpServers(parentMcpServers, childHttpUrl) {
438
438
  }
439
439
  return result;
440
440
  }
441
- function inferFailureReason(stderr, stdout) {
441
+ function inferFailureReason(stderr, stdout, sdkErrorMetadata) {
442
+ if (sdkErrorMetadata) {
443
+ if (sdkErrorMetadata.subtype === "error_max_turns") return "max_turns_exhausted";
444
+ return "adapter_error";
445
+ }
442
446
  const combined = `${stderr} ${stdout}`.toLowerCase();
443
447
  if (combined.includes("timed out") || combined.includes("timeout")) return "timeout";
444
448
  if (combined.includes("max turns") || combined.includes("max_turns") || combined.includes("turn limit")) return "max_turns_exhausted";
@@ -452,8 +456,8 @@ function buildContextFromEnv() {
452
456
  }
453
457
  async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector, childHttpUrl, onProgress) {
454
458
  onProgress?.({ stage: "initializing", percent: 0 });
455
- let effectiveBackend = input.backend;
456
- let selectionReason = "direct";
459
+ let effectiveBackend;
460
+ let selectionReason;
457
461
  if (backendSelector) {
458
462
  const availableBackends = registry2.listIds();
459
463
  const selectionContext = {
@@ -465,6 +469,12 @@ async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooks
465
469
  const selectionResult = backendSelector.selectBackendWithReason(selectionContext);
466
470
  effectiveBackend = selectionResult.backend;
467
471
  selectionReason = selectionResult.reason;
472
+ } else if (input.fallbackBackend) {
473
+ effectiveBackend = input.fallbackBackend;
474
+ selectionReason = "fallbackBackend";
475
+ } else {
476
+ effectiveBackend = "claude";
477
+ selectionReason = "default(no-selector)";
468
478
  }
469
479
  const envContext = buildContextFromEnv();
470
480
  const promptHash = RecursionGuard.hashPrompt(input.prompt);
@@ -667,22 +677,11 @@ ${input.prompt}`;
667
677
  maxDepth: guard.getConfig().maxDepth,
668
678
  traceId: envContext.traceId
669
679
  },
670
- ...mcpServers ? { mcpServers } : {},
671
- ...input.timeoutMs ? { timeoutMs: input.timeoutMs } : {}
680
+ ...mcpServers ? { mcpServers } : {}
672
681
  });
673
682
  }
674
683
  })();
675
- if (input.timeoutMs) {
676
- const timeoutPromise = new Promise(
677
- (_, reject) => setTimeout(
678
- () => reject(new Error(`Agent execution timed out after ${input.timeoutMs}ms`)),
679
- input.timeoutMs
680
- )
681
- );
682
- result = await Promise.race([executePromise, timeoutPromise]);
683
- } else {
684
- result = await executePromise;
685
- }
684
+ result = await executePromise;
686
685
  if (result && "_noSession" in result) {
687
686
  return {
688
687
  sessionId: session.relaySessionId,
@@ -705,7 +704,7 @@ ${input.prompt}`;
705
704
  }
706
705
  guard.recordSpawn(context);
707
706
  const status = result.exitCode === 0 ? "completed" : "error";
708
- const failureReason = result.exitCode !== 0 ? inferFailureReason(result.stderr, result.stdout) : void 0;
707
+ const failureReason = result.exitCode !== 0 ? inferFailureReason(result.stderr, result.stdout, result.sdkErrorMetadata) : void 0;
709
708
  await sessionManager2.update(session.relaySessionId, {
710
709
  status,
711
710
  ...result.nativeSessionId ? { nativeSessionId: result.nativeSessionId } : {}
@@ -781,8 +780,8 @@ var init_spawn_agent = __esm({
781
780
  init_recursion_guard();
782
781
  init_logger();
783
782
  spawnAgentInputSchema = z2.object({
784
- backend: z2.enum(["claude", "codex", "gemini"]).describe(
785
- "Required fallback backend. Overridden by automatic selection when BackendSelector is active (priority: preferredBackend > agentType mapping > taskType mapping > default)."
783
+ fallbackBackend: z2.enum(["claude", "codex", "gemini"]).optional().describe(
784
+ "Optional fallback backend. Used only when BackendSelector is not active or cannot determine a backend. When BackendSelector is active, backend is auto-selected by priority: preferredBackend > agentType mapping > taskType mapping > default (claude)."
786
785
  ),
787
786
  prompt: z2.string().describe(
788
787
  "The task instruction for the sub-agent. Be specific and include all necessary context for autonomous execution."
@@ -815,7 +814,6 @@ var init_spawn_agent = __esm({
815
814
  taskType: z2.enum(["code-writing", "code-review", "document-writing", "document-review", "research", "mixed"]).optional().describe(
816
815
  "Task type hint for automatic backend selection (priority 3, after preferredBackend and agentType). Mapping: code-writing\u2192codex, code-review\u2192claude, document-writing\u2192claude, document-review\u2192codex, research\u2192codex, mixed\u2192claude."
817
816
  ),
818
- timeoutMs: z2.number().optional().describe("Timeout in milliseconds for agent execution. Default: no timeout."),
819
817
  taskInstructionPath: z2.string().optional().describe(
820
818
  "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."
821
819
  ),
@@ -1324,7 +1322,7 @@ var init_server = __esm({
1324
1322
  this.guard = new RecursionGuard(guardConfig);
1325
1323
  this.backendSelector = new BackendSelector();
1326
1324
  this.server = new McpServer(
1327
- { name: "agentic-relay", version: "0.12.3" },
1325
+ { name: "agentic-relay", version: "0.14.0" },
1328
1326
  createMcpServerOptions()
1329
1327
  );
1330
1328
  this.registerTools(this.server);
@@ -1499,7 +1497,6 @@ var init_server = __esm({
1499
1497
  })).min(1).describe("Array of failed results with their original input configurations"),
1500
1498
  overrides: z5.object({
1501
1499
  maxTurns: z5.number().optional(),
1502
- timeoutMs: z5.number().optional(),
1503
1500
  preferredBackend: z5.enum(["claude", "codex", "gemini"]).optional()
1504
1501
  }).optional().describe("Parameter overrides applied to all retried agents")
1505
1502
  },
@@ -1509,7 +1506,6 @@ var init_server = __esm({
1509
1506
  const input = { ...r.originalInput };
1510
1507
  if (params.overrides) {
1511
1508
  if (params.overrides.maxTurns !== void 0) input.maxTurns = params.overrides.maxTurns;
1512
- if (params.overrides.timeoutMs !== void 0) input.timeoutMs = params.overrides.timeoutMs;
1513
1509
  if (params.overrides.preferredBackend !== void 0) input.preferredBackend = params.overrides.preferredBackend;
1514
1510
  }
1515
1511
  return input;
@@ -1688,7 +1684,7 @@ var init_server = __esm({
1688
1684
  sessionIdGenerator: () => randomUUID()
1689
1685
  });
1690
1686
  const server = new McpServer(
1691
- { name: "agentic-relay", version: "0.12.3" },
1687
+ { name: "agentic-relay", version: "0.14.0" },
1692
1688
  createMcpServerOptions()
1693
1689
  );
1694
1690
  this.registerTools(server);
@@ -2053,10 +2049,7 @@ var CLAUDE_NESTING_ENV_VARS = [
2053
2049
  "CLAUDE_CODE_SSE_PORT",
2054
2050
  "CLAUDE_CODE_ENTRYPOINT"
2055
2051
  ];
2056
- function resolveClaudeSdkTimeoutMs(flagsTimeoutMs) {
2057
- if (flagsTimeoutMs !== void 0 && flagsTimeoutMs > 0) {
2058
- return flagsTimeoutMs;
2059
- }
2052
+ function resolveClaudeSdkTimeoutMs() {
2060
2053
  const envVal = process.env["RELAY_CLAUDE_TIMEOUT_MS"];
2061
2054
  if (envVal) {
2062
2055
  const parsed = Number(envVal);
@@ -2103,7 +2096,7 @@ var ClaudeAdapter = class extends BaseAdapter {
2103
2096
  }
2104
2097
  const env = this.buildCleanEnv(flags);
2105
2098
  const permissionMode = this.getPermissionMode();
2106
- const timeoutMs = resolveClaudeSdkTimeoutMs(flags.timeoutMs);
2099
+ const timeoutMs = resolveClaudeSdkTimeoutMs();
2107
2100
  const abortController = new AbortController();
2108
2101
  const timer = timeoutMs !== void 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
2109
2102
  try {
@@ -2132,6 +2125,7 @@ var ClaudeAdapter = class extends BaseAdapter {
2132
2125
  let errorMessages = [];
2133
2126
  let totalInputTokens = 0;
2134
2127
  let totalOutputTokens = 0;
2128
+ let sdkError = null;
2135
2129
  for await (const message of q) {
2136
2130
  if (message.type === "assistant") {
2137
2131
  const betaMessage = message.message;
@@ -2146,18 +2140,32 @@ var ClaudeAdapter = class extends BaseAdapter {
2146
2140
  resultText = message.result;
2147
2141
  } else {
2148
2142
  isError = true;
2149
- errorMessages = message.errors;
2143
+ sdkError = message;
2144
+ errorMessages = sdkError.errors;
2150
2145
  }
2151
2146
  }
2152
2147
  }
2153
2148
  logger.debug(`Claude SDK session: ${sessionId}`);
2149
+ if (sdkError) {
2150
+ logger.warn(
2151
+ `Claude SDK error: subtype=${sdkError.subtype} stop_reason=${sdkError.stop_reason} num_turns=${sdkError.num_turns} errors=[${sdkError.errors.join("; ")}]`
2152
+ );
2153
+ }
2154
2154
  const hasTokenUsage = totalInputTokens > 0 || totalOutputTokens > 0;
2155
2155
  return {
2156
2156
  exitCode: isError ? 1 : 0,
2157
2157
  stdout: resultText,
2158
2158
  stderr: errorMessages.join("\n"),
2159
2159
  ...sessionId ? { nativeSessionId: sessionId } : {},
2160
- ...hasTokenUsage ? { tokenUsage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } } : {}
2160
+ ...hasTokenUsage ? { tokenUsage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } } : {},
2161
+ ...sdkError ? {
2162
+ sdkErrorMetadata: {
2163
+ subtype: sdkError.subtype,
2164
+ numTurns: sdkError.num_turns,
2165
+ stopReason: sdkError.stop_reason,
2166
+ totalCostUsd: sdkError.total_cost_usd
2167
+ }
2168
+ } : {}
2161
2169
  };
2162
2170
  } catch (error) {
2163
2171
  if (abortController.signal.aborted) {
@@ -2182,7 +2190,7 @@ var ClaudeAdapter = class extends BaseAdapter {
2182
2190
  }
2183
2191
  const env = this.buildCleanEnv(flags);
2184
2192
  const permissionMode = this.getPermissionMode();
2185
- const timeoutMs = resolveClaudeSdkTimeoutMs(flags.timeoutMs);
2193
+ const timeoutMs = resolveClaudeSdkTimeoutMs();
2186
2194
  const abortController = new AbortController();
2187
2195
  const timer = timeoutMs !== void 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
2188
2196
  try {
@@ -2238,13 +2246,23 @@ var ClaudeAdapter = class extends BaseAdapter {
2238
2246
  nativeSessionId
2239
2247
  };
2240
2248
  } else {
2241
- const errors = message.errors;
2249
+ const sdkError = message;
2250
+ const errors = sdkError.errors;
2251
+ logger.warn(
2252
+ `Claude SDK error: subtype=${sdkError.subtype} stop_reason=${sdkError.stop_reason} num_turns=${sdkError.num_turns} errors=[${sdkError.errors.join("; ")}]`
2253
+ );
2242
2254
  yield {
2243
2255
  type: "done",
2244
2256
  result: {
2245
2257
  exitCode: 1,
2246
2258
  stdout: "",
2247
- stderr: errors.join("\n")
2259
+ stderr: errors.join("\n"),
2260
+ sdkErrorMetadata: {
2261
+ subtype: sdkError.subtype,
2262
+ numTurns: sdkError.num_turns,
2263
+ stopReason: sdkError.stop_reason,
2264
+ totalCostUsd: sdkError.total_cost_usd
2265
+ }
2248
2266
  },
2249
2267
  nativeSessionId
2250
2268
  };
@@ -4640,7 +4658,7 @@ function createVersionCommand(registry2) {
4640
4658
  description: "Show relay and backend versions"
4641
4659
  },
4642
4660
  async run() {
4643
- const relayVersion = "0.12.3";
4661
+ const relayVersion = "0.14.0";
4644
4662
  console.log(`agentic-relay v${relayVersion}`);
4645
4663
  console.log("");
4646
4664
  console.log("Backends:");
@@ -4990,7 +5008,7 @@ void configManager.getConfig().then((config) => {
4990
5008
  var main = defineCommand10({
4991
5009
  meta: {
4992
5010
  name: "relay",
4993
- version: "0.12.3",
5011
+ version: "0.14.0",
4994
5012
  description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
4995
5013
  },
4996
5014
  subCommands: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rk0429/agentic-relay",
3
- "version": "0.12.3",
3
+ "version": "0.14.0",
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",